<commit_before>#!/usr/bin/env pwsh

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

#Requires -Version 6.0
#Requires -PSEdition Core
#Requires -Modules @{ModuleName='Az.Accounts'; ModuleVersion='1.6.4'}
#Requires -Modules @{ModuleName='Az.Resources'; ModuleVersion='1.8.0'}

[CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param (
    # Limit $BaseName to enough characters to be under limit plus prefixes, and https://docs.microsoft.com/azure/architecture/best-practices/resource-naming.
    [Parameter()]
    [ValidatePattern('^[-a-zA-Z0-9\.\(\)_]{0,80}(?<=[a-zA-Z0-9\(\)])$')]
    [string] $BaseName,

    [ValidatePattern('^[-\w\._\(\)]+$')]
    [string] $ResourceGroupName,

    [Parameter(Mandatory = $true, Position = 0)]
    [string] $ServiceDirectory,

    [Parameter()]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $TestApplicationId,

    [Parameter()]
    [string] $TestApplicationSecret,

    [Parameter()]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $TestApplicationOid,

    [Parameter(ParameterSetName = 'Provisioner', Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $TenantId,

    [Parameter(ParameterSetName = 'Provisioner')]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $SubscriptionId,

    [Parameter(ParameterSetName = 'Provisioner', Mandatory = $true)]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $ProvisionerApplicationId,

    [Parameter(ParameterSetName = 'Provisioner', Mandatory = $true)]
    [string] $ProvisionerApplicationSecret,

    [Parameter()]
    [ValidateRange(0, [int]::MaxValue)]
    [int] $DeleteAfterHours,

    [Parameter()]
    [string] $Location = '',

    [Parameter()]
    [ValidateSet('AzureCloud', 'AzureUSGovernment', 'AzureChinaCloud', 'Dogfood')]
    [string] $Environment = 'AzureCloud',

    [Parameter()]
    [hashtable] $AdditionalParameters,

    [Parameter()]
    [switch] $CI = ($null -ne $env:SYSTEM_TEAMPROJECTID),

    [Parameter()]
    [switch] $Force,

    [Parameter()]
    [switch] $OutFile
)

# By default stop for any error.
if (!$PSBoundParameters.ContainsKey('ErrorAction')) {
    $ErrorActionPreference = 'Stop'
}

function Log($Message) {
    Write-Host ('{0} - {1}' -f [DateTime]::Now.ToLongTimeString(), $Message)
}

function Retry([scriptblock] $Action, [int] $Attempts = 5) {
    $attempt = 0
    $sleep = 5

    while ($attempt -lt $Attempts) {
        try {
            $attempt++
            return $Action.Invoke()
        } catch {
            if ($attempt -lt $Attempts) {
                $sleep *= 2

                Write-Warning "Attempt $attempt failed: $_. Trying again in $sleep seconds..."
                Start-Sleep -Seconds $sleep
            } else {
                Write-Error -ErrorRecord $_
            }
        }
    }
}

# Support actions to invoke on exit.
$exitActions = @({
    if ($exitActions.Count -gt 1) {
        Write-Verbose 'Running registered exit actions'
    }
})

trap {
    # Like using try..finally in PowerShell, but without keeping track of more braces or tabbing content.
    $exitActions.Invoke()
}

# Enumerate test resources to deploy. Fail if none found.
$repositoryRoot = "$PSScriptRoot/../../.." | Resolve-Path
$root = [System.IO.Path]::Combine($repositoryRoot, "sdk", $ServiceDirectory) | Resolve-Path
$templateFileName = 'test-resources.json'
$templateFiles = @()
$environmentVariables = @{}
# Azure SDK Developer Playground
$defaultSubscription = "faa080af-c1d8-40ad-9cce-e1a450ca5b57"

Write-Verbose "Checking for '$templateFileName' files under '$root'"
Get-ChildItem -Path $root -Filter $templateFileName -Recurse | ForEach-Object {
    $templateFile = $_.FullName

    Write-Verbose "Found template '$templateFile'"
    $templateFiles += $templateFile
}

if (!$templateFiles) {
    Write-Warning -Message "No template files found under '$root'"
    exit
}

$UserName =  if ($env:USER) { $env:USER } else { "${env:USERNAME}" }

# If no base name is specified use current user name
if (!$BaseName) {
    $BaseName = "$UserName$ServiceDirectory"

    Log "BaseName was not set. Using default base name: '$BaseName'"
}

# Try detecting repos that support OutFile and defaulting to it
if (!$CI -and !$PSBoundParameters.ContainsKey('OutFile') -and $IsWindows) {
    # TODO: find a better way to detect the language
    if (Test-Path "$repositoryRoot/eng/service.proj") {
        $OutFile = $true
        Log "Detected .NET repository. Defaulting OutFile to true. Test environment settings would be stored into the file so you don't need to set environment variables manually."
    }
}

# If no location is specified use safe default locations for the given
# environment. If no matching environment is found $Location remains an empty
# string.
if (!$Location) {
    $Location = @{
        'AzureCloud' = 'westus2';
        'AzureUSGovernment' = 'usgovvirginia';
        'AzureChinaCloud' = 'chinaeast2';
        'Dogfood' = 'westus'
    }[$Environment]

    Write-Verbose "Location was not set. Using default location for environment: '$Location'"
}

# If no test application ID is specified during an interactive session, create a new service principal.
if (!$CI -and !$TestApplicationId) {

    # Make sure the user is logged in to create a service principal.
    $context = Get-AzContext;
    if (!$context) {
        Log "You are not logged in; connecting to 'Azure SDK Developer Playground'"
        $context = (Connect-AzAccount -Subscription $defaultSubscription).Context
    }

    Log "TestApplicationId was not specified; creating a new service principal"
    $servicePrincipal = New-AzADServicePrincipal -Role Owner

    $TestApplicationId = $servicePrincipal.ApplicationId
    $TestApplicationSecret = (ConvertFrom-SecureString $servicePrincipal.Secret -AsPlainText);

    Log "Created service principal '$TestApplicationId'"

    if (!$ProvisionerApplicationId) {
        $ProvisionerApplicationId = $TestApplicationId
        $ProvisionerApplicationSecret = $TestApplicationSecret
        $TenantId = $context.Tenant.Id
    }
}

# Log in as and run pre- and post-scripts as the provisioner service principal.
if ($ProvisionerApplicationId) {
    $null = Disable-AzContextAutosave -Scope Process

    Log "Logging into service principal '$ProvisionerApplicationId'"
    $provisionerSecret = ConvertTo-SecureString -String $ProvisionerApplicationSecret -AsPlainText -Force
    $provisionerCredential = [System.Management.Automation.PSCredential]::new($ProvisionerApplicationId, $provisionerSecret)

    # Use the given subscription ID if provided.
    $subscriptionArgs = if ($SubscriptionId) {
        @{SubscriptionId = $SubscriptionId}
    } else {
        @{}
    }

    $provisionerAccount = Retry {
        Connect-AzAccount -Force:$Force -Tenant $TenantId -Credential $provisionerCredential -ServicePrincipal -Environment $Environment @subscriptionArgs
    }

    $exitActions += {
        Write-Verbose "Logging out of service principal '$($provisionerAccount.Context.Account)'"

        # Only attempt to disconnect if the -WhatIf flag was not set. Otherwise, this call is not necessary and will fail.<commit_msg>Make sure we always log in as app or provisioner (#16669)<commit_after>#!/usr/bin/env pwsh

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

#Requires -Version 6.0
#Requires -PSEdition Core
#Requires -Modules @{ModuleName='Az.Accounts'; ModuleVersion='1.6.4'}
#Requires -Modules @{ModuleName='Az.Resources'; ModuleVersion='1.8.0'}

[CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param (
    # Limit $BaseName to enough characters to be under limit plus prefixes, and https://docs.microsoft.com/azure/architecture/best-practices/resource-naming.
    [Parameter()]
    [ValidatePattern('^[-a-zA-Z0-9\.\(\)_]{0,80}(?<=[a-zA-Z0-9\(\)])$')]
    [string] $BaseName,

    [ValidatePattern('^[-\w\._\(\)]+$')]
    [string] $ResourceGroupName,

    [Parameter(Mandatory = $true, Position = 0)]
    [string] $ServiceDirectory,

    [Parameter()]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $TestApplicationId,

    [Parameter()]
    [string] $TestApplicationSecret,

    [Parameter()]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $TestApplicationOid,

    [Parameter(ParameterSetName = 'Provisioner', Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $TenantId,

    [Parameter(ParameterSetName = 'Provisioner')]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $SubscriptionId,

    [Parameter(ParameterSetName = 'Provisioner', Mandatory = $true)]
    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    [string] $ProvisionerApplicationId,

    [Parameter(ParameterSetName = 'Provisioner', Mandatory = $true)]
    [string] $ProvisionerApplicationSecret,

    [Parameter()]
    [ValidateRange(0, [int]::MaxValue)]
    [int] $DeleteAfterHours,

    [Parameter()]
    [string] $Location = '',

    [Parameter()]
    [ValidateSet('AzureCloud', 'AzureUSGovernment', 'AzureChinaCloud', 'Dogfood')]
    [string] $Environment = 'AzureCloud',

    [Parameter()]
    [hashtable] $AdditionalParameters,

    [Parameter()]
    [switch] $CI = ($null -ne $env:SYSTEM_TEAMPROJECTID),

    [Parameter()]
    [switch] $Force,

    [Parameter()]
    [switch] $OutFile
)

# By default stop for any error.
if (!$PSBoundParameters.ContainsKey('ErrorAction')) {
    $ErrorActionPreference = 'Stop'
}

function Log($Message) {
    Write-Host ('{0} - {1}' -f [DateTime]::Now.ToLongTimeString(), $Message)
}

function Retry([scriptblock] $Action, [int] $Attempts = 5) {
    $attempt = 0
    $sleep = 5

    while ($attempt -lt $Attempts) {
        try {
            $attempt++
            return $Action.Invoke()
        } catch {
            if ($attempt -lt $Attempts) {
                $sleep *= 2

                Write-Warning "Attempt $attempt failed: $_. Trying again in $sleep seconds..."
                Start-Sleep -Seconds $sleep
            } else {
                Write-Error -ErrorRecord $_
            }
        }
    }
}

# Support actions to invoke on exit.
$exitActions = @({
    if ($exitActions.Count -gt 1) {
        Write-Verbose 'Running registered exit actions'
    }
})

trap {
    # Like using try..finally in PowerShell, but without keeping track of more braces or tabbing content.
    $exitActions.Invoke()
}

# Enumerate test resources to deploy. Fail if none found.
$repositoryRoot = "$PSScriptRoot/../../.." | Resolve-Path
$root = [System.IO.Path]::Combine($repositoryRoot, "sdk", $ServiceDirectory) | Resolve-Path
$templateFileName = 'test-resources.json'
$templateFiles = @()
$environmentVariables = @{}
# Azure SDK Developer Playground
$defaultSubscription = "faa080af-c1d8-40ad-9cce-e1a450ca5b57"

Write-Verbose "Checking for '$templateFileName' files under '$root'"
Get-ChildItem -Path $root -Filter $templateFileName -Recurse | ForEach-Object {
    $templateFile = $_.FullName

    Write-Verbose "Found template '$templateFile'"
    $templateFiles += $templateFile
}

if (!$templateFiles) {
    Write-Warning -Message "No template files found under '$root'"
    exit
}

$UserName =  if ($env:USER) { $env:USER } else { "${env:USERNAME}" }

# If no base name is specified use current user name
if (!$BaseName) {
    $BaseName = "$UserName$ServiceDirectory"

    # Make sure pre- and post-scripts are passed formerly required arguments.
    $PSBoundParameters['BaseName'] = $BaseName

    Log "BaseName was not set. Using default base name: '$BaseName'"
}

# Try detecting repos that support OutFile and defaulting to it
if (!$CI -and !$PSBoundParameters.ContainsKey('OutFile') -and $IsWindows) {
    # TODO: find a better way to detect the language
    if (Test-Path "$repositoryRoot/eng/service.proj") {
        $OutFile = $true
        Log "Detected .NET repository. Defaulting OutFile to true. Test environment settings would be stored into the file so you don't need to set environment variables manually."
    }
}

# If no location is specified use safe default locations for the given
# environment. If no matching environment is found $Location remains an empty
# string.
if (!$Location) {
    $Location = @{
        'AzureCloud' = 'westus2';
        'AzureUSGovernment' = 'usgovvirginia';
        'AzureChinaCloud' = 'chinaeast2';
        'Dogfood' = 'westus'
    }[$Environment]

    Write-Verbose "Location was not set. Using default location for environment: '$Location'"
}

if (!$CI) {

    # Make sure the user is logged in to create a service principal.
    $context = Get-AzContext;
    if (!$context) {
        Log "You are not logged in; connecting to 'Azure SDK Developer Playground'"
        $context = (Connect-AzAccount -Subscription $defaultSubscription).Context
    }

    # If no test application ID is specified during an interactive session, create a new service principal.
    if (!$TestApplicationId) {
        Log "TestApplicationId was not specified; creating a new service principal"
        $servicePrincipal = New-AzADServicePrincipal -Role Owner

        $TestApplicationId = $servicePrincipal.ApplicationId
        $TestApplicationSecret = (ConvertFrom-SecureString $servicePrincipal.Secret -AsPlainText);

        # Make sure pre- and post-scripts are passed formerly required arguments.
        $PSBoundParameters['TestApplicationId'] = $TestApplicationId
        $PSBoundParameters['TestApplicationSecret'] = $TestApplicationSecret

        Log "Created service principal '$TestApplicationId'"
    }

    if (!$ProvisionerApplicationId) {
        $ProvisionerApplicationId = $TestApplicationId
        $ProvisionerApplicationSecret = $TestApplicationSecret
        $TenantId = $context.Tenant.Id
    }
}

# Log in as and run pre- and post-scripts as the provisioner service principal.
if ($ProvisionerApplicationId) {
    $null = Disable-AzContextAutosave -Scope Process

    Log "Logging into service principal '$ProvisionerApplicationId'"
    $provisionerSecret = ConvertTo-SecureString -String $ProvisionerApplicationSecret -AsPlainText -Force
    $provisionerCredential = [System.Management.Automation.PSCredential]::new($ProvisionerApplicationId, $provisionerSecret)

    # Use the given subscription ID if provided.
    $subscriptionArgs = if ($SubscriptionId) {
        @{SubscriptionId = $SubscriptionId}
    } else {
        @{}
    }

    $provisionerAccount = Retry {
        Connect-AzAccount -Force:$Force -Tenant $TenantId -Credential $provisionerCredential -ServicePrincipal -Environment $Environment @subscriptionArgs
    }

    $exitActions += {
        Write-Verbose "Logging out of service principal '$($provisionerAccount.Context.Account)'"

        # Only attempt to disconnect if the -WhatIf flag was not set. Otherwise, this call is not necessary and will fail.
{'fraction_non_alphanumeric': 0.1354642313546423, 'fraction_numerical': 0.011580967507114024, 'mean_word_length': 3.5669386521607738, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5003523', 'n_tokens_mistral': 4704, 'n_tokens_neox': 4551, 'n_words': 1203}