Skip to content

Commit 8d4c2b9

Browse files
committed
feat: remove custom role definitions
1 parent 4582fd4 commit 8d4c2b9

1 file changed

Lines changed: 171 additions & 7 deletions

File tree

src/ALZ/Public/Remove-PlatformLandingZone.ps1

Lines changed: 171 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ function Remove-PlatformLandingZone {
1717
6. Deletes management groups in reverse depth order (children before parents)
1818
7. Deletes management group-level deployments from target management groups (if not being deleted)
1919
8. Deletes orphaned role assignments from target management groups (if not being deleted)
20-
9. Deletes all resource groups in the discovered/specified subscriptions (excluding retention patterns)
21-
10. Resets Microsoft Defender for Cloud plans to Free tier
22-
11. Deletes all subscription-level deployments
23-
12. Deletes orphaned role assignments from subscriptions
20+
9. Deletes custom role assignments and definitions from target management groups (if not being deleted)
21+
10. Deletes all resource groups in the discovered/specified subscriptions (excluding retention patterns)
22+
11. Resets Microsoft Defender for Cloud plans to Free tier
23+
12. Deletes all subscription-level deployments
24+
13. Deletes orphaned role assignments from subscriptions
2425
2526
CRITICAL WARNING: This is a highly destructive operation that will permanently delete Azure resources.
2627
By default, ALL resource groups in the subscriptions will be deleted unless they match retention patterns.
@@ -523,6 +524,131 @@ function Remove-PlatformLandingZone {
523524
}
524525
}
525526

527+
function Remove-CustomRoleDefinitionsForScope {
528+
[CmdletBinding(SupportsShouldProcess = $true)]
529+
param (
530+
[string]$ManagementGroupId,
531+
[string]$ManagementGroupDisplayName,
532+
[array]$Subscriptions,
533+
[int]$ThrottleLimit,
534+
[switch]$PlanMode
535+
)
536+
537+
if(-not $PSCmdlet.ShouldProcess("Delete Custom Role Definitions", "delete")) {
538+
return
539+
}
540+
541+
$funcWriteToConsoleLog = ${function:Write-ToConsoleLog}.ToString()
542+
543+
Write-ToConsoleLog "Checking for custom role definitions on management group: $ManagementGroupId ($ManagementGroupDisplayName)" -NoNewLine
544+
545+
# Get all custom role definitions scoped to this management group
546+
$customRoleDefinitions = (az role definition list --custom-role-only true --scope "/providers/Microsoft.Management/managementGroups/$ManagementGroupId" --query "[].{name:name,roleName:roleName,id:id,assignableScopes:assignableScopes}" -o json) | ConvertFrom-Json
547+
548+
$customRoleDefinitions = $customRoleDefinitions | Where-Object {
549+
$_.assignableScopes -contains "/providers/Microsoft.Management/managementGroups/$ManagementGroupId"
550+
}
551+
552+
if (-not $customRoleDefinitions -or $customRoleDefinitions.Count -eq 0) {
553+
Write-ToConsoleLog "No custom role definitions found on management group: $ManagementGroupId ($ManagementGroupDisplayName), skipping." -NoNewLine
554+
return
555+
}
556+
557+
Write-ToConsoleLog "Found $($customRoleDefinitions.Count) custom role definition(s) on management group: $ManagementGroupId ($ManagementGroupDisplayName)" -NoNewLine
558+
559+
# For each custom role definition, find and delete all assignments first
560+
foreach ($roleDefinition in $customRoleDefinitions) {
561+
Write-ToConsoleLog "Processing custom role definition: $($roleDefinition.roleName) (ID: $($roleDefinition.name))" -NoNewLine
562+
563+
# Find all role assignments for this custom role on the management group
564+
Write-ToConsoleLog "Checking for role assignments of custom role '$($roleDefinition.roleName)' on management group: $ManagementGroupId ($ManagementGroupDisplayName)" -NoNewLine
565+
$mgRoleAssignments = (az role assignment list --role $roleDefinition.roleName --scope "/providers/Microsoft.Management/managementGroups/$ManagementGroupId" --query "[].{id:id,principalName:principalName,principalId:principalId}" -o json) | ConvertFrom-Json
566+
567+
if ($mgRoleAssignments -and $mgRoleAssignments.Count -gt 0) {
568+
Write-ToConsoleLog "Found $($mgRoleAssignments.Count) role assignment(s) of custom role '$($roleDefinition.roleName)' on management group: $ManagementGroupId ($ManagementGroupDisplayName)" -NoNewLine
569+
570+
$mgRoleAssignments | ForEach-Object -Parallel {
571+
$assignment = $_
572+
$roleDefinitionName = $using:roleDefinition.roleName
573+
$managementGroupId = $using:ManagementGroupId
574+
$managementGroupDisplayName = $using:ManagementGroupDisplayName
575+
$funcWriteToConsoleLog = $using:funcWriteToConsoleLog
576+
${function:Write-ToConsoleLog} = $funcWriteToConsoleLog
577+
578+
Write-ToConsoleLog "Deleting role assignment of custom role '$roleDefinitionName' for principal: $($assignment.principalName) ($($assignment.principalId)) from management group: $managementGroupId ($managementGroupDisplayName)" -NoNewLine
579+
580+
if($using:PlanMode) {
581+
Write-ToConsoleLog "(Plan Mode) Would run: az role assignment delete --ids $($assignment.id)" -NoNewLine -Color Gray
582+
} else {
583+
$result = az role assignment delete --ids $assignment.id 2>&1
584+
if (!$result) {
585+
Write-ToConsoleLog "Deleted role assignment of custom role '$roleDefinitionName' from management group: $managementGroupId ($managementGroupDisplayName)" -NoNewLine
586+
} else {
587+
Write-ToConsoleLog "Failed to delete role assignment of custom role '$roleDefinitionName' from management group: $managementGroupId ($managementGroupDisplayName)" -IsWarning -NoNewLine
588+
}
589+
}
590+
} -ThrottleLimit $using:ThrottleLimit
591+
} else {
592+
Write-ToConsoleLog "No role assignments found for custom role '$($roleDefinition.roleName)' on management group: $ManagementGroupId ($ManagementGroupDisplayName)" -NoNewLine
593+
}
594+
595+
# Find all role assignments for this custom role on subscriptions under the management group
596+
if ($Subscriptions -and $Subscriptions.Count -gt 0) {
597+
Write-ToConsoleLog "Checking for role assignments of custom role '$($roleDefinition.roleName)' on subscriptions under management group: $ManagementGroupId ($ManagementGroupDisplayName)" -NoNewLine
598+
599+
$Subscriptions | ForEach-Object -Parallel {
600+
$subscription = $_
601+
$roleDefinition = $using:roleDefinition
602+
$managementGroupId = $using:ManagementGroupId
603+
$managementGroupDisplayName = $using:ManagementGroupDisplayName
604+
$funcWriteToConsoleLog = $using:funcWriteToConsoleLog
605+
${function:Write-ToConsoleLog} = $funcWriteToConsoleLog
606+
607+
Write-ToConsoleLog "Checking for role assignments of custom role '$($roleDefinition.roleName)' on subscription: $($subscription.Name) (ID: $($subscription.Id))" -NoNewLine
608+
609+
$subRoleAssignments = (az role assignment list --role $roleDefinition.roleName --subscription $subscription.Id --query "[].{id:id,principalName:principalName,principalId:principalId}" -o json) | ConvertFrom-Json
610+
611+
if ($subRoleAssignments -and $subRoleAssignments.Count -gt 0) {
612+
Write-ToConsoleLog "Found $($subRoleAssignments.Count) role assignment(s) of custom role '$($roleDefinition.roleName)' on subscription: $($subscription.Name) (ID: $($subscription.Id))" -NoNewLine
613+
614+
foreach ($assignment in $subRoleAssignments) {
615+
Write-ToConsoleLog "Deleting role assignment of custom role '$($roleDefinition.roleName)' for principal: $($assignment.principalName) ($($assignment.principalId)) from subscription: $($subscription.Name) (ID: $($subscription.Id))" -NoNewLine
616+
617+
if($using:PlanMode) {
618+
Write-ToConsoleLog "(Plan Mode) Would run: az role assignment delete --ids $($assignment.id)" -NoNewLine -Color Gray
619+
} else {
620+
$result = az role assignment delete --ids $assignment.id 2>&1
621+
if (!$result) {
622+
Write-ToConsoleLog "Deleted role assignment of custom role '$($roleDefinition.roleName)' from subscription: $($subscription.Name) (ID: $($subscription.Id))" -NoNewLine
623+
} else {
624+
Write-ToConsoleLog "Failed to delete role assignment of custom role '$($roleDefinition.roleName)' from subscription: $($subscription.Name) (ID: $($subscription.Id))" -IsWarning -NoNewLine
625+
}
626+
}
627+
}
628+
} else {
629+
Write-ToConsoleLog "No role assignments found for custom role '$($roleDefinition.roleName)' on subscription: $($subscription.Name) (ID: $($subscription.Id))" -NoNewLine
630+
}
631+
} -ThrottleLimit $using:ThrottleLimit
632+
}
633+
634+
# Now delete the custom role definition itself
635+
Write-ToConsoleLog "Deleting custom role definition: $($roleDefinition.roleName) (ID: $($roleDefinition.name))" -NoNewLine
636+
637+
if($PlanMode) {
638+
Write-ToConsoleLog "(Plan Mode) Would run: az role definition delete --name $($roleDefinition.name) --scope "/providers/Microsoft.Management/managementGroups/$ManagementGroupId"" -NoNewLine -Color Gray
639+
} else {
640+
$result = az role definition delete --name $roleDefinition.name --scope "/providers/Microsoft.Management/managementGroups/$ManagementGroupId" 2>&1
641+
if (!$result) {
642+
Write-ToConsoleLog "Deleted custom role definition: $($roleDefinition.roleName) (ID: $($roleDefinition.name))" -NoNewLine
643+
} else {
644+
Write-ToConsoleLog "Failed to delete custom role definition: $($roleDefinition.roleName) (ID: $($roleDefinition.name))" -IsWarning -NoNewLine
645+
}
646+
}
647+
}
648+
649+
Write-ToConsoleLog "All custom role definitions processed for management group: $ManagementGroupId ($ManagementGroupDisplayName)" -NoNewLine
650+
}
651+
526652
# Main execution starts here
527653
if ($PSCmdlet.ShouldProcess("Delete Management Groups and Clean Subscriptions", "delete")) {
528654

@@ -531,6 +657,7 @@ function Remove-PlatformLandingZone {
531657
$funcWriteToConsoleLog = ${function:Write-ToConsoleLog}.ToString()
532658
$funcRemoveOrphanedRoleAssignmentsForScope = ${function:Remove-OrphanedRoleAssignmentsForScope}.ToString()
533659
$funcRemoveDeploymentsForScope = ${function:Remove-DeploymentsForScope}.ToString()
660+
$funcRemoveCustomRoleDefinitionsForScope = ${function:Remove-CustomRoleDefinitionsForScope}.ToString()
534661

535662
if($BypassConfirmation) {
536663
Write-ToConsoleLog "Bypass confirmation enabled, proceeding without prompts..." -IsWarning
@@ -691,13 +818,23 @@ function Remove-PlatformLandingZone {
691818
if($using:PlanMode) {
692819
Write-ToConsoleLog "(Plan Mode) Would run: az account management-group subscription add --name $($subscriptionsTargetManagementGroup) --subscription $($subscription.name)" -NoNewLine -Color Gray
693820
} else {
694-
az account management-group subscription add --name $subscriptionsTargetManagementGroup --subscription $subscription.name | Out-Null
821+
$result = (az account management-group subscription add --name $subscriptionsTargetManagementGroup --subscription $subscription.name 2>&1)
822+
if($result) {
823+
Write-ToConsoleLog "Failed to move subscription to target management group: $($subscriptionsTargetManagementGroup), subscription: $($subscription.displayName)" -IsWarning -NoNewLine
824+
} else {
825+
Write-ToConsoleLog "Moved subscription to target management group: $($subscriptionsTargetManagementGroup), subscription: $($subscription.displayName)" -NoNewLine
826+
}
695827
}
696828
} else {
697829
if($using:PlanMode) {
698830
Write-ToConsoleLog "(Plan Mode) Would run: az account management-group subscription remove --name $_ --subscription $($subscription.name)" -NoNewLine -Color Gray
699831
} else {
700-
az account management-group subscription remove --name $_ --subscription $subscription.name | Out-Null
832+
$result = (az account management-group subscription remove --name $_ --subscription $subscription.name 2>&1)
833+
if($result) {
834+
Write-ToConsoleLog "Failed to remove subscription from management group: $_, subscription: $($subscription.displayName)" -IsWarning -NoNewLine
835+
} else {
836+
Write-ToConsoleLog "Removed subscription from management group: $_, subscription: $($subscription.displayName)" -NoNewLine
837+
}
701838
}
702839
}
703840
}
@@ -709,7 +846,12 @@ function Remove-PlatformLandingZone {
709846
if($using:PlanMode) {
710847
Write-ToConsoleLog "(Plan Mode) Would run: az account management-group delete --name $_" -NoNewline -Color Gray
711848
} else {
712-
az account management-group delete --name $_ | Out-Null
849+
$result = (az account management-group delete --name $_ 2>&1)
850+
if($result -like "*Error*") {
851+
Write-ToConsoleLog "Failed to delete management group: $_" -IsWarning -NoNewline
852+
} else {
853+
Write-ToConsoleLog "Deleted management group: $_" -NoNewline
854+
}
713855
}
714856
} -ThrottleLimit $using:ThrottleLimit
715857
}
@@ -941,6 +1083,28 @@ function Remove-PlatformLandingZone {
9411083

9421084
} -ThrottleLimit $ThrottleLimit
9431085

1086+
# Delete custom role definitions from target management groups that are not being deleted
1087+
if($managementGroupsFound.Count -ne 0 -and -not $DeleteTargetManagementGroups) {
1088+
$managementGroupsFound | ForEach-Object -Parallel {
1089+
$managementGroupId = $_.Name
1090+
$managementGroupDisplayName = $_.DisplayName
1091+
$funcWriteToConsoleLog = $using:funcWriteToConsoleLog
1092+
${function:Write-ToConsoleLog} = $funcWriteToConsoleLog
1093+
$funcRemoveCustomRoleDefinitionsForScope = $using:funcRemoveCustomRoleDefinitionsForScope
1094+
${function:Remove-CustomRoleDefinitionsForScope} = $funcRemoveCustomRoleDefinitionsForScope
1095+
1096+
Remove-CustomRoleDefinitionsForScope `
1097+
-ManagementGroupId $managementGroupId `
1098+
-ManagementGroupDisplayName $managementGroupDisplayName `
1099+
-Subscriptions $using:subscriptionsFinal `
1100+
-ThrottleLimit $using:ThrottleLimit `
1101+
-PlanMode:$using:PlanMode
1102+
1103+
} -ThrottleLimit $ThrottleLimit
1104+
} else {
1105+
Write-ToConsoleLog "Skipping custom role definition deletion for management groups" -NoNewLine
1106+
}
1107+
9441108
Write-ToConsoleLog "Cleanup completed." -IsSuccess
9451109
}
9461110
}

0 commit comments

Comments
 (0)