From 5825d22517bf44dfbf18999823ec6053effe8d82 Mon Sep 17 00:00:00 2001
From: Jovana Taylor
Date: Mon, 11 May 2020 09:41:23 -0700
Subject: [PATCH 1/5] add email report to remove-resourcegroups
---
Utility/ARM/Remove-ResourceGroups.ps1 | 275 ++++++++++++++++++++++++++
1 file changed, 275 insertions(+)
create mode 100644 Utility/ARM/Remove-ResourceGroups.ps1
diff --git a/Utility/ARM/Remove-ResourceGroups.ps1 b/Utility/ARM/Remove-ResourceGroups.ps1
new file mode 100644
index 0000000..29dcba4
--- /dev/null
+++ b/Utility/ARM/Remove-ResourceGroups.ps1
@@ -0,0 +1,275 @@
+
+<#PSScriptInfo
+
+.VERSION 1.0
+
+.GUID
+
+.AUTHOR AzureAutomationTeam
+
+.COMPANYNAME Microsoft
+
+.COPYRIGHT
+
+.TAGS AzureAutomation Utility
+
+.LICENSEURI
+
+.PROJECTURI https://github.com/azureautomation/runbooks/blob/master/Utility/Remove-ResourceGroups.ps1
+
+.ICONURI
+
+.EXTERNALMODULEDEPENDENCIES
+
+.REQUIREDSCRIPTS
+
+.EXTERNALSCRIPTDEPENDENCIES
+
+.RELEASENOTES
+
+#>
+
+#Requires -Module Azure
+#Requires -Module AzureRM.Profile
+#Requires -Module AzureRM.Resources
+
+<#
+.SYNOPSIS
+ Connects to Azure and removes all resource groups which match the name filter
+
+.DESCRIPTION
+ This runbook connects to Azure and removes all resource groups which match the name filter.
+ You can run across multiple subscriptions, delete all resource groups, or run in preview mode.
+ Warning: This will delete all resources, including child resources in a group when preview mode is set to $false.
+
+.PARAMETER NameFilter
+ Optional
+ Allows you to specify a name filter to limit the resource groups that you will KEEP or DELETE.
+ Pass multiple name filters through a comma separated list.
+ The filter is not case sensitive and will match any resource group that contains the string.
+
+.PARAMETER PreviewMode
+ Optional with default of $true.
+ Execute the runbook to see which resource groups would be deleted but take no action.
+
+#>
+
+workflow Remove-ResourceGroups
+{
+ [OutputType([String])]
+
+ param(
+ [parameter(Mandatory = $false)]
+ [string] $NameFilter,
+
+ [parameter(Mandatory = $false)]
+ [bool] $PreviewMode = $true,
+
+ [parameter(Mandatory = $false)]
+ [string] $FromEmailAddress,
+
+ [parameter(Mandatory = $false)]
+ [string] $DestEmailAddress,
+
+ [parameter(Mandatory = $false)]
+ [sting] $SendGridToken # To be converted to keyvault
+ )
+
+ $VerbosePreference = 'Continue'
+
+ inlineScript {
+ $NameFilter = $using:NameFilter
+ $PreviewMode = $using:PreviewMode
+ $PSPrivateMetadata = $using:PSPrivateMetadata
+
+ $FromEmailAddress = $using:FromEmailAddress
+ $DestEmailAddress = $using:DestEmailAddress
+ $SendGridToken = $using:SendGridToken
+
+ # Connect to Azure with RunAs account
+ $conn = Get-AutomationConnection -Name "AzureRunAsConnection"
+ $null = Add-AzureRmAccount `
+ -ServicePrincipal `
+ -Tenant $conn.TenantId `
+ -ApplicationId $conn.ApplicationId `
+ -CertificateThumbprint $conn.CertificateThumbprint
+
+ # Use the subscription that this Automation account is in
+ $null = Select-AzureRmSubscription -SubscriptionId $conn.SubscriptionID
+
+ # Parse name filter list
+ if ($NameFilter) {
+ $nameFilterList = $NameFilter.Split(',')
+ [regex]$nameFilterRegex = '(' + (($nameFilterList | foreach {[regex]::escape($_.ToLower())}) –join "|") + ')'
+ }
+
+ # Find the resource group that this Automation job is running in so that we can protect it from being removed
+ if ([string]::IsNullOrEmpty($PSPrivateMetadata.JobId.Guid)) {
+ throw ("This is not running from the Automation service, so could not retrieve the resource group for the Automation account in order to protect it from being removed.")
+ exit
+ }
+ else {
+ $resources = Get-AzureRmResource
+ $automationResources = $resources | ? {$_.ResourceType -eq "Microsoft.Automation/automationAccounts"}
+ foreach ($automation in $automationResources) {
+ # Loop through each Automation account to find this job
+ $job = Get-AzureRmAutomationJob -ResourceGroupName $automation.ResourceGroupName -AutomationAccountName $automation.Name -Id $PSPrivateMetadata.JobId.Guid -ErrorAction SilentlyContinue
+ if (!([string]::IsNullOrEmpty($job))) {
+ $thisResourceGroupName = $job.ResourceGroupName
+ return
+ }
+ }
+ }
+
+ # Process the resource groups
+ try {
+ # Find RGs to remove based on passed in name filter
+ $resourceGroups = Get-AzureRmResourceGroup | `
+ ? { $nameFilterList.Count -eq 0 -or $_.ResourceGroupName.ToLower() -match $nameFilterRegex }
+ $groupMap = @{}
+ $resourceGroups | % { $groupMap[$_.ResourceGroupName] = $_ }
+
+ # Get the locks on the resources and RGs
+ $locks = Get-AzureRmResourceLock
+ $rLocks = $locks | ? {$_.ExtensionResourceType -eq "Microsoft.Authorization/locks"} # locks on resources
+ $rgLocks = $locks | ? {$_.ResourceType -eq "Microsoft.Authorization/locks"} # locks on RGs
+
+ $resourceGroups | % {
+ $currentRg = $_
+ # Filter out RGs with locks
+ $rgLock = $rgLocks | ? {$_.ResourceGroupName -eq $currentRg.ResourceGroupName}
+ if ($rgLock) {
+ Write-Output "$($currentRg.ResourceGroupName) is locked: $($rgLock.Properties.notes)"
+ $groupMap.Remove($currentRg.ResourceGroupName)
+ return
+ }
+ # Filter out the RG running this runbook
+ if ($currentRg.ResourceGroupName -eq $thisResourceGroupName) {
+ Write-Output ("The resource group for this runbook job will not be removed. Resource group: $thisResourceGroupName")
+ $groupMap.Remove($currentRg.ResourceGroupName)
+ return
+ }
+ }
+
+ $resources = Get-AzureRmResource
+ $resourcesToRemove = ($resources | ? {$groupMap.ContainsKey($_.ResourceGroupName)})
+ $resourcesToRemove | % {
+ $currentR = $_
+ $rLock = $rLocks | ? {$_.ResourceGroupName -eq $currentR.ResourceGroupName} | ? {$_.ResourceName -eq $currentR.Name }
+ if ($rLock) {
+ # Filter out RGs that contain locked resources
+ Write-Output "$($currentR.ResourceGroupName)/$($currentR.Name) is locked: $($rLock.Properties.notes)"
+ $groupMap.Remove($currentR.ResourceGroupName)
+ }
+ }
+
+ # The RGs to be removed
+ $groupsToRemove = $groupMap.Values
+
+ # No matching groups were found to remove
+ if ($groupsToRemove.Count -eq 0) {
+ Write-Output "No matching resource groups found."
+ return
+ }
+ # Matching groups were found to remove
+ else
+ {
+ # The resources in RGs to be removed
+ $resourcesToRemove = ($resources | ? {$groupMap.ContainsKey($_.ResourceGroupName)})
+
+ # In preview mode, so report what would be removed, but take no action.
+ if ($PreviewMode -eq $true) {
+ Write-Output "Preview Mode: The following resource groups would be removed:"
+ foreach ($group in $groupsToRemove){
+ Write-Output "`t$($group.ResourceGroupName)"
+ }
+ Write-Output "Preview Mode: The following resources would be removed:"
+ $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
+ }
+ # Remove the resource groups
+ else {
+ Write-Output "The following resource groups will be removed:"
+ foreach ($group in $groupsToRemove){
+ Write-Output $($group.ResourceGroupName)
+ }
+ Write-Output "The following resources will be removed:"
+ $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
+ # Here is where the remove actions happen
+ foreach ($resourceGroup in $groupsToRemove) {
+ Write-Output "Starting to remove resource group: $($resourceGroup.ResourceGroupName) ..."
+ # Remove-AzureRmResourceGroup -Name $($resourceGroup.ResourceGroupName) -Force
+ if ((Get-AzureRmResourceGroup -Name $($resourceGroup.ResourceGroupName) -ErrorAction SilentlyContinue) -eq $null) {
+ Write-Output "...successfully removed resource group: $($resourceGroup.ResourceGroupName)"
+ }
+ }
+ }
+ Write-Output "Completed."
+ }
+ }
+ catch {
+ $errorMessage = $_
+ }
+ if ($errorMessage) {
+ Write-Error $errorMessage
+ }
+
+ # Send email report
+ if ($DestEmailAddress -and $FromEmailAddress -and $SendGridKey) {
+
+ $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
+ $headers.Add("Authorization", "Bearer " + $SendGridKey)
+ $headers.Add("Content-Type", "application/json")
+
+ if ($PreviewMode) {
+ $subject = "Azure Resources scheduled for cleanup"
+ $content = "Cleanup summary
"
+ $content += "Resources scheduled for clean up:
"
+ } else {
+ $subject = "Azure Resources cleanup report"
+ $content = "Cleanup summary
"
+ $content += "Resources cleaned up:
"
+ }
+
+ # TODO: if cleanup occurred, should report on any failures
+
+ $resourcesToRemoveTable = $resourcesToRemove | Select-Object Name, ResourceGroupName, ResourceType | ConvertTo-Html -Fragment
+ $content += $resourcesToRemoveTable
+
+ $content += "Locked Resource Groups:
"
+ $content += $rgLocks | Select-Object -ExpandProperty Properties -Property ResourceGroupName, Name | Select-Object ResourceGroupName, Name, notes | ConvertTo-Html -Fragment
+
+ $content += "Locked resources:
"
+ $content += $rLocks | Select-Object -ExpandProperty Properties -Property ResourceName, ResourceGroupName, Name | Select-Object ResourceName, ResourceGroupName, Name, notes | ConvertTo-Html -Fragment
+
+ $content += ""
+
+ $content = $content -join [Environment]::NewLine
+
+ $body = @{
+ personalizations = @(
+ @{
+ to = @(
+ @{
+ email = $DestEmailAddress
+ }
+ )
+ }
+ )
+ from = @{
+ email = $FromEmailAddress
+ }
+ subject = $subject
+ content = @(
+ @{
+ type = "text/html"
+ value = $content
+ }
+ )
+ }
+
+ $bodyJson = $body | ConvertTo-Json -Depth 8
+
+ $response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -Body $bodyJson
+ }
+ }
+}
\ No newline at end of file
From ea9d3566c44171492e187e44dff20bed1e50dec4 Mon Sep 17 00:00:00 2001
From: Jovana Taylor
Date: Mon, 11 May 2020 09:45:35 -0700
Subject: [PATCH 2/5] fixing indentation
---
Utility/ARM/Remove-ResourceGroups.ps1 | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/Utility/ARM/Remove-ResourceGroups.ps1 b/Utility/ARM/Remove-ResourceGroups.ps1
index 29dcba4..b6bb279 100644
--- a/Utility/ARM/Remove-ResourceGroups.ps1
+++ b/Utility/ARM/Remove-ResourceGroups.ps1
@@ -58,28 +58,28 @@ workflow Remove-ResourceGroups
{
[OutputType([String])]
- param(
- [parameter(Mandatory = $false)]
- [string] $NameFilter,
+ param(
+ [parameter(Mandatory = $false)]
+ [string] $NameFilter,
- [parameter(Mandatory = $false)]
- [bool] $PreviewMode = $true,
+ [parameter(Mandatory = $false)]
+ [bool] $PreviewMode = $true,
[parameter(Mandatory = $false)]
[string] $FromEmailAddress,
[parameter(Mandatory = $false)]
[string] $DestEmailAddress,
-
+
[parameter(Mandatory = $false)]
- [sting] $SendGridToken # To be converted to keyvault
- )
+ [sting] $SendGridToken # To be converted to keyvault
+ )
- $VerbosePreference = 'Continue'
+ $VerbosePreference = 'Continue'
- inlineScript {
- $NameFilter = $using:NameFilter
- $PreviewMode = $using:PreviewMode
+ inlineScript {
+ $NameFilter = $using:NameFilter
+ $PreviewMode = $using:PreviewMode
$PSPrivateMetadata = $using:PSPrivateMetadata
$FromEmailAddress = $using:FromEmailAddress
From c046763bbbc6a51ee1a8ffed09a73ed466ecbb66 Mon Sep 17 00:00:00 2001
From: Jovana Taylor
Date: Fri, 5 Jun 2020 15:08:56 -0700
Subject: [PATCH 3/5] fixing email logic
---
Utility/ARM/Remove-ResourceGroups.ps1 | 400 +++++++++++++-------------
1 file changed, 204 insertions(+), 196 deletions(-)
diff --git a/Utility/ARM/Remove-ResourceGroups.ps1 b/Utility/ARM/Remove-ResourceGroups.ps1
index b6bb279..10c0f49 100644
--- a/Utility/ARM/Remove-ResourceGroups.ps1
+++ b/Utility/ARM/Remove-ResourceGroups.ps1
@@ -30,6 +30,7 @@
#>
#Requires -Module Azure
+#Requires -Module AzureRM.KeyVault
#Requires -Module AzureRM.Profile
#Requires -Module AzureRM.Resources
@@ -54,222 +55,229 @@
#>
-workflow Remove-ResourceGroups
-{
- [OutputType([String])]
- param(
- [parameter(Mandatory = $false)]
- [string] $NameFilter,
+param(
+ [parameter(Mandatory = $false)]
+ [string] $NameFilter,
- [parameter(Mandatory = $false)]
- [bool] $PreviewMode = $true,
+ [parameter(Mandatory = $false)]
+ [bool] $PreviewMode = $true,
- [parameter(Mandatory = $false)]
- [string] $FromEmailAddress,
+ [parameter(Mandatory = $false)]
+ [string] $FromEmailAddress,
- [parameter(Mandatory = $false)]
- [string] $DestEmailAddress,
+ [parameter(Mandatory = $false)]
+ [string] $DestEmailAddress,
- [parameter(Mandatory = $false)]
- [sting] $SendGridToken # To be converted to keyvault
- )
-
- $VerbosePreference = 'Continue'
-
- inlineScript {
- $NameFilter = $using:NameFilter
- $PreviewMode = $using:PreviewMode
- $PSPrivateMetadata = $using:PSPrivateMetadata
-
- $FromEmailAddress = $using:FromEmailAddress
- $DestEmailAddress = $using:DestEmailAddress
- $SendGridToken = $using:SendGridToken
-
- # Connect to Azure with RunAs account
- $conn = Get-AutomationConnection -Name "AzureRunAsConnection"
- $null = Add-AzureRmAccount `
- -ServicePrincipal `
- -Tenant $conn.TenantId `
- -ApplicationId $conn.ApplicationId `
- -CertificateThumbprint $conn.CertificateThumbprint
-
- # Use the subscription that this Automation account is in
- $null = Select-AzureRmSubscription -SubscriptionId $conn.SubscriptionID
-
- # Parse name filter list
- if ($NameFilter) {
- $nameFilterList = $NameFilter.Split(',')
- [regex]$nameFilterRegex = '(' + (($nameFilterList | foreach {[regex]::escape($_.ToLower())}) –join "|") + ')'
- }
-
- # Find the resource group that this Automation job is running in so that we can protect it from being removed
- if ([string]::IsNullOrEmpty($PSPrivateMetadata.JobId.Guid)) {
- throw ("This is not running from the Automation service, so could not retrieve the resource group for the Automation account in order to protect it from being removed.")
- exit
+ [parameter(Mandatory = $false)]
+ [string] $KeyvaultName # Keyvault that contains "SendGridAPIKey" secret
+)
+
+Import-Module AzureRM.KeyVault
+
+$VerbosePreference = 'Continue'
+
+Write-Output "Connecting to Azure"
+
+# Connect to Azure with RunAs account
+$conn = Get-AutomationConnection -Name "AzureRunAsConnection"
+$null = Login-AzureRmAccount `
+ -ServicePrincipal `
+ -Tenant $conn.TenantId `
+ -ApplicationId $conn.ApplicationId `
+ -CertificateThumbprint $conn.CertificateThumbprint
+
+# Use the subscription that this Automation account is in
+$null = Select-AzureRmSubscription -SubscriptionId $conn.SubscriptionID
+
+# Parse name filter list
+if ($NameFilter) {
+ $nameFilterList = $NameFilter.Split(',')
+ [regex]$nameFilterRegex = '(' + (($nameFilterList | foreach {[regex]::escape($_.ToLower())}) –join "|") + ')'
+}
+
+Write-Output "Determine current resource group"
+
+# Find the resource group that this Automation job is running in so that we can protect it from being removed
+if ([string]::IsNullOrEmpty($PSPrivateMetadata.JobId.Guid)) {
+ throw ("This is not running from the Automation service, so could not retrieve the resource group for the Automation account in order to protect it from being removed.")
+ exit
+}
+else {
+ $resources = Get-AzureRmResource
+ $automationResources = $resources | ? {$_.ResourceType -eq "Microsoft.Automation/automationAccounts"}
+ foreach ($automation in $automationResources) {
+ # Loop through each Automation account to find this job
+ $job = Get-AzureRmAutomationJob -ResourceGroupName $automation.ResourceGroupName -AutomationAccountName $automation.Name -Id $PSPrivateMetadata.JobId.Guid -ErrorAction SilentlyContinue
+ if (!([string]::IsNullOrEmpty($job))) {
+ $thisResourceGroupName = $job.ResourceGroupName
+ break
}
- else {
- $resources = Get-AzureRmResource
- $automationResources = $resources | ? {$_.ResourceType -eq "Microsoft.Automation/automationAccounts"}
- foreach ($automation in $automationResources) {
- # Loop through each Automation account to find this job
- $job = Get-AzureRmAutomationJob -ResourceGroupName $automation.ResourceGroupName -AutomationAccountName $automation.Name -Id $PSPrivateMetadata.JobId.Guid -ErrorAction SilentlyContinue
- if (!([string]::IsNullOrEmpty($job))) {
- $thisResourceGroupName = $job.ResourceGroupName
- return
- }
- }
+ }
+}
+
+Get-Module -Name AzureRM*
+
+# Process the resource groups
+try {
+ # Find RGs to remove based on passed in name filter
+ $resourceGroups = Get-AzureRmResourceGroup | `
+ ? { $nameFilterList.Count -eq 0 -or $_.ResourceGroupName.ToLower() -match $nameFilterRegex }
+ $groupMap = @{}
+ $resourceGroups | % { $groupMap[$_.ResourceGroupName] = $_ }
+
+ # Get the locks on the resources and RGs
+ $locks = Get-AzureRmResourceLock
+ $rLocks = $locks | ? {$_.ExtensionResourceType -eq "Microsoft.Authorization/locks"} # locks on resources
+ $rgLocks = $locks | ? {$_.ResourceType -eq "Microsoft.Authorization/locks"} # locks on RGs
+
+ $resourceGroups | % {
+ $currentRg = $_
+ # Filter out RGs with locks
+ $rgLock = $rgLocks | ? {$_.ResourceGroupName -eq $currentRg.ResourceGroupName}
+ if ($rgLock) {
+ Write-Output "$($currentRg.ResourceGroupName) is locked: $($rgLock.Properties.notes)"
+ $groupMap.Remove($currentRg.ResourceGroupName)
+ return
}
+ # Filter out the RG running this runbook
+ if ($currentRg.ResourceGroupName -eq $thisResourceGroupName) {
+ Write-Output ("The resource group for this runbook job will not be removed. Resource group: $thisResourceGroupName")
+ $groupMap.Remove($currentRg.ResourceGroupName)
+ return
+ }
+ }
- # Process the resource groups
- try {
- # Find RGs to remove based on passed in name filter
- $resourceGroups = Get-AzureRmResourceGroup | `
- ? { $nameFilterList.Count -eq 0 -or $_.ResourceGroupName.ToLower() -match $nameFilterRegex }
- $groupMap = @{}
- $resourceGroups | % { $groupMap[$_.ResourceGroupName] = $_ }
-
- # Get the locks on the resources and RGs
- $locks = Get-AzureRmResourceLock
- $rLocks = $locks | ? {$_.ExtensionResourceType -eq "Microsoft.Authorization/locks"} # locks on resources
- $rgLocks = $locks | ? {$_.ResourceType -eq "Microsoft.Authorization/locks"} # locks on RGs
-
- $resourceGroups | % {
- $currentRg = $_
- # Filter out RGs with locks
- $rgLock = $rgLocks | ? {$_.ResourceGroupName -eq $currentRg.ResourceGroupName}
- if ($rgLock) {
- Write-Output "$($currentRg.ResourceGroupName) is locked: $($rgLock.Properties.notes)"
- $groupMap.Remove($currentRg.ResourceGroupName)
- return
- }
- # Filter out the RG running this runbook
- if ($currentRg.ResourceGroupName -eq $thisResourceGroupName) {
- Write-Output ("The resource group for this runbook job will not be removed. Resource group: $thisResourceGroupName")
- $groupMap.Remove($currentRg.ResourceGroupName)
- return
- }
- }
-
- $resources = Get-AzureRmResource
- $resourcesToRemove = ($resources | ? {$groupMap.ContainsKey($_.ResourceGroupName)})
- $resourcesToRemove | % {
- $currentR = $_
- $rLock = $rLocks | ? {$_.ResourceGroupName -eq $currentR.ResourceGroupName} | ? {$_.ResourceName -eq $currentR.Name }
- if ($rLock) {
- # Filter out RGs that contain locked resources
- Write-Output "$($currentR.ResourceGroupName)/$($currentR.Name) is locked: $($rLock.Properties.notes)"
- $groupMap.Remove($currentR.ResourceGroupName)
- }
- }
+ $resources = Get-AzureRmResource
+ $resourcesToRemove = ($resources | ? {$groupMap.ContainsKey($_.ResourceGroupName)})
+ $resourcesToRemove | % {
+ $currentR = $_
+ $rLock = $rLocks | ? {$_.ResourceGroupName -eq $currentR.ResourceGroupName} | ? {$_.ResourceName -eq $currentR.Name }
+ if ($rLock) {
+ # Filter out RGs that contain locked resources
+ Write-Output "$($currentR.ResourceGroupName)/$($currentR.Name) is locked: $($rLock.Properties.notes)"
+ $groupMap.Remove($currentR.ResourceGroupName)
+ }
+ }
- # The RGs to be removed
- $groupsToRemove = $groupMap.Values
+ # The RGs to be removed
+ $groupsToRemove = $groupMap.Values
- # No matching groups were found to remove
- if ($groupsToRemove.Count -eq 0) {
- Write-Output "No matching resource groups found."
- return
- }
- # Matching groups were found to remove
- else
- {
- # The resources in RGs to be removed
- $resourcesToRemove = ($resources | ? {$groupMap.ContainsKey($_.ResourceGroupName)})
-
- # In preview mode, so report what would be removed, but take no action.
- if ($PreviewMode -eq $true) {
- Write-Output "Preview Mode: The following resource groups would be removed:"
- foreach ($group in $groupsToRemove){
- Write-Output "`t$($group.ResourceGroupName)"
- }
- Write-Output "Preview Mode: The following resources would be removed:"
- $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
- }
- # Remove the resource groups
- else {
- Write-Output "The following resource groups will be removed:"
- foreach ($group in $groupsToRemove){
- Write-Output $($group.ResourceGroupName)
- }
- Write-Output "The following resources will be removed:"
- $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
- # Here is where the remove actions happen
- foreach ($resourceGroup in $groupsToRemove) {
- Write-Output "Starting to remove resource group: $($resourceGroup.ResourceGroupName) ..."
- # Remove-AzureRmResourceGroup -Name $($resourceGroup.ResourceGroupName) -Force
- if ((Get-AzureRmResourceGroup -Name $($resourceGroup.ResourceGroupName) -ErrorAction SilentlyContinue) -eq $null) {
- Write-Output "...successfully removed resource group: $($resourceGroup.ResourceGroupName)"
- }
- }
- }
- Write-Output "Completed."
+ # No matching groups were found to remove
+ if ($groupsToRemove.Count -eq 0) {
+ Write-Output "No matching resource groups found."
+ return
+ }
+ # Matching groups were found to remove
+ else
+ {
+ # The resources in RGs to be removed
+ $resourcesToRemove = ($resources | ? {$groupMap.ContainsKey($_.ResourceGroupName)})
+
+ # In preview mode, so report what would be removed, but take no action.
+ if ($PreviewMode -eq $true) {
+ Write-Output "Preview Mode: The following resource groups would be removed:"
+ foreach ($group in $groupsToRemove){
+ Write-Output "`t$($group.ResourceGroupName)"
}
+ Write-Output "Preview Mode: The following resources would be removed:"
+ $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
}
- catch {
- $errorMessage = $_
- }
- if ($errorMessage) {
- Write-Error $errorMessage
+ # Remove the resource groups
+ else {
+ $resourcesRemoved = @()
+ $resourceRemoveFailed = @()
+ Write-Output "The following resource groups will be removed:"
+ foreach ($group in $groupsToRemove){
+ Write-Output $($group.ResourceGroupName)
+ }
+ Write-Output "The following resources will be removed:"
+ $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
+ # Here is where the remove actions happen
+ foreach ($resourceGroup in $groupsToRemove) {
+ Write-Output "Starting to remove resource group: $($resourceGroup.ResourceGroupName) ..."
+ Remove-AzureRmResourceGroup -Name $($resourceGroup.ResourceGroupName) -Force
+ if ((Get-AzureRmResourceGroup -Name $($resourceGroup.ResourceGroupName) -ErrorAction SilentlyContinue) -eq $null) {
+ $resourcesRemoved += $resourceGroup
+ Write-Output "...successfully removed resource group: $($resourceGroup.ResourceGroupName)"
+ } else {
+ $resourceRemoveFailed += $resourceGroup
+ Write-Output "...failed to remove resource group: $($resourceGroup.ResourceGroupName)"
+ }
+ }
}
-
- # Send email report
- if ($DestEmailAddress -and $FromEmailAddress -and $SendGridKey) {
+ Write-Output "Completed."
+ }
+}
+catch {
+ $errorMessage = $_
+}
+if ($errorMessage) {
+ Write-Error $errorMessage
+}
+
+# Send email report
+if ($DestEmailAddress -and $FromEmailAddress -and $KeyvaultName) {
+ $SendGridKey = (Get-AzureKeyVaultSecret -VaultName $KeyvaultName -Name "SendGridAPIKey").SecretValueText
+
+ $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
+ $headers.Add("Authorization", "Bearer " + $SendGridKey)
+ $headers.Add("Content-Type", "application/json")
- $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
- $headers.Add("Authorization", "Bearer " + $SendGridKey)
- $headers.Add("Content-Type", "application/json")
-
- if ($PreviewMode) {
- $subject = "Azure Resources scheduled for cleanup"
- $content = "Cleanup summary
"
- $content += "Resources scheduled for clean up:
"
- } else {
- $subject = "Azure Resources cleanup report"
- $content = "Cleanup summary
"
- $content += "Resources cleaned up:
"
- }
-
- # TODO: if cleanup occurred, should report on any failures
-
- $resourcesToRemoveTable = $resourcesToRemove | Select-Object Name, ResourceGroupName, ResourceType | ConvertTo-Html -Fragment
- $content += $resourcesToRemoveTable
+ if ($PreviewMode) {
+ $subject = "Azure Resources scheduled for cleanup"
+ $content = "Cleanup summary
"
+ $content += "Resources scheduled for clean up:
"
+ $resourcesToRemoveTable = $resourcesToRemove | Select-Object Name, ResourceGroupName, ResourceType | ConvertTo-Html -Fragment
+ $content += $resourcesToRemoveTable
+ } else {
+ $subject = "Azure Resources cleanup report"
+ $content = "Cleanup summary
"
+ if ($resourcesRemoved) {
+ $content += "Resource groups cleaned up:
"
+ $resourcesRemovedTable = $resourcesRemoved | Select-Object ResourceGroupName | ConvertTo-Html -Fragment
+ $content += $resourcesRemovedTable
+ }
+ if ($resourceRemoveFailed) {
+ $content += "Resource groups with failed clean up:
"
+ $resourceRemoveFailedTable = $resourceRemoveFailed | Select-Object ResourceGroupName | ConvertTo-Html -Fragment
+ $content += $resourceRemoveFailedTable
+ }
+ }
- $content += "Locked Resource Groups:
"
- $content += $rgLocks | Select-Object -ExpandProperty Properties -Property ResourceGroupName, Name | Select-Object ResourceGroupName, Name, notes | ConvertTo-Html -Fragment
+ $content += "Locked Resource Groups:
"
+ $content += $rgLocks | Select-Object -ExpandProperty Properties -Property ResourceGroupName, Name | Select-Object ResourceGroupName, Name, notes | ConvertTo-Html -Fragment
- $content += "Locked resources:
"
- $content += $rLocks | Select-Object -ExpandProperty Properties -Property ResourceName, ResourceGroupName, Name | Select-Object ResourceName, ResourceGroupName, Name, notes | ConvertTo-Html -Fragment
+ $content += "Locked resources:
"
+ $content += $rLocks | Select-Object -ExpandProperty Properties -Property ResourceName, ResourceGroupName, Name | Select-Object ResourceName, ResourceGroupName, Name, notes | ConvertTo-Html -Fragment
- $content += ""
+ $content += ""
- $content = $content -join [Environment]::NewLine
+ $content = $content -join [Environment]::NewLine
- $body = @{
- personalizations = @(
- @{
- to = @(
- @{
- email = $DestEmailAddress
- }
- )
- }
+ $body = @{
+ personalizations = @(
+ @{
+ to = @(
+ @{
+ email = $DestEmailAddress
+ }
)
- from = @{
- email = $FromEmailAddress
- }
- subject = $subject
- content = @(
- @{
- type = "text/html"
- value = $content
- }
- )
- }
-
- $bodyJson = $body | ConvertTo-Json -Depth 8
-
- $response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -Body $bodyJson
}
+ )
+ from = @{
+ email = $FromEmailAddress
}
+ subject = $subject
+ content = @(
+ @{
+ type = "text/html"
+ value = $content
+ }
+ )
+ }
+
+ $bodyJson = $body | ConvertTo-Json -Depth 8
+
+ $response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -Body $bodyJson
}
\ No newline at end of file
From 303a80cfda936a5478ab48dcb42f39682f14c701 Mon Sep 17 00:00:00 2001
From: Jovana Taylor
Date: Tue, 9 Jun 2020 15:29:39 -0700
Subject: [PATCH 4/5] remove unused code
---
Utility/ARM/Remove-ResourceGroups.ps1 | 2 --
1 file changed, 2 deletions(-)
diff --git a/Utility/ARM/Remove-ResourceGroups.ps1 b/Utility/ARM/Remove-ResourceGroups.ps1
index 10c0f49..9ab66fb 100644
--- a/Utility/ARM/Remove-ResourceGroups.ps1
+++ b/Utility/ARM/Remove-ResourceGroups.ps1
@@ -116,8 +116,6 @@ else {
}
}
-Get-Module -Name AzureRM*
-
# Process the resource groups
try {
# Find RGs to remove based on passed in name filter
From 11e58bf8ed489531e0f445794cdcc42507302b57 Mon Sep 17 00:00:00 2001
From: Jovana Taylor
Date: Wed, 17 Jun 2020 14:08:03 -0700
Subject: [PATCH 5/5] Adding comments
---
Utility/ARM/Remove-ResourceGroups.ps1 | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/Utility/ARM/Remove-ResourceGroups.ps1 b/Utility/ARM/Remove-ResourceGroups.ps1
index 9ab66fb..14f21e6 100644
--- a/Utility/ARM/Remove-ResourceGroups.ps1
+++ b/Utility/ARM/Remove-ResourceGroups.ps1
@@ -53,6 +53,21 @@
Optional with default of $true.
Execute the runbook to see which resource groups would be deleted but take no action.
+.PARAMETER FromEmailAddress
+ Optional
+ Specify the sender address for the email notification
+ Requires DestEmailAddress and KeyvaultName to be set
+
+.PARAMETER DestEmailAddress
+ Optional
+ Specify the recipient address for the email notification
+ Requires FromEmailAddress and KeyvaultName to be set
+
+.PARAMETER KeyvaultName
+ Optional
+ Specify the name of a KeyVault that contains the "SendGridAPIKey" secret
+ The Run As Automation Account must have read access to the secrets in the KeyVault
+ Requires FromEmailAddress and DestEmailAddress to be set
#>
@@ -70,7 +85,7 @@ param(
[string] $DestEmailAddress,
[parameter(Mandatory = $false)]
- [string] $KeyvaultName # Keyvault that contains "SendGridAPIKey" secret
+ [string] $KeyvaultName
)
Import-Module AzureRM.KeyVault
@@ -179,7 +194,7 @@ try {
Write-Output "`t$($group.ResourceGroupName)"
}
Write-Output "Preview Mode: The following resources would be removed:"
- $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
+ $resourcesToRemove | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
}
# Remove the resource groups
else {
@@ -190,7 +205,7 @@ try {
Write-Output $($group.ResourceGroupName)
}
Write-Output "The following resources will be removed:"
- $resources | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
+ $resourcesToRemove | % { "`t$($_.ResourceGroupName)/$($_.Name)" }
# Here is where the remove actions happen
foreach ($resourceGroup in $groupsToRemove) {
Write-Output "Starting to remove resource group: $($resourceGroup.ResourceGroupName) ..."