Skip to content

Commit

Permalink
[Modules] Fix/Add purge during removal of machine learning workspace (#…
Browse files Browse the repository at this point in the history
…3597)

* Update custom publishing resources

* Add script for purging machine learning workspace

* reset settings

* remove double space

* fix attempt #1

* correct spelling of retry

* align with Invoke-ResourceRemoval.ps1

* fix

* Reset files

* Update utilities/pipelines/resourceRemoval/helper/Remove-ResourceList.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceRemoval.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Restructure

* added break again

* align outputs/logs

* Moved out of default to before switch

* Apply suggestions from code review

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceRemoval.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceRemoval.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceRemoval.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceRemoval.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Remove DevTestLab as logic is covered with regular flow now

* Change logging and removal loop slightly

* Fixing logic

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceRemoval.ps1

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceLockRemoval.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

* Update utilities/pipelines/resourceRemoval/helper/Invoke-ResourceLockRemoval.ps1

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>

---------

Co-authored-by: Alexander Sehr <ASehr@hotmail.de>
  • Loading branch information
MariusStorhaug and AlexanderSehr authored Sep 4, 2023
1 parent f27f09f commit 2bd0601
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ jobs:
@{ Name = 'Az.CognitiveServices' },
@{ Name = 'Az.Compute' },
@{ Name = 'Az.KeyVault' },
@{ Name = 'Az.MachineLearningServices' },
@{ Name = 'Az.Monitor' },
@{ Name = 'Az.OperationalInsights' },
@{ Name = 'Az.RecoveryServices' }
Expand Down
1 change: 1 addition & 0 deletions .github/actions/templates/setEnvironment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ runs:
@{ Name = 'Az.CognitiveServices' },
@{ Name = 'Az.Compute' },
@{ Name = 'Az.KeyVault' },
@{ Name = 'Az.MachineLearningServices' },
@{ Name = 'Az.Monitor' },
@{ Name = 'Az.OperationalInsights' },
@{ Name = 'Az.RecoveryServices' }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<#
.SYNOPSIS
Remove resource locks from a resource or a specific resource lock.
.DESCRIPTION
Remove resource locks from a resource or a specific resource lock.
.PARAMETER ResourceId
Mandatory. The resourceID of the resource to check, and remove a resource lock from.
.PARAMETER Type
Optional. The type of the resource. If the resource is a lock, the lock itself will be removed. If the resource is a resource, all locks on the resource will be removed. If not specified, the resource will be checked for locks, and if any are found, all locks will be removed.
.PARAMETER RetryLimit
Optional. The number of times to retry checking if the lock is removed.
.PARAMETER RetryInterval
Optional. The number of seconds to wait between each retry.
.EXAMPLE
Invoke-ResourceLockRemoval -ResourceId '/subscriptions/.../resourceGroups/validation-rg/.../resource-name'
Check if the resource 'resource-name' is locked. If it is, remove the lock.
#>
function Invoke-ResourceLockRemoval {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $true)]
[string] $ResourceId,

[Parameter(Mandatory = $false)]
[string] $Type,

[Parameter(Mandatory = $false)]
[int] $RetryLimit = 10,

[Parameter(Mandatory = $false)]
[int] $RetryInterval = 10
)
# Load functions
. (Join-Path $PSScriptRoot 'Invoke-ResourceLockRetrieval.ps1')

$resourceLock = Invoke-ResourceLockRetrieval -ResourceId $ResourceId -Type $Type

$isLocked = $resourceLock.count -gt 0
if (-not $isLocked) {
return
}

$resourceLock | ForEach-Object {
Write-Warning (' [-] Removing lock [{0}] on [{1}] of type [{2}].' -f $_.Name, $_.ResourceName, $_.ResourceType)
if ($PSCmdlet.ShouldProcess(('Lock [{0}] on resource [{1}] of type [{2}].' -f $_.Name, $_.ResourceName, $_.ResourceType ), 'Remove')) {
$null = $_ | Remove-AzResourceLock -Force
}
}

$retryCount = 0
do {
$retryCount++
if ($retryCount -ge $RetryLimit) {
Write-Warning (' [!] Lock was not removed after {1} seconds. Continuing with resource removal.' -f ($retryCount * $RetryInterval))
break
}
Write-Verbose ' [⏱️] Waiting for lock to be removed.' -Verbose
Start-Sleep -Seconds $RetryInterval

# Rechecking the resource locks to see if they have been removed.
$resourceLock = Invoke-ResourceLockRetrieval -ResourceId $ResourceId -Type $Type
$isLocked = $resourceLock.count -gt 0
} while ($isLocked)

Write-Verbose (' [-] [{0}] resource lock(s) removed.' -f $resourceLock.count) -Verbose
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<#
.SYNOPSIS
Gets resource locks on a resource or a specific resource lock.
.DESCRIPTION
Gets resource locks on a resource or a specific resource lock.
.PARAMETER ResourceId
Mandatory. The resourceID of the resource to check or the resource lock to check.
.PARAMETER Type
Optional. The type of the resource.
If the resource is a lock, the lock itself will be returned.
If the resource is not a lock, all locks on the resource will be returned.
.EXAMPLE
Invoke-ResourceLockRetrieval -ResourceId '/subscriptions/.../resourceGroups/validation-rg/.../resource-name'
Check if the resource 'resource-name' is locked. If it is, return the lock.
.EXAMPLE
Invoke-ResourceLockRetrieval -ResourceId '/subscriptions/.../resourceGroups/validation-rg/.../resource-name/providers/Microsoft.Authorization/locks/lock-name' -Type 'Microsoft.Authorization/locks'
Return the lock 'lock-name' on the resource 'resource-name'.
.NOTES
Needed as the AzPwsh cmdlet Get-AzResourceLock does not support getting a specific lock by LockId.
#>
function Invoke-ResourceLockRetrieval {
[OutputType([System.Management.Automation.PSCustomObject])]
param (
[Parameter(Mandatory = $true)]
[string] $ResourceId,

[Parameter(Mandatory = $false)]
[string] $Type = ''
)
if ($Type -eq 'Microsoft.Authorization/locks') {
$lockName = ($ResourceId -split '/')[-1]
$lockScope = ($ResourceId -split '/providers/Microsoft.Authorization/locks')[0]
return Get-AzResourceLock -LockName $lockName -Scope $lockScope -ErrorAction SilentlyContinue
} else {
return Get-AzResourceLock -Scope $ResourceId -ErrorAction SilentlyContinue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ function Invoke-ResourcePostRemoval {
[string] $Type
)

switch ($type) {
switch ($Type) {
'Microsoft.AppConfiguration/configurationStores' {
$subscriptionId = $resourceId.Split('/')[2]
$subscriptionId = $ResourceId.Split('/')[2]
$resourceName = Split-Path $ResourceId -Leaf

# Fetch service in soft-delete
Expand All @@ -38,7 +38,7 @@ function Invoke-ResourcePostRemoval {
Method = 'GET'
Path = $getPath
}
$softDeletedConfigurationStore = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.configurationStoreId -eq $resourceId }
$softDeletedConfigurationStore = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.configurationStoreId -eq $ResourceId }

if ($softDeletedConfigurationStore) {
# Purge service
Expand All @@ -47,6 +47,7 @@ function Invoke-ResourcePostRemoval {
Method = 'POST'
Path = $purgePath
}
Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose
if ($PSCmdlet.ShouldProcess(('App Configuration Store with ID [{0}]' -f $softDeletedConfigurationStore.properties.configurationStoreId), 'Purge')) {
$response = Invoke-AzRestMethod @purgeRequestInputObject
if ($response.StatusCode -ne 200) {
Expand All @@ -61,7 +62,7 @@ function Invoke-ResourcePostRemoval {

$matchingKeyVault = Get-AzKeyVault -InRemovedState | Where-Object { $_.resourceId -eq $ResourceId }
if ($matchingKeyVault -and -not $matchingKeyVault.EnablePurgeProtection) {
Write-Verbose ("Purging key vault [$resourceName]") -Verbose
Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose
if ($PSCmdlet.ShouldProcess(('Key Vault with ID [{0}]' -f $matchingKeyVault.Id), 'Purge')) {
try {
$null = Remove-AzKeyVault -ResourceId $matchingKeyVault.Id -InRemovedState -Force -Location $matchingKeyVault.Location -ErrorAction 'Stop'
Expand All @@ -77,19 +78,20 @@ function Invoke-ResourcePostRemoval {
break
}
'Microsoft.CognitiveServices/accounts' {
$resourceGroupName = $resourceId.Split('/')[4]
$resourceGroupName = $ResourceId.Split('/')[4]
$resourceName = Split-Path $ResourceId -Leaf

$matchingAccount = Get-AzCognitiveServicesAccount -InRemovedState | Where-Object { $_.AccountName -eq $resourceName }
if ($matchingAccount) {
Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose
if ($PSCmdlet.ShouldProcess(('Cognitive services account with ID [{0}]' -f $matchingAccount.Id), 'Purge')) {
$null = Remove-AzCognitiveServicesAccount -InRemovedState -Force -Location $matchingAccount.Location -ResourceGroupName $resourceGroupName -Name $matchingAccount.AccountName
}
}
break
}
'Microsoft.ApiManagement/service' {
$subscriptionId = $resourceId.Split('/')[2]
$subscriptionId = $ResourceId.Split('/')[2]
$resourceName = Split-Path $ResourceId -Leaf

# Fetch service in soft-delete
Expand All @@ -98,7 +100,7 @@ function Invoke-ResourcePostRemoval {
Method = 'GET'
Path = $getPath
}
$softDeletedService = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.serviceId -eq $resourceId }
$softDeletedService = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.serviceId -eq $ResourceId }

if ($softDeletedService) {
# Purge service
Expand All @@ -107,6 +109,7 @@ function Invoke-ResourcePostRemoval {
Method = 'DELETE'
Path = $purgePath
}
Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose
if ($PSCmdlet.ShouldProcess(('API management service with ID [{0}]' -f $softDeletedService.properties.serviceId), 'Purge')) {
$null = Invoke-AzRestMethod @purgeRequestInputObject
}
Expand All @@ -116,7 +119,7 @@ function Invoke-ResourcePostRemoval {
'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems' {
# Remove protected VM
# Required if e.g. a VM was listed in an RSV and only that VM is removed
$vaultId = $resourceId.split('/backupFabrics/')[0]
$vaultId = $ResourceId.split('/backupFabrics/')[0]
$resourceName = Split-Path $ResourceId -Leaf
$softDeleteStatus = (Get-AzRecoveryServicesVaultProperty -VaultId $vaultId).SoftDeleteFeatureState
if ($softDeleteStatus -ne 'Disabled') {
Expand All @@ -132,7 +135,7 @@ function Invoke-ResourcePostRemoval {
Name = $resourceName
}
if ($backupItem = Get-AzRecoveryServicesBackupItem @backupItemInputObject -ErrorAction 'SilentlyContinue') {
Write-Verbose ('Removing Backup item [{0}] from RSV [{1}]' -f $backupItem.Name, $vaultId) -Verbose
Write-Verbose (' [-] Removing Backup item [{0}] from RSV [{1}]' -f $backupItem.Name, $vaultId) -Verbose

if ($backupItem.DeleteState -eq 'ToBeDeleted') {
if ($PSCmdlet.ShouldProcess('Soft-deleted backup data removal', 'Undo')) {
Expand Down
Loading

0 comments on commit 2bd0601

Please sign in to comment.