...

Text file src/github.com/Azure/azure-sdk-for-go/eng/common/TestResources/Remove-TestResources.ps1

Documentation: github.com/Azure/azure-sdk-for-go/eng/common/TestResources

     1#!/usr/bin/env pwsh
     2
     3# Copyright (c) Microsoft Corporation. All rights reserved.
     4# Licensed under the MIT License.
     5
     6#Requires -Version 6.0
     7#Requires -PSEdition Core
     8#Requires -Modules @{ModuleName='Az.Accounts'; ModuleVersion='1.6.4'}
     9#Requires -Modules @{ModuleName='Az.Resources'; ModuleVersion='1.8.0'}
    10
    11[CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    12param (
    13    # Limit $BaseName to enough characters to be under limit plus prefixes, and https://docs.microsoft.com/azure/architecture/best-practices/resource-naming.
    14    [Parameter(ParameterSetName = 'Default')]
    15    [Parameter(ParameterSetName = 'Default+Provisioner', Mandatory = $true, Position = 0)]
    16    [ValidatePattern('^[-a-zA-Z0-9\.\(\)_]{0,80}(?<=[a-zA-Z0-9\(\)])$')]
    17    [string] $BaseName,
    18
    19    [Parameter(ParameterSetName = 'ResourceGroup')]
    20    [Parameter(ParameterSetName = 'ResourceGroup+Provisioner')]
    21    [string] $ResourceGroupName,
    22
    23    [Parameter(ParameterSetName = 'Default+Provisioner', Mandatory = $true)]
    24    [Parameter(ParameterSetName = 'ResourceGroup+Provisioner', Mandatory = $true)]
    25    [ValidateNotNullOrEmpty()]
    26    [string] $TenantId,
    27
    28    [Parameter()]
    29    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    30    [string] $SubscriptionId,
    31
    32    [Parameter(ParameterSetName = 'Default+Provisioner', Mandatory = $true)]
    33    [Parameter(ParameterSetName = 'ResourceGroup+Provisioner', Mandatory = $true)]
    34    [ValidatePattern('^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$')]
    35    [string] $ProvisionerApplicationId,
    36
    37    [Parameter(ParameterSetName = 'Default+Provisioner', Mandatory = $true)]
    38    [Parameter(ParameterSetName = 'ResourceGroup+Provisioner', Mandatory = $true)]
    39    [string] $ProvisionerApplicationSecret,
    40
    41    [Parameter(ParameterSetName = 'Default', Position = 0)]
    42    [Parameter(ParameterSetName = 'Default+Provisioner')]
    43    [Parameter(ParameterSetName = 'ResourceGroup')]
    44    [Parameter(ParameterSetName = 'ResourceGroup+Provisioner')]
    45    [string] $ServiceDirectory,
    46
    47    [Parameter()]
    48    [ValidateSet('AzureCloud', 'AzureUSGovernment', 'AzureChinaCloud', 'Dogfood')]
    49    [string] $Environment = 'AzureCloud',
    50
    51    [Parameter(ParameterSetName = 'ResourceGroup')]
    52    [Parameter(ParameterSetName = 'ResourceGroup+Provisioner')]
    53    [switch] $CI,
    54
    55    [Parameter()]
    56    [switch] $Force,
    57
    58    # Captures any arguments not declared here (no parameter errors)
    59    [Parameter(ValueFromRemainingArguments = $true)]
    60    $RemoveTestResourcesRemainingArguments
    61)
    62
    63# By default stop for any error.
    64if (!$PSBoundParameters.ContainsKey('ErrorAction')) {
    65    $ErrorActionPreference = 'Stop'
    66}
    67
    68# Support actions to invoke on exit.
    69$exitActions = @({
    70    if ($exitActions.Count -gt 1) {
    71        Write-Verbose 'Running registered exit actions.'
    72    }
    73})
    74
    75trap {
    76    # Like using try..finally in PowerShell, but without keeping track of more braces or tabbing content.
    77    $exitActions.Invoke()
    78}
    79
    80. $PSScriptRoot/SubConfig-Helpers.ps1
    81# Source helpers to purge resources.
    82. "$PSScriptRoot\..\scripts\Helpers\Resource-Helpers.ps1"
    83
    84function Log($Message) {
    85    Write-Host ('{0} - {1}' -f [DateTime]::Now.ToLongTimeString(), $Message)
    86}
    87
    88function Retry([scriptblock] $Action, [int] $Attempts = 5) {
    89    $attempt = 0
    90    $sleep = 5
    91
    92    while ($attempt -lt $Attempts) {
    93        try {
    94            $attempt++
    95            return $Action.Invoke()
    96        } catch {
    97            if ($attempt -lt $Attempts) {
    98                $sleep *= 2
    99
   100                Write-Warning "Attempt $attempt failed: $_. Trying again in $sleep seconds..."
   101                Start-Sleep -Seconds $sleep
   102            } else {
   103                Write-Error -ErrorRecord $_
   104            }
   105        }
   106    }
   107}
   108
   109if ($ProvisionerApplicationId) {
   110    $null = Disable-AzContextAutosave -Scope Process
   111
   112    Log "Logging into service principal '$ProvisionerApplicationId'"
   113    $provisionerSecret = ConvertTo-SecureString -String $ProvisionerApplicationSecret -AsPlainText -Force
   114    $provisionerCredential = [System.Management.Automation.PSCredential]::new($ProvisionerApplicationId, $provisionerSecret)
   115
   116    # Use the given subscription ID if provided.
   117    $subscriptionArgs = if ($SubscriptionId) {
   118        @{SubscriptionId = $SubscriptionId}
   119    }
   120
   121    $provisionerAccount = Retry {
   122        Connect-AzAccount -Force:$Force -Tenant $TenantId -Credential $provisionerCredential -ServicePrincipal -Environment $Environment @subscriptionArgs
   123    }
   124
   125    $exitActions += {
   126        Write-Verbose "Logging out of service principal '$($provisionerAccount.Context.Account)'"
   127        $null = Disconnect-AzAccount -AzureContext $provisionerAccount.Context
   128    }
   129}
   130
   131$context = Get-AzContext
   132
   133if (!$ResourceGroupName) {
   134    if ($CI) {
   135        if (!$ServiceDirectory) {
   136            Write-Warning "ServiceDirectory parameter is empty, nothing to remove"
   137            exit 0
   138        }
   139        $envVarName = (BuildServiceDirectoryPrefix (GetServiceLeafDirectoryName $ServiceDirectory)) + "RESOURCE_GROUP"
   140        $ResourceGroupName = [Environment]::GetEnvironmentVariable($envVarName)
   141        if (!$ResourceGroupName) {
   142            Write-Error "Could not find resource group name environment variable '$envVarName'. This is likely due to an earlier failure in the 'Deploy Test Resources' step above."
   143            exit 0
   144        }
   145    } else {
   146        if (!$BaseName) {
   147            $UserName = GetUserName
   148            $BaseName = GetBaseName $UserName $ServiceDirectory
   149            Log "BaseName was not set. Using default base name '$BaseName'"
   150        }
   151
   152        # Format the resource group name like in New-TestResources.ps1.
   153        $ResourceGroupName = "rg-$BaseName"
   154    }
   155}
   156
   157# If no subscription was specified, try to select the Azure SDK Developer Playground subscription.
   158# Ignore errors to leave the automatically selected subscription.
   159if ($SubscriptionId) {
   160    $currentSubcriptionId = $context.Subscription.Id
   161    if ($currentSubcriptionId -ne $SubscriptionId) {
   162        Log "Selecting subscription '$SubscriptionId'"
   163        $null = Select-AzSubscription -Subscription $SubscriptionId
   164
   165        $exitActions += {
   166            Log "Selecting previous subscription '$currentSubcriptionId'"
   167            $null = Select-AzSubscription -Subscription $currentSubcriptionId
   168        }
   169
   170        # Update the context.
   171        $context = Get-AzContext
   172    }
   173} else {
   174    Log "Attempting to select subscription 'Azure SDK Developer Playground (faa080af-c1d8-40ad-9cce-e1a450ca5b57)'"
   175    $null = Select-AzSubscription -Subscription 'faa080af-c1d8-40ad-9cce-e1a450ca5b57' -ErrorAction Ignore
   176
   177    # Update the context.
   178    $context = Get-AzContext
   179
   180    $SubscriptionId = $context.Subscription.Id
   181    $PSBoundParameters['SubscriptionId'] = $SubscriptionId
   182}
   183
   184# Use cache of well-known team subs without having to be authenticated.
   185$wellKnownSubscriptions = @{
   186    'faa080af-c1d8-40ad-9cce-e1a450ca5b57' = 'Azure SDK Developer Playground'
   187    'a18897a6-7e44-457d-9260-f2854c0aca42' = 'Azure SDK Engineering System'
   188    '2cd617ea-1866-46b1-90e3-fffb087ebf9b' = 'Azure SDK Test Resources'
   189}
   190
   191# Print which subscription is currently selected.
   192$subscriptionName = $context.Subscription.Id
   193if ($wellKnownSubscriptions.ContainsKey($subscriptionName)) {
   194    $subscriptionName = '{0} ({1})' -f $wellKnownSubscriptions[$subscriptionName], $subscriptionName
   195}
   196
   197Log "Selected subscription '$subscriptionName'"
   198
   199if ($ServiceDirectory) {
   200    $root = [System.IO.Path]::Combine("$PSScriptRoot/../../../sdk", $ServiceDirectory) | Resolve-Path
   201    $preRemovalScript = Join-Path -Path $root -ChildPath 'remove-test-resources-pre.ps1'
   202    if (Test-Path $preRemovalScript) {
   203        Log "Invoking pre resource removal script '$preRemovalScript'"
   204
   205        if (!$PSCmdlet.ParameterSetName.StartsWith('ResourceGroup')) {
   206            $PSBoundParameters.Add('ResourceGroupName', $ResourceGroupName);
   207        }
   208
   209        &$preRemovalScript @PSBoundParameters
   210    }
   211
   212    # Make sure environment files from New-TestResources -OutFile are removed.
   213    Get-ChildItem -Path $root -Filter test-resources.json.env -Recurse | Remove-Item -Force:$Force
   214}
   215
   216$verifyDeleteScript = {
   217    try {
   218        $group = Get-AzResourceGroup -name $ResourceGroupName
   219    } catch {
   220        if ($_.ToString().Contains("Provided resource group does not exist")) {
   221            Write-Verbose "Resource group '$ResourceGroupName' not found. Continuing..."
   222            return
   223        }
   224        throw $_
   225    }
   226
   227    if ($group.ProvisioningState -ne "Deleting")
   228    {
   229        throw "Resource group is in '$($group.ProvisioningState)' state, expected 'Deleting'"
   230    }
   231}
   232
   233# Get any resources that can be purged after the resource group is deleted coerced into a collection even if empty.
   234$purgeableResources = Get-PurgeableGroupResources $ResourceGroupName
   235
   236Log "Deleting resource group '$ResourceGroupName'"
   237if ($Force -and !$purgeableResources) {
   238    Remove-AzResourceGroup -Name "$ResourceGroupName" -Force:$Force -AsJob
   239    Write-Verbose "Running background job to delete resource group '$ResourceGroupName'"
   240
   241    Retry $verifyDeleteScript 3
   242} else {
   243    # Don't swallow interactive confirmation when Force is false
   244    Remove-AzResourceGroup -Name "$ResourceGroupName" -Force:$Force
   245}
   246
   247# Now purge the resources that should have been deleted with the resource group.
   248Remove-PurgeableResources $purgeableResources
   249
   250$exitActions.Invoke()
   251
   252<#
   253.SYNOPSIS
   254Deletes the resource group deployed for a service directory from Azure.
   255
   256.DESCRIPTION
   257Removes a resource group and all its resources previously deployed using
   258New-TestResources.ps1.
   259If you are not currently logged into an account in the Az PowerShell module,
   260you will be asked to log in with Connect-AzAccount. Alternatively, you (or a
   261build pipeline) can pass $ProvisionerApplicationId and
   262$ProvisionerApplicationSecret to authenticate a service principal with access to
   263create resources.
   264
   265.PARAMETER BaseName
   266A name to use in the resource group and passed to the ARM template as 'baseName'.
   267This will delete the resource group named 'rg-<baseName>'
   268
   269.PARAMETER ResourceGroupName
   270The name of the resource group to delete.
   271
   272.PARAMETER TenantId
   273The tenant ID of a service principal when a provisioner is specified.
   274
   275.PARAMETER SubscriptionId
   276Optional subscription ID to use when deleting resources when logging in as a
   277provisioner. You can also use Set-AzContext if not provisioning.
   278
   279If you do not specify a SubscriptionId and are not logged in, one will be
   280automatically selected for you by the Connect-AzAccount cmdlet.
   281
   282Once you are logged in (or were previously), the selected SubscriptionId
   283will be used for subsequent operations that are specific to a subscription.
   284
   285.PARAMETER ProvisionerApplicationId
   286A service principal ID to provision test resources when a provisioner is specified.
   287
   288.PARAMETER ProvisionerApplicationSecret
   289A service principal secret (password) to provision test resources when a provisioner is specified.
   290
   291.PARAMETER ServiceDirectory
   292A directory under 'sdk' in the repository root - optionally with subdirectories
   293specified - in which to discover pre removal script named 'remove-test-resources-pre.json'.
   294
   295.PARAMETER Environment
   296Name of the cloud environment. The default is the Azure Public Cloud
   297('PublicCloud')
   298
   299.PARAMETER CI
   300Run script in CI mode. Infers various environment variable names based on CI convention.
   301
   302.PARAMETER Force
   303Force removal of resource group without asking for user confirmation
   304
   305.EXAMPLE
   306Remove-TestResources.ps1 keyvault -Force
   307Use the currently logged-in account to delete the resources created for Key Vault testing.
   308
   309.EXAMPLE
   310Remove-TestResources.ps1 `
   311    -ResourceGroupName "${env:AZURE_RESOURCEGROUP_NAME}" `
   312    -TenantId '$(TenantId)' `
   313    -ProvisionerApplicationId '$(AppId)' `
   314    -ProvisionerApplicationSecret '$(AppSecret)' `
   315    -Force `
   316    -Verbose `
   317When run in the context of an Azure DevOps pipeline, this script removes the
   318resource group whose name is stored in the environment variable
   319AZURE_RESOURCEGROUP_NAME.
   320
   321#>

View as plain text