utilities/pipelines/deploymentRemoval/Clear-ManagementGroupDeploymentHistory.ps1 (92 lines of code) (raw):

 <# .SYNOPSIS Bulk delete all deployments on the given management group scope .DESCRIPTION Bulk delete all deployments on the given management group scope .PARAMETER ManagementGroupId Mandatory. The Resource ID of the Management Group to remove the deployments from. .PARAMETER DeploymentStatusToExclude Optional. The status to exlude from removals. Can be multiple. By default, we exclude any deployment that is in state 'running' or 'failed'. .PARAMETER maxDeploymentRetentionInDays Optional. The time to keep deployments with a status to exclude. In other words, if a deployment is in a status to exclude, but older than the threshold, it will be deleted. .EXAMPLE Clear-ManagementGroupDeploymentHistory -ManagementGroupId 'MyManagementGroupId' Bulk remove all 'non-running' & 'non-failed' deployments from the Management Group with ID 'MyManagementGroupId' .EXAMPLE Clear-ManagementGroupDeploymentHistory -ManagementGroupId 'MyManagementGroupId' -DeploymentStatusToExclude @('running') Bulk remove all 'non-running' deployments from the Management Group with ID 'MyManagementGroupId' #> function Clear-ManagementGroupDeploymentHistory { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ManagementGroupId, [Parameter(Mandatory = $false)] [string[]] $DeploymentStatusToExclude = @('running', 'failed'), [Parameter(Mandatory = $false)] [int] $maxDeploymentRetentionInDays = 14 ) [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Enables web reponse $deploymentThreshold = (Get-Date).AddDays(-1 * $maxDeploymentRetentionInDays) # Load used functions . (Join-Path (Split-Path $PSScriptRoot -Parent) 'sharedScripts' 'Split-Array.ps1') $getInputObject = @{ Method = 'GET' Uri = "https://management.azure.com/providers/Microsoft.Management/managementGroups/$ManagementGroupId/providers/Microsoft.Resources/deployments/?api-version=2021-04-01" Headers = @{ Authorization = 'Bearer {0}' -f (Get-AzAccessToken).Token } } $response = Invoke-RestMethod @getInputObject if (($response | Get-Member -MemberType 'NoteProperty').Name -notcontains 'value') { throw ('Fetching deployments failed with error [{0}]' -f ($reponse | Out-String)) } Write-Verbose ('Found [{0}] deployments in management group [{1}]' -f $response.value.Count, $ManagementGroupId) -Verbose $relevantDeployments = $response.value | Where-Object { $_.properties.provisioningState -notin $DeploymentStatusToExclude -or ([DateTime]$_.properties.timestamp) -lt $deploymentThreshold -and $_.properties.provisioningState -ne 'running' # we should never delete 'running' deployments } Write-Verbose ('Filtering [{0}] deployments out as they are in state [{1}] or newer than [{2}] days ({3})' -f ($response.value.Count - $relevantDeployments.Count), ($DeploymentStatusToExclude -join '/'), $maxDeploymentRetentionInDays, $deploymentThreshold.ToString('yyyy-MM-dd')) -Verbose if (-not $relevantDeployments) { Write-Verbose 'No deployments found' -Verbose return } $rawDeploymentChunks = Split-Array -InputArray $relevantDeployments -SplitSize 100 if ($relevantDeployments.Count -le 100) { $relevantDeploymentChunks = , $rawDeploymentChunks } else { $relevantDeploymentChunks = $rawDeploymentChunks } Write-Verbose ('Triggering the removal of [{0}] deployments from management group [{1}]' -f $relevantDeployments.Count, $ManagementGroupId) -Verbose foreach ($deployments in $relevantDeploymentChunks) { $requests = $deployments | ForEach-Object { @{ httpMethod = 'DELETE' name = (New-Guid).Guid # Each batch request needs a unique ID requestHeaderDetails = @{ commandName = 'HubsExtension.Microsoft.Resources/deployments.BulkDelete.execute' } url = '/providers/Microsoft.Management/managementGroups/{0}/providers/Microsoft.Resources/deployments/{1}?api-version=2019-08-01' -f $ManagementGroupId, $_.name } } if ($requests -is [hashtable]) { $requests = , $requests } $removeInputObject = @{ Method = 'POST' Uri = 'https://management.azure.com/batch?api-version=2020-06-01' Headers = @{ Authorization = 'Bearer {0}' -f (Get-AzAccessToken).Token 'Content-Type' = 'application/json' } Body = @{ requests = $requests } | ConvertTo-Json -Depth 4 } if ($PSCmdlet.ShouldProcess(('Removal of [{0}] deployments' -f $requests.Count), 'Request')) { $null = Invoke-RestMethod @removeInputObject } } Write-Verbose 'Script execution finished. Note that the removal can take a few minutes to propagate.' -Verbose }