-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpredown-remove-app-registrations.ps1
More file actions
126 lines (100 loc) · 5.22 KB
/
Copy pathpredown-remove-app-registrations.ps1
File metadata and controls
126 lines (100 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<#
The Azure Developer CLI doesn't support deleting Entra ID resources yet, so we have to do it in a hook.
Related GitHub issue: https://github.com/Azure/azure-dev/issues/4724
We're using a predown hook because the environment variables are (sometimes) empty in a postdown hook.
The Entra ID resources have a custom tag "azd-env-id: <environment-id>", so we can find and delete them.
#>
param(
[Parameter(Mandatory = $false)]
[string]$SubscriptionId = $env:AZURE_SUBSCRIPTION_ID,
[Parameter(Mandatory = $false)]
[string]$AzureEnvironmentId = $env:AZURE_ENV_ID
)
# This retry function is included because Entra ID operations can be eventually consistent,
# leading to transient failures when trying to delete or verify deletions.
# The function retries the provided script block up to MaxAttempts times.
function Invoke-WithRetry {
param(
[Parameter(Mandatory = $true)]
[scriptblock]$ScriptBlock,
[Parameter(Mandatory = $false)]
[int]$MaxAttempts = 15,
[Parameter(Mandatory = $false)]
[int]$DelayInSeconds = 2
)
$attempt = 1
while ($attempt -le $MaxAttempts) {
& $ScriptBlock
if ($LASTEXITCODE -eq 0) {
Write-Host "Operation succeeded on attempt $attempt"
return
}
if ($attempt -eq $MaxAttempts) {
Write-Host "Operation failed after $MaxAttempts attempts (exit code: $LASTEXITCODE)"
return
}
Write-Host "Operation failed (attempt $attempt/$MaxAttempts, exit code: $LASTEXITCODE). Retrying in $DelayInSeconds seconds..."
Start-Sleep -Seconds $DelayInSeconds
$attempt++
}
}
# Validate required parameters
if ([string]::IsNullOrEmpty($SubscriptionId)) {
throw "SubscriptionId parameter is required. Please provide it as a parameter or set the AZURE_SUBSCRIPTION_ID environment variable."
}
if ([string]::IsNullOrEmpty($AzureEnvironmentId)) {
throw "AzureEnvironmentId parameter is required. Please provide it as a parameter or set the AZURE_ENV_ID environment variable."
}
# First, ensure the Azure CLI is logged in and set to the correct subscription
az account set --subscription $SubscriptionId
if ($LASTEXITCODE -ne 0) {
throw "Unable to set the Azure subscription. Please make sure that you're logged into the Azure CLI with the same credentials as the Azure Developer CLI."
}
# Find all app registrations with the matching azd-env-id tag
$targetTag = "azd-env-id: $AzureEnvironmentId"
Write-Host "Looking for app registrations with tag '$targetTag'"
$apps = az ad app list | ConvertFrom-Json | Where-Object { $_.tags -contains $targetTag }
if ($apps) {
Write-Host "Found $($apps.Count) app registration(s) to delete"
foreach ($app in $apps) {
# Get the service principal of the application
$sp = az ad sp list --all | ConvertFrom-Json | Where-Object { $_.appId -eq $app.appId }
if ($sp) {
Write-Host "Deleting service principal $($sp.id) of application with unique name $($app.uniqueName)"
# Delete the service principal (moves the service principal to the deleted items)
Invoke-WithRetry -ScriptBlock {
az ad sp delete --id $sp.id
}
Write-Host "Verifying service principal $($sp.id) is in deleted items..."
# Wait for the service principal to appear in deleted items
Invoke-WithRetry -ScriptBlock {
$deletedSp = az rest --method GET --url "https://graph.microsoft.com/beta/directory/deleteditems/$($sp.id)"
}
Write-Host "Permanently deleting service principal $($sp.id) of application with unique name $($app.uniqueName)"
# Permanently delete the service principal. If we don't do this, we can't create a new service principal with the same name.
Invoke-WithRetry -ScriptBlock {
az rest --method DELETE --url "https://graph.microsoft.com/beta/directory/deleteditems/$($sp.id)"
}
}
else {
Write-Host "Unable to delete service principal for application with unique name $($app.uniqueName). Service principal not found."
}
Write-Host "Deleting application $($app.id) with unique name $($app.uniqueName)"
# Delete the application (moves the application to the deleted items)
Invoke-WithRetry -ScriptBlock {
az ad app delete --id $app.id
}
Write-Host "Verifying application $($app.id) is in deleted items..."
# Wait for the application to appear in deleted items
Invoke-WithRetry -ScriptBlock {
$deletedApp = az rest --method GET --url "https://graph.microsoft.com/beta/directory/deleteditems/$($app.id)"
}
Write-Host "Permanently deleting application $($app.id) with unique name $($app.uniqueName)"
# Permanently delete the application. If we don't do this, we can't create a new application with the same name.
Invoke-WithRetry -ScriptBlock {
az rest --method DELETE --url "https://graph.microsoft.com/beta/directory/deleteditems/$($app.id)"
}
}
} else {
Write-Host "No app registrations found with tag: '$targetTag'"
}