utilities/pipelines/resourceRemoval/helper/Remove-Deployment.ps1 (127 lines of code) (raw):
<#
.SYNOPSIS
Invoke the removal of a deployed module
.DESCRIPTION
Invoke the removal of a deployed module.
Requires the resource in question to be tagged with 'removeModule = <moduleName>'
.PARAMETER ModuleName
Mandatory. The name of the module to remove
.PARAMETER ResourceGroupName
Optional. The resource group of the resource to remove
.PARAMETER ManagementGroupId
Optional. The ID of the management group to fetch deployments from. Relevant for management-group level deployments.
.PARAMETER DeploymentNames
Optional. The deployment names to use for the removal
.PARAMETER TemplateFilePath
Mandatory. The path to the deployment file
.PARAMETER RemovalSequence
Optional. The order of resource types to apply for deletion
.EXAMPLE
Remove-Deployment -DeploymentNames @('KeyVault-t1','KeyVault-t2') -TemplateFilePath 'C:/main.json'
Remove all resources deployed via the with deployment names 'KeyVault-t1' & 'KeyVault-t2'
#>
function Remove-Deployment {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $false)]
[string] $ResourceGroupName,
[Parameter(Mandatory = $false)]
[string] $ManagementGroupId,
[Parameter(Mandatory = $true)]
[string[]] $DeploymentNames,
[Parameter(Mandatory = $true)]
[string] $TemplateFilePath,
[Parameter(Mandatory = $false)]
[string[]] $RemovalSequence = @()
)
begin {
Write-Debug ('{0} entered' -f $MyInvocation.MyCommand)
# Load helper
. (Join-Path (Get-Item -Path $PSScriptRoot).parent.parent.FullName 'sharedScripts' 'Get-ScopeOfTemplateFile.ps1')
. (Join-Path (Split-Path $PSScriptRoot -Parent) 'helper' 'Get-DeploymentTargetResourceList.ps1')
. (Join-Path (Split-Path $PSScriptRoot -Parent) 'helper' 'Get-ResourceIdsAsFormattedObjectList.ps1')
. (Join-Path (Split-Path $PSScriptRoot -Parent) 'helper' 'Get-OrderedResourcesList.ps1')
. (Join-Path (Split-Path $PSScriptRoot -Parent) 'helper' 'Remove-ResourceList.ps1')
}
process {
$azContext = Get-AzContext
# Prepare data
# ============
$deploymentScope = Get-ScopeOfTemplateFile -TemplateFilePath $TemplateFilePath
# Fundamental checks
if ($deploymentScope -eq 'resourcegroup' -and -not (Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction 'SilentlyContinue')) {
Write-Verbose "Resource group [$ResourceGroupName] does not exist (anymore). Skipping removal of its contained resources" -Verbose
return
}
# Fetch deployments
# =================
$deployedTargetResources = @()
foreach ($deploymentName in $DeploymentNames) {
$deploymentsInputObject = @{
Name = $deploymentName
Scope = $deploymentScope
}
if (-not [String]::IsNullOrEmpty($ResourceGroupName)) {
$deploymentsInputObject['resourceGroupName'] = $ResourceGroupName
}
if (-not [String]::IsNullOrEmpty($ManagementGroupId)) {
$deploymentsInputObject['ManagementGroupId'] = $ManagementGroupId
}
$deployedTargetResources += Get-DeploymentTargetResourceList @deploymentsInputObject
}
if ($deployedTargetResources.Count -eq 0) {
throw 'No deployment target resources found.'
}
[array] $deployedTargetResources = $deployedTargetResources | Select-Object -Unique
Write-Verbose ('Total number of deployment target resources after fetching deployments [{0}]' -f $deployedTargetResources.Count) -Verbose
# Pre-Filter & order items
# ========================
$rawTargetResourceIdsToRemove = $deployedTargetResources | Sort-Object -Property { $_.Split('/').Count } -Descending | Select-Object -Unique
Write-Verbose ('Total number of deployment target resources after pre-filtering (duplicates) & ordering items [{0}]' -f $rawTargetResourceIdsToRemove.Count) -Verbose
# Format items
# ============
[array] $resourcesToRemove = Get-ResourceIdsAsFormattedObjectList -ResourceIds $rawTargetResourceIdsToRemove
Write-Verbose ('Total number of deployment target resources after formatting items [{0}]' -f $resourcesToRemove.Count) -Verbose
if ($resourcesToRemove.Count -eq 0) {
return
}
# Filter resources
# ================
# Resource IDs in the below list are ignored by the removal
$resourceIdsToIgnore = @(
'/subscriptions/{0}/resourceGroups/NetworkWatcherRG' -f $azContext.Subscription.Id
)
# Resource IDs starting with a prefix in the below list are ignored by the removal
$resourceIdPrefixesToIgnore = @(
'/subscriptions/{0}/providers/Microsoft.Security/autoProvisioningSettings/' -f $azContext.Subscription.Id
'/subscriptions/{0}/providers/Microsoft.Security/deviceSecurityGroups/' -f $azContext.Subscription.Id
'/subscriptions/{0}/providers/Microsoft.Security/iotSecuritySolutions/' -f $azContext.Subscription.Id
'/subscriptions/{0}/providers/Microsoft.Security/pricings/' -f $azContext.Subscription.Id
'/subscriptions/{0}/providers/Microsoft.Security/securityContacts/' -f $azContext.Subscription.Id
'/subscriptions/{0}/providers/Microsoft.Security/workspaceSettings/' -f $azContext.Subscription.Id
)
[regex] $ignorePrefix_regex = '(?i)^(' + (($resourceIdPrefixesToIgnore | ForEach-Object { [regex]::escape($_) }) -join '|') + ')'
if ($resourcesToIgnore = $resourcesToRemove | Where-Object { $_.resourceId -in $resourceIdsToIgnore -or $_.resourceId -match $ignorePrefix_regex }) {
Write-Verbose 'Resources excluded from removal:' -Verbose
$resourcesToIgnore | ForEach-Object { Write-Verbose ('- Ignore [{0}]' -f $_.resourceId) -Verbose }
}
[array] $resourcesToRemove = $resourcesToRemove | Where-Object { $_.resourceId -notin $resourceIdsToIgnore -and $_.resourceId -notmatch $ignorePrefix_regex }
Write-Verbose ('Total number of deployments after filtering all dependency resources [{0}]' -f $resourcesToRemove.Count) -Verbose
# Order resources
# ===============
[array] $resourcesToRemove = Get-OrderedResourcesList -ResourcesToOrder $resourcesToRemove -Order $RemovalSequence
Write-Verbose ('Total number of deployments after final ordering of resources [{0}]' -f $resourcesToRemove.Count) -Verbose
# Remove resources
# ================
if ($resourcesToRemove.Count -gt 0) {
if ($PSCmdlet.ShouldProcess(('[{0}] resources' -f (($resourcesToRemove -is [array]) ? $resourcesToRemove.Count : 1)), 'Remove')) {
Remove-ResourceList -ResourcesToRemove $resourcesToRemove
}
} else {
Write-Verbose 'Found [0] resources to remove'
}
}
end {
Write-Debug ('{0} exited' -f $MyInvocation.MyCommand)
}
}