The previous property was calculated so didn't exist from the REST-based call. Also updated the api-version to the latest stable control plane version. Co-authored-by: Heath Stewart <heaths@microsoft.com>
207 lines
8.2 KiB
PowerShell
207 lines
8.2 KiB
PowerShell
# Add 'AzsdkResourceType' member to outputs since actual output types have changed over the years.
|
|
|
|
#Requires -Modules @{ModuleName='Az.KeyVault'; ModuleVersion='3.4.1'}
|
|
|
|
function Get-PurgeableGroupResources {
|
|
param (
|
|
[Parameter(Mandatory=$true, Position=0)]
|
|
[string] $ResourceGroupName
|
|
)
|
|
|
|
$purgeableResources = @()
|
|
|
|
# Discover Managed HSMs first since they are a premium resource.
|
|
Write-Verbose "Retrieving deleted Managed HSMs from resource group $ResourceGroupName"
|
|
|
|
# Get any Managed HSMs in the resource group, for which soft delete cannot be disabled.
|
|
$deletedHsms = @(Get-AzKeyVaultManagedHsm -ResourceGroupName $ResourceGroupName -ErrorAction Ignore `
|
|
| Add-Member -MemberType NoteProperty -Name AzsdkResourceType -Value 'Managed HSM' -PassThru `
|
|
| Add-Member -MemberType AliasProperty -Name AzsdkName -Value VaultName -PassThru)
|
|
|
|
if ($deletedHsms) {
|
|
Write-Verbose "Found $($deletedHsms.Count) deleted Managed HSMs to potentially purge."
|
|
$purgeableResources += $deletedHsms
|
|
}
|
|
|
|
Write-Verbose "Retrieving deleted Key Vaults from resource group $ResourceGroupName"
|
|
|
|
# Get any Key Vaults that will be deleted so they can be purged later if soft delete is enabled.
|
|
$deletedKeyVaults = @(Get-AzKeyVault -ResourceGroupName $ResourceGroupName -ErrorAction Ignore | ForEach-Object {
|
|
# Enumerating vaults from a resource group does not return all properties we required.
|
|
Get-AzKeyVault -VaultName $_.VaultName -ErrorAction Ignore | Where-Object { $_.EnableSoftDelete } `
|
|
| Add-Member -MemberType NoteProperty -Name AzsdkResourceType -Value 'Key Vault' -PassThru `
|
|
| Add-Member -MemberType AliasProperty -Name AzsdkName -Value VaultName -PassThru
|
|
})
|
|
|
|
if ($deletedKeyVaults) {
|
|
Write-Verbose "Found $($deletedKeyVaults.Count) deleted Key Vaults to potentially purge."
|
|
$purgeableResources += $deletedKeyVaults
|
|
}
|
|
|
|
return $purgeableResources
|
|
}
|
|
|
|
function Get-PurgeableResources {
|
|
$purgeableResources = @()
|
|
$subscriptionId = (Get-AzContext).Subscription.Id
|
|
|
|
# Discover Managed HSMs first since they are a premium resource.
|
|
Write-Verbose "Retrieving deleted Managed HSMs from subscription $subscriptionId"
|
|
|
|
# Get deleted Managed HSMs for the current subscription.
|
|
$response = Invoke-AzRestMethod -Method GET -Path "/subscriptions/$subscriptionId/providers/Microsoft.KeyVault/deletedManagedHSMs?api-version=2023-02-01" -ErrorAction Ignore
|
|
if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 300 -and $response.Content) {
|
|
$content = $response.Content | ConvertFrom-Json
|
|
|
|
$deletedHsms = @()
|
|
foreach ($r in $content.value) {
|
|
$deletedHsms += [pscustomobject] @{
|
|
AzsdkResourceType = 'Managed HSM'
|
|
AzsdkName = $r.name
|
|
Id = $r.id
|
|
Name = $r.name
|
|
Location = $r.properties.location
|
|
DeletionDate = $r.properties.deletionDate -as [DateTime]
|
|
ScheduledPurgeDate = $r.properties.scheduledPurgeDate -as [DateTime]
|
|
EnablePurgeProtection = $r.properties.purgeProtectionEnabled
|
|
}
|
|
}
|
|
|
|
if ($deletedHsms) {
|
|
Write-Verbose "Found $($deletedHsms.Count) deleted Managed HSMs to potentially purge."
|
|
$purgeableResources += $deletedHsms
|
|
}
|
|
}
|
|
|
|
Write-Verbose "Retrieving deleted Key Vaults from subscription $subscriptionId"
|
|
|
|
# TODO: Remove try/catch handler for Get-AzKeyVault - https://github.com/Azure/azure-sdk-tools/issues/5315
|
|
# This is a temporary workaround since Az module >= 9.2.0 uses a more recent API
|
|
# version than is supported in the dogfood cloud environment:
|
|
#
|
|
# | The resource type 'deletedVaults' could not be found in the namespace 'Microsoft.KeyVault' for api version '2022-07-01'. The supported api-versions are
|
|
# | '2016-10-01,2018-02-14-preview,2018-02-14,2019-09-01,2021-04-01-preview,2021-06-01-preview,2021-10-01,2021-11-01-preview'.
|
|
try {
|
|
# Get deleted Key Vaults for the current subscription.
|
|
$deletedKeyVaults = @(Get-AzKeyVault -InRemovedState `
|
|
| Add-Member -MemberType NoteProperty -Name AzsdkResourceType -Value 'Key Vault' -PassThru `
|
|
| Add-Member -MemberType AliasProperty -Name AzsdkName -Value VaultName -PassThru)
|
|
|
|
if ($deletedKeyVaults) {
|
|
Write-Verbose "Found $($deletedKeyVaults.Count) deleted Key Vaults to potentially purge."
|
|
$purgeableResources += $deletedKeyVaults
|
|
}
|
|
} catch { }
|
|
|
|
return $purgeableResources
|
|
}
|
|
|
|
# A filter differs from a function by teating body as -process {} instead of -end {}.
|
|
# This allows you to pipe a collection and process each item in the collection.
|
|
filter Remove-PurgeableResources {
|
|
param (
|
|
[Parameter(Position=0, ValueFromPipeline=$true)]
|
|
[object[]] $Resource,
|
|
|
|
[Parameter()]
|
|
[ValidateRange(1, [int]::MaxValue)]
|
|
[int] $Timeout = 30,
|
|
|
|
[Parameter()]
|
|
[switch] $PassThru
|
|
)
|
|
|
|
if (!$Resource) {
|
|
return
|
|
}
|
|
|
|
$subscriptionId = (Get-AzContext).Subscription.Id
|
|
|
|
foreach ($r in $Resource) {
|
|
Log "Attempting to purge $($r.AzsdkResourceType) '$($r.AzsdkName)'"
|
|
switch ($r.AzsdkResourceType) {
|
|
'Key Vault' {
|
|
if ($r.EnablePurgeProtection) {
|
|
# We will try anyway but will ignore errors.
|
|
Write-Warning "Key Vault '$($r.VaultName)' has purge protection enabled and may not be purged until $($r.ScheduledPurgeDate)"
|
|
}
|
|
|
|
# Use `-AsJob` to start a lightweight, cancellable job and pass to `Wait-PurgeableResoruceJob` for consistent behavior.
|
|
Remove-AzKeyVault -VaultName $r.VaultName -Location $r.Location -InRemovedState -Force -ErrorAction Continue -AsJob `
|
|
| Wait-PurgeableResourceJob -Resource $r -Timeout $Timeout -PassThru:$PassThru
|
|
}
|
|
|
|
'Managed HSM' {
|
|
if ($r.EnablePurgeProtection) {
|
|
# We will try anyway but will ignore errors.
|
|
Write-Warning "Managed HSM '$($r.Name)' has purge protection enabled and may not be purged until $($r.ScheduledPurgeDate)"
|
|
}
|
|
|
|
# Use `GetNewClosure()` on the `-Action` ScriptBlock to make sure variables are captured.
|
|
Invoke-AzRestMethod -Method POST -Path "/subscriptions/$subscriptionId/providers/Microsoft.KeyVault/locations/$($r.Location)/deletedManagedHSMs/$($r.Name)/purge?api-version=2023-02-01" -ErrorAction Ignore -AsJob `
|
|
| Wait-PurgeableResourceJob -Resource $r -Timeout $Timeout -PassThru:$PassThru -Action {
|
|
param ( $response )
|
|
if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 300) {
|
|
Write-Warning "Successfully requested that Managed HSM '$($r.Name)' be purged, but may take a few minutes before it is actually purged."
|
|
} elseif ($response.Content) {
|
|
$content = $response.Content | ConvertFrom-Json
|
|
if ($content.error) {
|
|
$err = $content.error
|
|
Write-Warning "Failed to deleted Managed HSM '$($r.Name)': ($($err.code)) $($err.message)"
|
|
}
|
|
}
|
|
}.GetNewClosure()
|
|
}
|
|
|
|
default {
|
|
Write-Warning "Cannot purge $($r.AzsdkResourceType) '$($r.AzsdkName)'. Add support to https://github.com/Azure/azure-sdk-tools/blob/main/eng/common/scripts/Helpers/Resource-Helpers.ps1."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# The Log function can be overridden by the sourcing script.
|
|
function Log($Message) {
|
|
Write-Host ('{0} - {1}' -f [DateTime]::Now.ToLongTimeString(), $Message)
|
|
}
|
|
|
|
function Wait-PurgeableResourceJob {
|
|
param (
|
|
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
|
|
$Job,
|
|
|
|
# The resource is used for logging and to return if `-PassThru` is specified
|
|
# so we can easily see all resources that may be in a bad state when the script has completed.
|
|
[Parameter(Mandatory=$true)]
|
|
$Resource,
|
|
|
|
# Optional ScriptBlock should define params corresponding to the associated job's `Output` property.
|
|
[Parameter()]
|
|
[scriptblock] $Action,
|
|
|
|
[Parameter()]
|
|
[ValidateRange(1, [int]::MaxValue)]
|
|
[int] $Timeout = 30,
|
|
|
|
[Parameter()]
|
|
[switch] $PassThru
|
|
)
|
|
|
|
$null = Wait-Job -Job $Job -Timeout $Timeout
|
|
|
|
if ($Job.State -eq 'Completed' -or $Job.State -eq 'Failed') {
|
|
$result = Receive-Job -Job $Job -ErrorAction Continue
|
|
|
|
if ($Action) {
|
|
$null = $Action.Invoke($result)
|
|
}
|
|
} else {
|
|
Write-Warning "Timed out waiting to purge $($Resource.AzsdkResourceType) '$($Resource.AzsdkName)'. Cancelling job."
|
|
$Job.Cancel()
|
|
|
|
if ($PassThru) {
|
|
$Resource
|
|
}
|
|
}
|
|
}
|