From 3762756f3d64e0d3d597ab2a2c088c7de8f0a803 Mon Sep 17 00:00:00 2001 From: Jared Holgate Date: Fri, 4 Apr 2025 11:35:12 +0100 Subject: [PATCH] feat: update subscription creator role assignment function --- actions_bootstrap.ps1 | 9 - src/ALZ/ALZ.psd1 | 2 +- .../Public/Grant-SubscriptionCreatorRole.ps1 | 150 ++++++++++++++ .../Invoke-EABillingSPNPermissionsSetup.ps1 | 184 ------------------ 4 files changed, 151 insertions(+), 194 deletions(-) create mode 100644 src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 delete mode 100644 src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 diff --git a/actions_bootstrap.ps1 b/actions_bootstrap.ps1 index 4df355f0..71665a48 100644 --- a/actions_bootstrap.ps1 +++ b/actions_bootstrap.ps1 @@ -29,15 +29,6 @@ $null = $modulesToInstall.Add(([PSCustomObject]@{ ModuleName = 'platyPS' ModuleVersion = '0.12.0' })) -# Required for Invoke-EABillingSPNPermissionsSetup to work -$null = $modulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'Az.Accounts' - ModuleVersion = '2.10.4' - })) -$null = $modulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'Az.Resources' - ModuleVersion = '6.5.0' - })) 'Installing PowerShell Modules' foreach ($module in $modulesToInstall) { diff --git a/src/ALZ/ALZ.psd1 b/src/ALZ/ALZ.psd1 index 9f5fd242..9349296e 100644 --- a/src/ALZ/ALZ.psd1 +++ b/src/ALZ/ALZ.psd1 @@ -74,7 +74,7 @@ FunctionsToExport = @( 'Test-AcceleratorRequirement', 'Deploy-Accelerator', - 'Invoke-EABillingSPNPermissionsSetup' + 'Grant-SubscriptionCreatorRole' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 b/src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 new file mode 100644 index 00000000..555716f9 --- /dev/null +++ b/src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 @@ -0,0 +1,150 @@ +function Grant-SubscriptionCreatorRole { + <# +.SYNOPSIS +Assigns the 'SubscriptionCreator' role to a specified service principal to allow it to create subscriptions in the specified billing account. + +.DESCRIPTION +Assigns the 'SubscriptionCreator' role to a specified service principal to allow it to create subscriptions in the specified billing account. + +.EXAMPLE +# Grant the 'SubscriptionCreator' role on the specified Enterprise Agreement billing account - using the 'billingAccountID' and 'enrollmentAccountID' parameters +Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb-cb96cfe10fb5" -billingAccountID "1234567" -enrollmentAccountID "987654" + +# Grant the 'SubscriptionCreator' role on the specified Enterprise Agreement billing account - using the 'billingResourceID' parameter +Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb-cb96cfe10fb5" -billingResourceID "/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654" + +# Grant the 'SubscriptionCreator' role on the specified Microsoft Customer Agreement billing account - using the 'billingAccountID', 'billingProfileID', and 'invoiceSectionID' parameters +Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb-cb96cfe10fb5" -billingAccountID "aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_xxxx-xx-xx" -billingProfileID "AW4F-xxxx-xxx-xxx" -invoiceSectionID "SH3V-xxxx-xxx-xxx" + +# Grant the 'SubscriptionCreator' role on the specified Microsoft Customer Agreement billing account - using the 'billingResourceID' parameter +Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb-cb96cfe10fb5" -billingResourceID "/providers/Microsoft.Billing/billingAccounts/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_xxxx-xx-xx/billingProfiles/AW4F-xxxx-xxx-xxx/invoiceSections/SH3V-xxxx-xxx-xxx" +#> + + [CmdletBinding(DefaultParameterSetName = "Default")] + param ( + [Parameter(ParameterSetName = "Default", Mandatory = $true, Position = 1, HelpMessage = "(Required) Provide a Service Principal Object ID to grant the 'SubscriptionCreator' role to on the specified billing account. This can be an app registration or a managed identity. Example: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.")] + [string] + $servicePrincipalObjectId, + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 2, HelpMessage = "(Optional) If using an Enterprise Agreement or Microsoft Customer Agreement, provide the billing account ID that the service principal will be granted the 'SubscriptionCreator' role upon. Examples: '1234567' or 'aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_xxxx-xx-xx'.")] + [string] + $billingAccountID = "", + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 3, HelpMessage = "(Optional) If using an Enterprise Agreement, provide the enrollment account ID that the service principal will be granted the 'SubscriptionCreator' role upon. Example: '987654'.")] + [string] + $enrollmentAccountID = "", + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 4, HelpMessage = "(Optional) If using a Microsoft Customer Agreement, provide the billing profile ID that the service principal will be granted the 'SubscriptionCreator' role upon. Example: 'AW4F-xxxx-xxx-xxx'.")] + [string] + $billingProfileID = "", + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 5, HelpMessage = "(Optional) If using a Microsoft Customer Agreement, provide the invoice section ID that the service principal will be granted the 'SubscriptionCreator' role upon. Example: 'SH3V-xxxx-xxx-xxx'.")] + [string] + $invoiceSectionID = "", + + [Parameter(ParameterSetName = "Advanced", Mandatory = $false, Position = 6, HelpMessage = "(Optional) Provide the resource ID for the billing account that the service will be granted the 'SubscriptionCreator' role upon. This differs based on the type of agreement you have. Examples: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' or '/providers/Microsoft.Billing/billingAccounts/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_xxxx-xx-xx/billingProfiles/AW4F-xxxx-xxx-xxx/invoiceSections/SH3V-xxxx-xxx-xxx'.")] + [string] + $billingResourceID = "", + + + [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 7, HelpMessage = "(Optional) Provide a the Azure management API url prefix. Default: 'https://management.azure.com'.")] + [string] + $managementApiPrefix = "https://management.azure.com" + ) + + # Checks + Write-Host "Checking inputs..." -ForegroundColor Cyan + Write-Host "" + + if($null -eq $servicePrincipalObjectId -or $servicePrincipalObjectId -eq "") { + $errorMessage = "The 'Service Principal Object ID' parameter is required. Please provide a valid value and try again." + Write-Error $errorMessage + throw $errorMessage + } + + $enterpriseAgreementResourceIDFormat = "/providers/Microsoft.Billing/billingAccounts/$billingAccountID/enrollmentAccounts/$enrollmentAccountID" + $microsoftCustomerAgreementResourceIDFormat = "/providers/Microsoft.Billing/billingAccounts/$billingAccountID/billingProfiles/$billingProfileID/invoiceSections/$invoiceSectionID" + + if($null -ne $billingAccountID -and $billingAccountID -ne "" -and $null -ne $billingResourceID -and $billingResourceID -ne "" -and $null -ne $invoiceSectionID -and $invoiceSectionID -ne "") { + $billingResourceID = $microsoftCustomerAgreementResourceIDFormat + Write-Host "Microsoft Customer Agreement (MCA) parameters provided..." + } + + if($null -ne $billingAccountID -and $billingAccountID -ne "" -and $null -ne $enrollmentAccountID -and $enrollmentAccountID -ne "") { + $billingResourceID = $enterpriseAgreementResourceIDFormat + Write-Host "Enterpruse Agreement (EA) parameters provided..." + } + + if($null -ne $billingResourceID -and $billingResourceID -ne "") { + Write-Host "Billing Resource ID or required parameters provided..." -ForegroundColor Green + } else { + $errorMessage = "No Billing Resource ID or required parameters provided." + Write-Error $errorMessage + throw $errorMessage + } + + Write-Host "Checking the specified billing account resource ID '$($billingResourceID)' exists..." -ForegroundColor Yellow + + # Check $billingResourceID is valid and exists + $getbillingResourceID = $(az rest --method GET --url "$managementApiPrefix$($billingResourceID)?api-version=2024-04-01") | ConvertFrom-Json + + if ($null -eq $getbillingResourceID) { + $errorMessage = "The specified billing account resource ID '$($billingResourceID)' does not exist or you do not have access to it. Please check the value and try again. Also ensure you are logged in as the Account Owner for the specified billing account." + Write-Error $errorMessage + throw $errorMessage + } else { + Write-Host "The specified billing account ID '$($billingResourceID)' exists. Continuing..." -ForegroundColor Green + Write-Host "" + } + + # Check $existingSpnMiObjectId is valid and exists + Write-Host "Checking the specified service principal 'Object ID' '$($servicePrincipalObjectId)' exists..." -ForegroundColor Yellow + $getexistingSpnMiObjectId = $(az ad sp show --id $servicePrincipalObjectId) | ConvertFrom-Json + + if ($null -eq $getexistingSpnMiObjectId) { + $errorMessage = "The specified service principal 'Object ID' '$($existingSpnMiObjectId)' does not exist. Please check the value and try again." + Write-Error $errorMessage + throw $errorMessage + } else { + $finalSpnMiObjectId = $getexistingSpnMiObjectId.id + $finalSpnMiDisplayName = $getexistingSpnMiObjectId.displayName + $finalSpnMiType = $getexistingSpnMiObjectId.servicePrincipalType + + Write-Host "The specified service principal 'Object ID' '$($servicePrincipalObjectId)' exists with a Display Name of: '$finalSpnMiDisplayName' with a Type of: '$finalSpnMiType'. Continuing..." -ForegroundColor Green + Write-Host "" + } + + # Grant service principal access to the specified EA billing account + $subscriptionCreatorRoleId = "a0bcee42-bf30-4d1b-926a-48d21664ef71" + Write-Host "Pre-reqs passed and complete..." -ForegroundColor Cyan + Write-Host "Granting the 'SubscriptionCreator' role (ID: '$subscriptionCreatorRoleId') on the Billing Account ID of: '$($billingResourceID)' to the AAD Object ID of: '$($finalSpnMiObjectId)' which has the Display Name of: '$($finalSpnMiDisplayName)'..." -ForegroundColor Yellow + + # Get the current AAD Tenant ID + $tenantId = $(az account show --query tenantId -o tsv) + + # Create GUID for role assignment name + $roleAssignmentName = New-Guid + + $roleAssignmentHashTable = [ordered]@{ + "properties" = @{ + "principalId" = "$finalSpnMiObjectId" + "roleDefinitionId" = "$billingResourceID/billingRoleDefinitions/$subscriptionCreatorRoleId" + "principalTenantId" = $tenantId + } + } + $roleAssignmentPayloadJson = $roleAssignmentHashTable | ConvertTo-Json -Depth 100 -Compress + $roleAssignmentPayloadJson = $roleAssignmentPayloadJson -replace '"', '\"' + + $grantRbac = $(az rest --method PUT --url "$managementApiPrefix$($billingResourceID)/billingRoleAssignments/$($roleAssignmentName)?api-version=2024-04-01" --body $roleAssignmentPayloadJson) | ConvertFrom-Json + + if ($null -eq $grantRbac) { + $errorMessage = "The 'SubscriptionCreator' role could not be granted to the service principal. Please check the error message above and try again." + Write-Error $errorMessage + throw $errorMessage + } else { + Write-Host "The 'SubscriptionCreator' role has been granted to the service principal." -ForegroundColor Green + Write-Host "" + } + + return +} diff --git a/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 b/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 deleted file mode 100644 index 42f358f0..00000000 --- a/src/ALZ/Public/Invoke-EABillingSPNPermissionsSetup.ps1 +++ /dev/null @@ -1,184 +0,0 @@ -function Invoke-EABillingSPNPermissionsSetup { - <# -.SYNOPSIS -Creates a new SPN, or uses an existing SPN/MI, and assigns it the 'SubscriptionCreator' role to it to allow it to create subscriptions in the specified EA billing enrollment account. - -.DESCRIPTION -Creates a new SPN, or uses an existing SPN/MI, and assigns it the 'SubscriptionCreator' role to it to allow it to create subscriptions in the specified EA billing enrollment account. - -.EXAMPLE -# Create a new SPN and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters -/Invoke-EABillingSPNPermissionsSetup.ps1 -eaEnrollmentNumber "123456" -eaEnrollmentAccountNumber "987654" - -# Create a new SPN and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaBillingAccountResourceId' parameter -./Invoke-EABillingSPNPermissionsSetup.ps1 -eaBillingAccountResourceId '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' - -# Create a new SPN, with a custom name, and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters -./Invoke-EABillingSPNPermissionsSetup.ps1 -eaEnrollmentNumber "123456" -eaEnrollmentAccountNumber "987654" -newSpnDisplayName 'spn-lz-sub-vending-custom-name' - -# Create a new SPN, with a custom name, and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaBillingAccountResourceId' parameter -./Invoke-EABillingSPNPermissionsSetup.ps1 -eaBillingAccountResourceId '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' -newSpnDisplayName 'spn-lz-sub-vending-custom-name' - -# Use an existing SPN/MI and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters -./Invoke-EABillingSPNPermissionsSetup.ps1 -eaEnrollmentNumber "123456" -eaEnrollmentAccountNumber "987654" -existingSpnMiObjectId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - -# Use an existing SPN/MI and grant it the 'SubscriptionCreator' role on the specified EA billing account - using the 'eaBillingAccountResourceId' parameter -./Invoke-EABillingSPNPermissionsSetup.ps1 -eaBillingAccountResourceId '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' -existingSpnMiObjectId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' -.NOTES -# Release notes 22/12/2022 - V1.0.0: -- Initial release. - -# Release notes 23/12/2022 - V1.1.0: -- Added simplified inputs for the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters to form the 'eaBillingAccountResourceId' parameter value, instead of having to provide the full resource ID in the 'eaBillingAccountResourceId' parameter. -#> - - # Check for pre-reqs - #Requires -PSEdition Core - #Requires -Modules @{ ModuleName="Az.Accounts"; ModuleVersion="2.10.4" } - #Requires -Modules @{ ModuleName="Az.Resources"; ModuleVersion="6.5.0" } - - [CmdletBinding(DefaultParameterSetName = "Default")] - param ( - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 1, HelpMessage = "Provide the EA enrollment number that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '1234567'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] - [string] - $eaEnrollmentNumber, - - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 2, HelpMessage = "Provide the EA enrollment Account Number/ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '987654'. This parameter is only used if the 'eaBillingAccountResourceId' parameter is not provided. It's value is used to create the 'eaBillingAccountResourceId' parameter value, that looks like this: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/987654' (this parameter value is the middle numerical value).")] - [string] - $eaEnrollmentAccountNumber, - - [Parameter(ParameterSetName = "Advanced", Mandatory = $false, Position = 4, HelpMessage = "Provide the EA enrollment/billing account ID that the SPN will be granted the 'SubscriptionCreator' role upon. Example: '/providers/Microsoft.Billing/billingAccounts/1234567/enrollmentAccounts/123456'")] - [string] - $eaBillingAccountResourceId, - - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 3, HelpMessage = "(Optional) Provide an existing Service Principal Name (SPN) (aka Enterprise Application) 'Object ID' to grant the 'SubscriptionCreator' role to on the specified billing account instead of creating a new one. If left blank a new SPN will be created. This can also be the object ID of a Managed Identity's SPN.")] - [string] - $existingSpnMiObjectId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - - [Parameter(ParameterSetName = "Default", Mandatory = $false, Position = 5, HelpMessage = "(Optional) Provide a Display Name for the new Service Principal (SPN) (aka Enterprise Application) to be created. If left blank the default value of 'spn-lz-sub-vending' will be used.")] - [string] - $newSpnDisplayName = "spn-lz-sub-vending" - ) - - # Checks - Write-Host "Checking inputs..." -ForegroundColor Cyan - Write-Host "" - - # Check $eaBillingAccountResourceId is valid and populate from $eaEnrollmentNumber and $eaEnrollmentAccountNumber if not provided - if ($eaBillingAccountResourceId -eq $null -or $eaBillingAccountResourceId -eq "") { - Write-Host "eaBillingAccountResourceId paramter value not set, forming parameter value from eaEnrollmentNumber and eaEnrollmentAccountNumber parameters..." -ForegroundColor Magenta - if ($eaEnrollmentNumber -eq $null -or $eaEnrollmentAccountNumber -eq $null -or $eaEnrollmentNumber -eq '' -or $eaEnrollmentAccountNumber -eq '') { - throw "No values provdided for the 'eaEnrollmentNumber' and 'eaEnrollmentAccountNumber' parameters. These parameters are required if the 'eaBillingAccountResourceId' parameter is not provided. Please provide values for these parameters and try again." - } - $eaBillingAccountResourceId = "/providers/Microsoft.Billing/billingAccounts/$eaEnrollmentNumber/enrollmentAccounts/$eaEnrollmentAccountNumber" - Write-Host "eaBillingAccountResourceId parameter value set to '$($eaBillingAccountResourceId)'" -ForegroundColor Green - Write-Host "" - } - - # Check $eaBillingAccountResourceId is valid and exists - Write-Host "EA billing account parameters provided..." -ForegroundColor Cyan - Write-Host "Checking the specified EA billing account ID '$($eaBillingAccountResourceId)' exists..." -ForegroundColor Yellow - - if ($null -ne $eaBillingAccountResourceId -and $eaBillingAccountResourceId -ne "") { - $geteaBillingAccountResourceId = Invoke-AzRestMethod -Method GET -Path "$($eaBillingAccountResourceId)?api-version=2019-10-01-preview" -ErrorAction SilentlyContinue - - if ($geteaBillingAccountResourceId.StatusCode -ne 200) { - Write-Error "HTTP Status Code: $($geteaBillingAccountResourceId.StatusCode)" - Write-Error "HTTP Repsone Content: $($geteaBillingAccountResourceId.Content)" - throw "The specified EA billing account ID '$($eaBillingAccountResourceId)' does not exist. Please check the value and try again. Also ensure you are logged in as the EA Account Owner for the specified EA billing account." - } else { - Write-Host "The specified EA billing account ID '$($eaBillingAccountResourceId)' exists. Continuing..." -ForegroundColor Green - Write-Host "" - } - } - - # Check $existingSpnMiObjectId is valid and exists - if ($existingSpnMiObjectId -ne $null -and $existingSpnMiObjectId -ne "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") { - Write-Host "Existing SPN/MI provided..." -ForegroundColor Cyan - Write-Host "Checking the specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists..." -ForegroundColor Yellow - $getexistingSpnMiObjectId = Get-AzADServicePrincipal -ObjectId $existingSpnMiObjectId -ErrorAction SilentlyContinue - - if ($null -eq $getexistingSpnMiObjectId) { - throw "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' does not exist. Please check the value and try again." - } else { - Write-Host "The specified SPN/MI 'Object ID' '$($existingSpnMiObjectId)' exists with a Display Name of: '$($getexistingSpnMiObjectId.DisplayName)' with a Type of: '$($getexistingSpnMiObjectId.ServicePrincipalType)'. Continuing..." -ForegroundColor Green - Write-Host "" - $finalSpnMiObjectId = $getexistingSpnMiObjectId.Id - $finalSpnMiAppId = $getexistingSpnMiObjectId.AppId - $finalSpnMiDisplayName = $getexistingSpnMiObjectId.DisplayName - $finalSpnMiType = $getexistingSpnMiObjectId.ServicePrincipalType - } - } else { - # Create a new SPN (aka Enterprise Application) as no existing SPN/MI was provided via the $existingSpnMiObjectId parameter - Write-Host "No Existing SPN/MI provided. Proceeding to create a new SPN (aka Enterprise Application)..." -ForegroundColor Cyan - Write-Host "Creating a new SPN (aka Enterprise Application) with a Display Name of '$($newSpnDisplayName)'..." -ForegroundColor Yellow - - $newSpn = New-AzADServicePrincipal -DisplayName $newSpnDisplayName -Description "Service Principal Name (SPN) for the Landing Zone Subscription Vending. See https://aka.ms/lz-vending/bicep or https://aka.ms/lz-vending/tf for more information." -ErrorAction Stop - Write-Host "New SPN (aka Enterprise Application) created with a Display Name of '$($newSpn.DisplayName)' and an Object ID of '$($newSpn.Id)'." -ForegroundColor Green - Write-Host "" - - $finalSpnMiObjectId = $newSpn.Id - $finalSpnMiDisplayName = $newSpn.DisplayName - $finalSpnMiType = $newSpn.ServicePrincipalType - $finalSpnMiAppId = $newSpn.AppId - } - - ## convert this to a retry loop - # Start-Sleep -Seconds 15 - - # Grant SPN/MI access to the specified EA billing account - Write-Host "Pre-reqs passed and complete..." -ForegroundColor Cyan - Write-Host "Granting the 'SubscriptionCreator' role (ID: 'a0bcee42-bf30-4d1b-926a-48d21664ef71') on the EA Billing Account ID of: '$($eaBillingAccountResourceId)' to the AAD Object ID of: '$($finalSpnMiObjectId)' which has the Display Name of: '$($finalSpnMiDisplayName)'..." -ForegroundColor Yellow - - # Get the current AAD Tenant ID - $currentTenant = Get-AzTenant -ErrorAction Stop - - # Create GUID for role assignment name - $roleAssignmentName = New-Guid - - $roleAssignmentHashTable = [ordered]@{ - "properties" = @{ - "principalId" = "$finalSpnMiObjectId" - "roleDefinitionId" = "$eaBillingAccountResourceId/billingRoleDefinitions/a0bcee42-bf30-4d1b-926a-48d21664ef71" - "principalTenantId" = "$($currentTenant.TenantId)" - } - } - $roleAssignmentPayloadJson = $roleAssignmentHashTable | ConvertTo-Json -Depth 100 - - $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue - - # Create variables for retry loop - $retryCount = 0 - $retryLimit = 10 - $retryDelay = 5 - - - if ($grantRbac.StatusCode -eq 400 -and $grantRbac.Content.Contains("are not valid")) { - while ($retryCount -le $retryLimit) { - Write-Host "The 'SubscriptionCreator' role has not been granted to the SPN/MI. Retrying in $retryDelay seconds to allow platform replication to occur..." -ForegroundColor Magenta - - Start-Sleep -Seconds $retryDelay - $retryCount++ - - $grantRbac = Invoke-AzRestMethod -Method PUT -Path "$($eaBillingAccountResourceId)/billingRoleAssignments/$($roleAssignmentName)?api-version=2019-10-01-preview" -Payload $roleAssignmentPayloadJson -ErrorAction SilentlyContinue - - if ($grantRbac.StatusCode -eq 200) { - break - } - } - } - if ($grantRbac.StatusCode -ne 200) { - Write-Error "HTTP Status Code: $($grantRbac.StatusCode)" - Write-Error "HTTP Repsone Content: $($grantRbac.Content)" - throw "An error occurred while attempting to grant the 'SubscriptionCreator' role to the SPN/MI. Please check the error message above and try again." - } else { - Write-Host "The 'SubscriptionCreator' role has been granted to the SPN/MI." -ForegroundColor Green - Write-Host "" - Write-Host "The SPN/MI 'Object ID' is: '$($finalSpnMiObjectId)'" -ForegroundColor Green - Write-Host "The SPN/MI 'App ID' is: '$($finalSpnMiAppId)'" -ForegroundColor Green - Write-Host "The SPN/MI 'Display Name' is: '$($finalSpnMiDisplayName)'" -ForegroundColor Green - Write-Host "The SPN/MI 'Type' is: '$($finalSpnMiType)'" -ForegroundColor Green - } - - return -}