patterns/alz/scripts/old-scripts/Start-AMBACleanup.ps1 (202 lines of code) (raw):

# The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. <# .SYNOPSIS This script cleans up the resources deployed by the ALZ-Monitor automation, including alerts, policy assignments, policy initiatives, policy definitions, and policy assignment role assignments. .DESCRIPTION .NOTES In order for this script to function the deployed resources must have a tag _deployed_by_amba with a value of true and Policy resources must have metadata property named _deployed_by_amba with a value of True. These tags and metadata are included in the automation, but if they are subsequently removed, there may be orphaned resources after this script executes. The Role Assignments associated with Policy assignment identities and including _deployed_by_amba in the description field will also be deleted. This script leverages the Azure Resource Graph to find object to delete. Note that the Resource Graph lags behind ARM by a couple minutes. .LINK https://github.com/Azure/azure-monitor-baseline-alerts .EXAMPLE ./Start-AMBACleanup.ps1 -pseudoRootManagementGroup Contoso -WhatIf # show output of what would happen if deletes executed. .EXAMPLE ./Start-AMBACleanup.ps1 -pseudoRootManagementGroup Contoso # execute the script and will ask for confirmation before taking the configured action. .EXAMPLE ./Start-AMBACleanup.ps1 -pseudoRootManagementGroup Contoso -Confirm:$false # execute the script without asking for confirmation before taking the configured action. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param( # the pseudo managemnt group to start from [Parameter(Mandatory = $True, ValueFromPipeline = $false)] [string]$pseudoRootManagementGroup ) Function Search-AzGraphRecursive { # ensure query results with more than 100 resources and/or over more than 10 management groups are returned param($query, $managementGroupNames, $skipToken) $optionalParams = @{} If ($skipToken) { $optionalParams += @{skipToken = $skipToken } } # ARG will only query 10 management groups at a time--implement batching If ($managementGroupNames.count -gt 10) { $managementGroupBatches = @() For ($i = 0; $i -le $managementGroupNames.count; $i = $i + 10) { $batchGroups = $managementGroupNames[$i..($i + 9)] $managementGroupBatches += , @($batchGroups) If ($batchGroups.count -lt 10) { continue } } $result = @() ForEach ($managementGroupBatch in $managementGroupBatches) { $batchResult = Search-AzGraph -Query $query -ManagementGroup $managementGroupBatch -Verbose:$false @optionalParams # resource graph returns pages of 100 resources, if there are more than 100 resources in a batch, recursively query for more If ($batchResult.count -eq 100 -and $batchResult.SkipToken) { $result += $batchResult Search-AzGraphRecursive -query $query -managementGroupNames $managementGroupNames -skipToken $batchResult.SkipToken } else { $result += $batchResult } } } Else { $result = Search-AzGraph -Query $query -ManagementGroup $managementGroupNames -Verbose:$false @optionalParams If ($result.count -eq 100 -and $result.SkipToken) { Search-AzGraphRecursive -query $query -managementGroupNames $managementGroupNames -skipToken $result.SkipToken } } $result } Function Iterate-ManagementGroups($mg) { $script:managementGroups += $mg.Name if ($mg.Children) { foreach ($child in $mg.Children) { if ($child.Type -eq 'Microsoft.Management/managementGroups') { Iterate-ManagementGroups $child } } } } $ErrorActionPreference = 'Stop' If (-NOT(Get-Module -ListAvailable Az.Resources)) { Write-Warning "This script requires the Az.Resources module." $response = Read-Host "Would you like to install the 'Az.Resources' module now? (y/n)" If ($response -match '[yY]') { Install-Module Az.Resources -Scope CurrentUser } } If (-NOT(Get-Module -ListAvailable Az.ResourceGraph)) { Write-Warning "This script requires the Az.ResourceGraph module." $response = Read-Host "Would you like to install the 'Az.ResourceGraph' module now? (y/n)" If ($response -match '[yY]') { Install-Module Az.ResourceGraph -Scope CurrentUser } } If (-NOT(Get-Module -ListAvailable Az.ManagedServiceIdentity)) { Write-Warning "This script requires the Az.ManagedServiceIdentity module." $response = Read-Host "Would you like to install the 'Az.ManagedServiceIdentity' module now? (y/n)" If ($response -match '[yY]') { Install-Module Az.ManagedServiceIdentity -Scope CurrentUser } } # get all management groups -- used in graph query scope $managementGroups = @() $allMgs = Get-AzManagementGroup -GroupName $pseudoRootManagementGroup -Expand -Recurse foreach ($mg in $allMgs) { Iterate-ManagementGroups $mg } Write-Host "Found '$($managementGroups.Count)' management group(s) (including the parent one) which are part of the '$pseudoRootManagementGroup' management group hierarchy, to be queried for AMBA-ALZ resources." If ($managementGroups.count -eq 0) { Write-Error "The command 'Get-AzManagementGroups' returned '0' groups. This script needs to run with Owner permissions on the Azure Landing Zones intermediate root management group to effectively clean up Policies and all related resources deployed by AMBA-ALZ." } # get alert resources to delete $query = "Resources | where type in~ ('Microsoft.Insights/metricAlerts','Microsoft.Insights/activityLogAlerts', 'Microsoft.Insights/scheduledQueryRules') and tags['_deployed_by_amba'] =~ 'True' | project id" $alertResourceIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Select-Object -ExpandProperty Id | Sort-Object | Get-Unique Write-Host "- Found '$($alertResourceIds.Count)' metric, activity log and log alerts with tag '_deployed_by_amba=True' to be deleted." # get resource group to delete $query = "ResourceContainers | where type =~ 'microsoft.resources/subscriptions/resourcegroups' and tags['_deployed_by_amba'] =~ 'True' | project id" $resourceGroupIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Select-Object -ExpandProperty Id | Sort-Object | Get-Unique Write-Host "- Found '$($resourceGroupIds.Count)' resource groups with tag '_deployed_by_amba=True' to be deleted." # get policy assignments to delete $query = "policyresources | where type =~ 'microsoft.authorization/policyAssignments' | project name,metadata=parse_json(properties.metadata),type,identity,id | where metadata._deployed_by_amba =~ 'true'" $policyAssignmentIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Select-Object -ExpandProperty Id | Sort-Object | Get-Unique Write-Host "- Found '$($policyAssignmentIds.Count)' policy assignments with metadata '_deployed_by_amba=True' to be deleted." # get policy set definitions to delete $query = "policyresources | where type =~ 'microsoft.authorization/policysetdefinitions' | project name,metadata=parse_json(properties.metadata),type,id | where metadata._deployed_by_amba =~ 'true' | project id" $policySetDefinitionIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Select-Object -ExpandProperty Id | Sort-Object | Get-Unique Write-Host "- Found '$($policySetDefinitionIds.Count)' policy set definitions with metadata '_deployed_by_amba=True' to be deleted." # get policy definitions to delete $query = "policyresources | where type =~ 'microsoft.authorization/policyDefinitions' | project name,metadata=parse_json(properties.metadata),type,id | where metadata._deployed_by_amba =~ 'true' | project id" $policyDefinitionIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Select-Object -ExpandProperty Id | Sort-Object | Get-Unique Write-Host "- Found '$($policyDefinitionIds.Count)' policy definitions with metadata '_deployed_by_amba=True' to be deleted." # get user assigned managed identities to delete $query = "Resources | where type =~ 'Microsoft.ManagedIdentity/userAssignedIdentities' and tags['_deployed_by_amba'] =~ 'True' | project id, name, principalId = properties.principalId, tenantId, subscriptionId, resourceGroup" $UamiIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Sort-Object -Property id | Get-Unique -AsString Write-Host "- Found '$($UamiIds.Count)' user assigned managed identities with tag '_deployed_by_amba=True' to be deleted." # get role assignments to delete $query = "authorizationresources | where type =~ 'microsoft.authorization/roleassignments' and properties.description == '_deployed_by_amba' | project roleDefinitionId = properties.roleDefinitionId, objectId = properties.principalId, scope = properties.scope, id" $roleAssignments = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Sort-Object -Property id | Get-Unique -AsString Write-Host "- Found '$($roleAssignments.Count)' role assignments with description '_deployed_by_amba' to be deleted." # get alert processing rules to delete #$query = "resources | where type =~ 'Microsoft.AlertsManagement/actionRules' | where tags['_deployed_by_amba'] =~ 'True'| project id" $query = "resources | where type =~ 'Microsoft.AlertsManagement/actionRules' | where name startswith 'apr-AMBA-' and properties.description startswith 'AMBA Notification Assets - ' and tags['_deployed_by_amba'] =~ 'True'| project id" $alertProcessingRuleIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Select-Object -ExpandProperty Id | Sort-Object | Get-Unique Write-Host "- Found '$($alertProcessingRuleIds.Count)' alert processing rule(s) with tag '_deployed_by_amba=True' to be deleted." # get action groups to delete $query = "resources | where type =~ 'Microsoft.Insights/actionGroups' | where tags['_deployed_by_amba'] =~ 'True' | project id" $actionGroupIds = Search-AzGraphRecursive -Query $query -ManagementGroupNames $managementGroups | Select-Object -ExpandProperty Id | Sort-Object | Get-Unique Write-Host "- Found '$($actionGroupIds.Count)' action group(s) with tag '_deployed_by_amba=True' to be deleted." If (($alertResourceIds.count -gt 0) -or ($policyAssignmentIds.count -gt 0) -or ($policySetDefinitionIds.count -gt 0) -or ($policyDefinitionIds.count -gt 0) -or ($roleAssignments.count -gt 0) -or ($UamiIds.count -gt 0) -or ($alertProcessingRuleIds.count -gt 0) -or ($actionGroupIds.count -gt 0)) { If ($PSCmdlet.ShouldProcess($pseudoRootManagementGroup, "Delete alerts, policy assignments, policy initiatives, policy definitions, policy role assignments, user assigned managed identities, alert processing rules and action groups deployed by AMBA-ALZ on the '$pseudoRootManagementGroup' Management Group hierarchy ..." )) { # delete alert resources If ($alertResourceIds.count -gt 0) { Write-Host "-- Deleting alerts ..." $alertResourceIds | Foreach-Object { Remove-AzResource -ResourceId $_ -Force } } <# ### Leave this in place incase we decide to remove the rg later on # delete resource groups If ($resourceGroupIds.count -gt 0) { Write-Host "-- Deleting resource groups ..." $resourceGroupIds | ForEach-Object { Remove-AzResourceGroup -ResourceGroupId $_ -Confirm:$false | Out-Null } } #> # delete policy assignments If ($policyAssignmentIds.count -gt 0) { Write-Host "-- Deleting policy assignments ..." $policyAssignmentIds | ForEach-Object { Remove-AzPolicyAssignment -Id $_ -Confirm:$false -ErrorAction Stop } } # delete policy set definitions If ($policySetDefinitionIds.count -gt 0) { Write-Host "-- Deleting policy set definitions ..." $policySetDefinitionIds | ForEach-Object { Remove-AzPolicySetDefinition -Id $_ -Force } } # delete policy definitions If ($policyDefinitionIds.count -gt 0) { Write-Host "-- Deleting policy definitions ..." $policyDefinitionIds | ForEach-Object { Remove-AzPolicyDefinition -Id $_ -Force } } # delete role assignments If ($roleAssignments.count -gt 0) { Write-Host "-- Deleting role assignments ..." $roleAssignments | Select-Object -Property objectId, roleDefinitionId, scope | ForEach-Object { Remove-AzRoleAssignment @psItem -Confirm:$false | Out-Null } } # delete user assigned managed identities If ($UamiIds.count -gt 0) { Write-Host "-- Deleting user assigned managed identities ..." $UamiIds | ForEach-Object { Remove-AzUserAssignedIdentity -ResourceGroupName $_.resourceGroup -Name $_.name -SubscriptionId $_.subscriptionId -Confirm:$false } } # delete alert processing rules If ($alertProcessingRuleIds.count -gt 0) { Write-Host "-- Deleting alert processing rules ..." $alertProcessingRuleIds | Foreach-Object { Remove-AzResource -ResourceId $_ -Force } } # delete action groups If ($actionGroupIds.count -gt 0) { Write-Host "-- Deleting action groups ..." $actionGroupIds | Foreach-Object { Remove-AzResource -ResourceId $_ -Force } } } } Write-Host "=== Script execution completed. ==="