From 19baa94081575baa90cd4638258b3b4d72e3ba16 Mon Sep 17 00:00:00 2001 From: Simone Bertaccini Date: Mon, 6 Dec 2021 18:28:34 +0100 Subject: [PATCH 1/5] Custom automation account removal script --- .../helper/Remove-AutomationAccount.ps1 | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 diff --git a/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 b/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 new file mode 100644 index 0000000000..b20297b933 --- /dev/null +++ b/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 @@ -0,0 +1,114 @@ +<# +.SYNOPSIS +Remove Automation account, Log analytics link and Update solution deployed with a given deployment name. Resources will be removed even if Log Analytics is in a different resource group than the Automation Account + +.DESCRIPTION +Remove Automation account, Log analytics link and Update solution deployed with a given deployment name. Resources will be removed even if Log Analytics is in a different resource group than the Automation Account + +.PARAMETER deploymentName +Mandatory. The deployment name to use and find resources to remove + +.PARAMETER searchRetryLimit +Optional. The maximum times to retry the search for resources via their removal tag + +.PARAMETER searchRetryInterval +Optional. The time to wait in between the search for resources via their remove tags + +.EXAMPLE +Remove-automationAccount -deploymentname 'aa-12345' + +Remove Automation account, Log analytics link and Update solution deployed starting with the deployment name 'aa-12345'. +#> +function Remove-automationAccount { + + [Cmdletbinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory = $true)] + [string] $deploymentName, + + [Parameter(Mandatory = $false)] + [string] $ResourceGroupName = 'validation-rg', + + [Parameter(Mandatory = $false)] + [int] $searchRetryLimit = 40, + + [Parameter(Mandatory = $false)] + [int] $searchRetryInterval = 60 + ) + + begin { + Write-Debug ('{0} entered' -f $MyInvocation.MyCommand) + + # Load helper + . (Join-Path $PSScriptRoot 'Remove-Resource.ps1') + } + + process { + + # Identify resources + # ------------------ + $searchRetryCount = 1 + do { + $deployments = Get-AzResourceGroupDeploymentOperation -DeploymentName $deploymentName -ResourceGroupName $resourceGroupName -ErrorAction 'SilentlyContinue' + if ($deployments) { + break + } + Write-Verbose ('Did not to find Automation Account deployment resources by name [{0}] in scope [{1}]. Retrying in [{2}] seconds [{3}/{4}]' -f $deploymentName, $deploymentScope, $searchRetryInterval, $searchRetryCount, $searchRetryLimit) -Verbose + Start-Sleep $searchRetryInterval + $searchRetryCount++ + } while ($searchRetryCount -le $searchRetryLimit) + + if (-not $deployments) { + throw "No deployment found for [$deploymentName]" + } + + $resourcesToRemove = @() + $unorderedResourceIds = @() + $childDeploymentsIds = $deployments.TargetResource | Where-Object { $_ -and $_ -match '/deployments/' } + + foreach ($childDeploymentId in $childDeploymentsIds) { + $searchRetryCount = 1 + $childDeploymentTokens = $childDeploymentId.Split('/') + $childDeploymentName = $childDeploymentTokens[8] + $childDeploymentResourceGroup = $childDeploymentTokens[4] + do { + Write-Verbose ('Searching child deployment named [{0}] in resource group [{1}]. Attempt [{2}/{3}]' -f $childDeploymentName, $childDeploymentResourceGroup, $searchRetryCount, $searchRetryLimit) -Verbose + $childDeployment = Get-AzResourceGroupDeploymentOperation -DeploymentName $childDeploymentName -ResourceGroupName $childDeploymentResourceGroup -ErrorAction 'SilentlyContinue' + if ($childDeployment) { + Write-Verbose ('[Success] Child deployment named [{0}] in resource group [{1}] found' -f $childDeploymentName, $childDeploymentResourceGroup) -Verbose + $unorderedResourceIds += $childDeployment.TargetResource + break + } + Write-Verbose ('[Failure] Did not to find child deployment named [{0}] in resource group [{1}]. Retrying in [{2}] seconds [{3}/{4}]' -f $childDeploymentName, $childDeploymentResourceGroup, $searchRetryInterval, $searchRetryCount, $searchRetryLimit) -Verbose + Start-Sleep $searchRetryInterval + $searchRetryCount++ + } while ($searchRetryCount -le $searchRetryLimit) + } + + $unorderedResourceIds = $unorderedResourceIds | Where-Object { $_ -and ($_ -notmatch '/variables/') -and ($_ -notmatch '/variables/') } + + $orderedResourceIds = @( + $unorderedResourceIds | Where-Object { $_ -match 'Microsoft.OperationsManagement/solutions/Updates' } + $unorderedResourceIds | Where-Object { $_ -match 'linkedServices/automation' } + $unorderedResourceIds | Where-Object { $_ -match 'Microsoft.Insights/diagnosticSettings' } + $unorderedResourceIds | Where-Object { $_ -match 'Microsoft.Automation/automationAccounts' } + ) + $resourcesToRemove = $orderedResourceIds | ForEach-Object { + @{ + resourceId = $_ + name = $_.Split('/')[-1] + type = $_.Split('/')[6..7] -join '/' + } + } + + # Remove resources + # ---------------- + if ($PSCmdlet.ShouldProcess(('[{0}] resources' -f $resourcesToRemove.Count), 'Remove')) { + Remove-Resource -resourceToRemove $resourcesToRemove -Verbose + } + } + + end { + Write-Debug ('{0} exited' -f $MyInvocation.MyCommand) + } +} From e4d8838d07447b8c62d7a9caf7a0ac8147837df4 Mon Sep 17 00:00:00 2001 From: Simone Bertaccini Date: Mon, 6 Dec 2021 19:52:20 +0100 Subject: [PATCH 2/5] Add removal exclusions --- .../helper/Remove-AutomationAccount.ps1 | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 b/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 index b20297b933..e1052269e5 100644 --- a/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 +++ b/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 @@ -53,7 +53,7 @@ function Remove-automationAccount { if ($deployments) { break } - Write-Verbose ('Did not to find Automation Account deployment resources by name [{0}] in scope [{1}]. Retrying in [{2}] seconds [{3}/{4}]' -f $deploymentName, $deploymentScope, $searchRetryInterval, $searchRetryCount, $searchRetryLimit) -Verbose + Write-Verbose ('[Failure] not to find Automation Account deployment resources by name [{0}] in scope [{1}]. Retrying in [{2}] seconds [{3}/{4}]' -f $deploymentName, $deploymentScope, $searchRetryInterval, $searchRetryCount, $searchRetryLimit) -Verbose Start-Sleep $searchRetryInterval $searchRetryCount++ } while ($searchRetryCount -le $searchRetryLimit) @@ -63,7 +63,7 @@ function Remove-automationAccount { } $resourcesToRemove = @() - $unorderedResourceIds = @() + $unorderedResourceIds = $deployments.TargetResource | Where-Object { $_ -and $_ -notmatch '/deployments/' } $childDeploymentsIds = $deployments.TargetResource | Where-Object { $_ -and $_ -match '/deployments/' } foreach ($childDeploymentId in $childDeploymentsIds) { @@ -85,7 +85,16 @@ function Remove-automationAccount { } while ($searchRetryCount -le $searchRetryLimit) } - $unorderedResourceIds = $unorderedResourceIds | Where-Object { $_ -and ($_ -notmatch '/variables/') -and ($_ -notmatch '/variables/') } + $unorderedResourceIds = $unorderedResourceIds | Where-Object { $_ ` + -and ($_ -notmatch '/Microsoft.Insights/diagnosticSettings/') ` + -and ($_ -notmatch '/variables/') ` + -and ($_ -notmatch '/softwareUpdateConfigurations/') ` + -and ($_ -notmatch '/jobSchedules/') ` + -and ($_ -notmatch '/schedules/') ` + -and ($_ -notmatch '/runbooks/') ` + -and ($_ -notmatch '/modules/') ` + -and ($_ -notmatch '/Microsoft.Authorization/roleAssignments/') ` + } | Select-Object -Unique $orderedResourceIds = @( $unorderedResourceIds | Where-Object { $_ -match 'Microsoft.OperationsManagement/solutions/Updates' } @@ -93,6 +102,7 @@ function Remove-automationAccount { $unorderedResourceIds | Where-Object { $_ -match 'Microsoft.Insights/diagnosticSettings' } $unorderedResourceIds | Where-Object { $_ -match 'Microsoft.Automation/automationAccounts' } ) + $resourcesToRemove = $orderedResourceIds | ForEach-Object { @{ resourceId = $_ From fb538e64b5ff13226a729e148d6a3c3dfaf40fa3 Mon Sep 17 00:00:00 2001 From: Simone Bertaccini Date: Mon, 6 Dec 2021 19:52:35 +0100 Subject: [PATCH 3/5] Include automation account as exception --- .../resourceRemoval/Remove-DeployedModule.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 b/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 index 3c288be8e8..3274ab5ff2 100644 --- a/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 +++ b/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 @@ -39,6 +39,18 @@ } Remove-VirtualMachine @inputObject -Verbose } + 'automationAccounts' { + Write-Verbose 'Run automation account removal script' -Verbose + # Load function + . (Join-Path $PSScriptRoot 'helper' 'Remove-AutomationAccount.ps1') + + # Invoke removal + $inputObject = @{ + deploymentName = $deploymentName + ResourceGroupName = $ResourceGroupName + } + Remove-VirtualMachine @inputObject -Verbose + } default { Write-Verbose 'Run default removal script' -Verbose # Load function From be13bebc7f571d9c57234bdc47e71e242822d0ee Mon Sep 17 00:00:00 2001 From: Simone Bertaccini Date: Tue, 7 Dec 2021 13:23:30 +0100 Subject: [PATCH 4/5] Function call fix --- utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 | 2 +- .../resourceRemoval/helper/Remove-AutomationAccount.ps1 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 b/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 index 3274ab5ff2..8398641a2d 100644 --- a/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 +++ b/utilities/pipelines/resourceRemoval/Remove-DeployedModule.ps1 @@ -49,7 +49,7 @@ deploymentName = $deploymentName ResourceGroupName = $ResourceGroupName } - Remove-VirtualMachine @inputObject -Verbose + Remove-AutomationAccount @inputObject -Verbose } default { Write-Verbose 'Run default removal script' -Verbose diff --git a/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 b/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 index e1052269e5..8867a18695 100644 --- a/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 +++ b/utilities/pipelines/resourceRemoval/helper/Remove-AutomationAccount.ps1 @@ -15,11 +15,11 @@ Optional. The maximum times to retry the search for resources via their removal Optional. The time to wait in between the search for resources via their remove tags .EXAMPLE -Remove-automationAccount -deploymentname 'aa-12345' +Remove-AutomationAccount -deploymentname 'aa-12345' Remove Automation account, Log analytics link and Update solution deployed starting with the deployment name 'aa-12345'. #> -function Remove-automationAccount { +function Remove-AutomationAccount { [Cmdletbinding(SupportsShouldProcess)] param( From d9ca63a3d956df57e334f075825fe1e01f8a5bd8 Mon Sep 17 00:00:00 2001 From: Simone Bertaccini Date: Tue, 7 Dec 2021 15:26:52 +0100 Subject: [PATCH 5/5] Full parameters --- .../.parameters/parameters.json | 158 +++++++++--------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/arm/Microsoft.Automation/automationAccounts/.parameters/parameters.json b/arm/Microsoft.Automation/automationAccounts/.parameters/parameters.json index 8eb92bdbc8..4284a511cc 100644 --- a/arm/Microsoft.Automation/automationAccounts/.parameters/parameters.json +++ b/arm/Microsoft.Automation/automationAccounts/.parameters/parameters.json @@ -76,85 +76,85 @@ } ] }, - // "linkedWorkspaceId": { - // "value": "/subscriptions/<>/resourcegroups/test-rg/providers/Microsoft.OperationalInsights/workspaces/test-law" - // }, - // "gallerySolutions": { - // "value": [ - // "Updates" - // ] - // }, - // "softwareUpdateConfigurations": { - // "value": [ - // { - // "name": "Windows_ZeroDay", - // "frequency": "Month", - // "operatingSystem": "Windows", - // "rebootSetting": "IfRequired", - // "scopeByTags": { - // "Update": [ - // "Automatic-Wave1" - // ] - // }, - // "maintenanceWindow": "PT4H", - // "updateClassifications": [ - // "Critical", - // "Security", - // "UpdateRollup", - // "FeaturePack", - // "ServicePack", - // "Definition", - // "Tools", - // "Updates" - // ], - // "includeUpdates": [ - // "654321" - // ], - // "excludeUpdates": [ - // "123456" - // ], - // "interval": 1, - // "monthlyOccurrences": [ - // { - // "occurrence": 3, - // "day": "Friday" - // } - // ], - // "startTime": "22:00" - // }, - // { - // "name": "Linux_ZeroDay", - // "frequency": "OneTime", - // "operatingSystem": "Linux", - // "rebootSetting": "IfRequired", - // "maintenanceWindow": "PT4H", - // "updateClassifications": [ - // "Critical", - // "Security", - // "Other" - // ], - // "includeUpdates": [ - // "kernel" - // ], - // "excludeUpdates": [ - // "icacls" - // ], - // "startTime": "2021-12-31T06:00" - // } - // ] - // }, - // "privateEndpoints": { - // "value": [ - // { - // "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-005-privateEndpoints", - // "service": "Webhook" - // }, - // { - // "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-005-privateEndpoints", - // "service": "DSCAndHybridWorker" - // } - // ] - // }, + "linkedWorkspaceId": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/microsoft.operationalinsights/workspaces/adp-sxx-az-law-x-001" + }, + "gallerySolutions": { + "value": [ + "Updates" + ] + }, + "softwareUpdateConfigurations": { + "value": [ + { + "name": "Windows_ZeroDay", + "frequency": "Month", + "operatingSystem": "Windows", + "rebootSetting": "IfRequired", + "scopeByTags": { + "Update": [ + "Automatic-Wave1" + ] + }, + "maintenanceWindow": "PT4H", + "updateClassifications": [ + "Critical", + "Security", + "UpdateRollup", + "FeaturePack", + "ServicePack", + "Definition", + "Tools", + "Updates" + ], + "includeUpdates": [ + "654321" + ], + "excludeUpdates": [ + "123456" + ], + "interval": 1, + "monthlyOccurrences": [ + { + "occurrence": 3, + "day": "Friday" + } + ], + "startTime": "22:00" + }, + { + "name": "Linux_ZeroDay", + "frequency": "OneTime", + "operatingSystem": "Linux", + "rebootSetting": "IfRequired", + "maintenanceWindow": "PT4H", + "updateClassifications": [ + "Critical", + "Security", + "Other" + ], + "includeUpdates": [ + "kernel" + ], + "excludeUpdates": [ + "icacls" + ], + "startTime": "2021-12-31T06:00" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-005-privateEndpoints", + "service": "Webhook" + }, + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-005-privateEndpoints", + "service": "DSCAndHybridWorker" + } + ] + }, "systemAssignedIdentity": { "value": true },