From 2ffd935f57f28aac0bba4f802dc6317372277ae6 Mon Sep 17 00:00:00 2001 From: Heinrich Gantenbein <6719941+techlake@users.noreply.github.com> Date: Wed, 17 Jan 2024 20:25:36 -0600 Subject: [PATCH] Migrating to Invoke-AzRestMethod (#450) --- Scripts/Deploy/Deploy-RolesPlan.ps1 | 74 +++++++++++-------- Scripts/Helpers/Add-HelperScripts.ps1 | 3 +- .../Get-AzPolicyAssignmentRestMethod.ps1 | 18 +++++ ...et-AzPolicyExemptionsAtScopeRestMethod.ps1 | 20 ----- .../Helpers/Set-AzCloudTenantSubscription.ps1 | 2 +- .../Set-AzRoleAssignmentRestMethod.ps1 | 45 +++++++++++ 6 files changed, 109 insertions(+), 53 deletions(-) create mode 100644 Scripts/Helpers/Get-AzPolicyAssignmentRestMethod.ps1 delete mode 100644 Scripts/Helpers/Get-AzPolicyExemptionsAtScopeRestMethod.ps1 create mode 100644 Scripts/Helpers/Set-AzRoleAssignmentRestMethod.ps1 diff --git a/Scripts/Deploy/Deploy-RolesPlan.ps1 b/Scripts/Deploy/Deploy-RolesPlan.ps1 index 5e02ce43..4e0d8800 100644 --- a/Scripts/Deploy/Deploy-RolesPlan.ps1 +++ b/Scripts/Deploy/Deploy-RolesPlan.ps1 @@ -91,7 +91,7 @@ else { Write-Information "Remove ($($removedRoleAssignments.psbase.Count)) obsolete Role assignments" Write-Information "---------------------------------------------------------------------------------------------------" foreach ($roleAssignment in $removedRoleAssignments) { - Write-Information "PrincipalId $($roleAssignment.principalId), role $($roleAssignment.roleDisplayName)($($roleAssignment.roleDefinitionId)) at $($roleAssignment.scope) -- id '$($roleAssignment.id)'" + Write-Information "PrincipalId $($roleAssignment.principalId), role $($roleAssignment.roleDisplayName)($($roleAssignment.roleDefinitionId)) at $($roleAssignment.scope)" $null = Remove-AzRoleAssignmentRestMethod -RoleAssignmentId $roleAssignment.id } Write-Information "" @@ -101,46 +101,58 @@ else { Write-Information "===================================================================================================" Write-Information "Add ($($addedRoleAssignments.psbase.Count)) new Role assignments" Write-Information "---------------------------------------------------------------------------------------------------" - $retriesLimit = 4 - $identitiesByAssignmentId = @{} + + # Get identities for policy assignments from plan or by calling the REST API to retrieve the Policy Assignment + $assignmentById = @{} foreach ($roleAssignment in $addedRoleAssignments) { $principalId = $roleAssignment.principalId - $scope = $roleAssignment.scope - $roleDefinitionId = ($roleAssignment.roleDefinitionId -split "/")[-1] - $assignmentDisplayName = $roleAssignment.displayName if ($null -eq $principalId) { $policyAssignmentId = $roleAssignment.assignmentId $identity = $null - if ($identitiesByAssignmentId.ContainsKey($policyAssignmentId)) { - $identity = $identitiesByAssignmentId.$policyAssignmentId - } - else { - $policyAssignment = Get-AzPolicyAssignment -Id $roleAssignment.assignmentId -WarningAction SilentlyContinue - $identity = $policyAssignment.Identity - $null = $identitiesByAssignmentId.Add($policyAssignmentId, $identity) - } - $principalId = $identity.PrincipalId - $roleAssignment.principalId = $principalId - } - Write-Information "$($assignmentDisplayName)($principalId): $($roleAssignment.roleDisplayName)($($roleDefinitionId)) at $($scope)" - - if (Get-AzRoleAssignment -Scope $scope -ObjectId $principalId -RoleDefinitionId $roleDefinitionId) { - Write-Information "Role assignment already exists" - } - else { - while ($retries -le $retriesLimit) { - # Write-Information "Attempt $retries of $retriesLimit" - $result = New-AzRoleAssignment -Scope $scope -ObjectType $roleAssignment.objectType -ObjectId $principalId -RoleDefinitionId $roleDefinitionId -WarningAction SilentlyContinue - if ($null -ne $result) { - break + $principalId = "" + if (-not $assignmentById.ContainsKey($policyAssignmentId)) { + $policyAssignment = Get-AzPolicyAssignmentRestMethod -AssignmentId $roleAssignment.assignmentId + $identity = $policyAssignment.identity + if ($identity -and $identity.type -ne "None") { + $principalId = "" + if ($identity.type -eq "SystemAssigned") { + $principalId = $identity.principalId + } + else { + $userAssignedIdentityId = $identity.userAssignedIdentities.PSObject.Properties.Name + $principalId = $identity.userAssignedIdentities.$userAssignedIdentityId.principalId + } } else { - Start-Sleep -Seconds 10 - $retries++ + Write-Error "Identity not found for assignment '$($policyAssignmentId)'" -ErrorAction Stop } + $null = $assignmentById.Add($policyAssignmentId, @{ + principalId = $principalId + displayName = $policyAssignment.properties.displayName + }) } } - + else { + $null = $assignmentById.Add($roleAssignment.assignmentId, @{ + principalId = $principalId + displayName = $roleAssignment.displayName + }) + } + } + + # Add the role assignments using the information collected above + foreach ($roleAssignment in $addedRoleAssignments) { + $assignmentId = $roleAssignment.assignmentId + $assignmentInfo = $assignmentById.$assignmentId + $splat = @{ + Scope = $roleAssignment.scope + ObjectType = $roleAssignment.objectType + ObjectId = $assignmentInfo.principalId + RoleDefinitionId = $roleAssignment.roleDefinitionId + RoleDisplayName = $roleAssignment.roleDisplayName + AssignmentDisplayName = $assignmentInfo.displayName + } + Set-AzRoleAssignmentRestMethod @splat -IgnoreDuplicateError } } Write-Information "" diff --git a/Scripts/Helpers/Add-HelperScripts.ps1 b/Scripts/Helpers/Add-HelperScripts.ps1 index 3ceb6b7f..6fcbad85 100644 --- a/Scripts/Helpers/Add-HelperScripts.ps1 +++ b/Scripts/Helpers/Add-HelperScripts.ps1 @@ -44,7 +44,7 @@ . "$PSScriptRoot/Find-AzNonCompliantResources.ps1" . "$PSScriptRoot/Get-AssignmentsDetails.ps1" -. "$PSScriptRoot/Get-AzPolicyExemptionsAtScopeRestMethod.ps1" +. "$PSScriptRoot/Get-AzPolicyAssignmentRestMethod.ps1" . "$PSScriptRoot/Get-AzPolicyResources.ps1" . "$PSScriptRoot/Get-AzScopeTree.ps1" . "$PSScriptRoot/Get-CustomMetadata.ps1" @@ -91,6 +91,7 @@ . "$PSScriptRoot/Set-AzPolicyDefinitionRestMethod.ps1" . "$PSScriptRoot/Set-AzPolicySetDefinitionRestMethod.ps1" . "$PSScriptRoot/Set-AzPolicyExemptionRestMethod.ps1" +. "$PSScriptRoot/Set-AzRoleAssignmentRestMethod.ps1" . "$PSScriptRoot/Export-AssignmentNode.ps1" . "$PSScriptRoot/Set-ExportNode.ps1" diff --git a/Scripts/Helpers/Get-AzPolicyAssignmentRestMethod.ps1 b/Scripts/Helpers/Get-AzPolicyAssignmentRestMethod.ps1 new file mode 100644 index 00000000..a12e330b --- /dev/null +++ b/Scripts/Helpers/Get-AzPolicyAssignmentRestMethod.ps1 @@ -0,0 +1,18 @@ +function Get-AzPolicyAssignmentRestMethod { + [CmdletBinding()] + param ( + [string] $AssignmentID + ) + + # Invoke the REST API + $response = Invoke-AzRestMethod -Path "$($AssignmentId)?api-version=2022-06-01" -Method GET + + # Process response + $statusCode = $response.StatusCode + if ($statusCode -lt 200 -or $statusCode -ge 300) { + $content = $response.Content + Write-Error "Get Policy Assignment error for '$policyAssignmentId' $($statusCode) -- $($content)" -ErrorAction Stop + } + + return $response.Content | ConvertFrom-Json -Depth 100 +} diff --git a/Scripts/Helpers/Get-AzPolicyExemptionsAtScopeRestMethod.ps1 b/Scripts/Helpers/Get-AzPolicyExemptionsAtScopeRestMethod.ps1 deleted file mode 100644 index 4f4517b7..00000000 --- a/Scripts/Helpers/Get-AzPolicyExemptionsAtScopeRestMethod.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -function Get-AzPolicyExemptionsAtScopeRestMethod { - [CmdletBinding()] - param ( - [string] $Scope - ) - - $response = Invoke-AzRestMethod -Path "$($Scope)/providers/Microsoft.Authorization/policyExemptions?api-version=2022-07-01-preview" -Method GET - - # Process response - $statusCode = $response.StatusCode - if ($statusCode -lt 200 -or $statusCode -ge 300) { - $content = $response.Content - Write-Error "Policy Exemption error $($statusCode) -- $($content)" -ErrorAction Stop - } - - $content = $response.Content - $exemptions = $content | ConvertFrom-Json - return $exemptions.value - -} diff --git a/Scripts/Helpers/Set-AzCloudTenantSubscription.ps1 b/Scripts/Helpers/Set-AzCloudTenantSubscription.ps1 index b0a62756..b5ce034a 100644 --- a/Scripts/Helpers/Set-AzCloudTenantSubscription.ps1 +++ b/Scripts/Helpers/Set-AzCloudTenantSubscription.ps1 @@ -7,7 +7,7 @@ function Set-AzCloudTenantSubscription { [Parameter(Mandatory = $true)] [bool] $Interactive ) - if (!(Get-Module Az.ResourceGraph -ListAvailable)) { + if ($null -eq (Get-Module Az.ResourceGraph -ListAvailable)) { Write-Information "Installing Az.ResourceGraph module" Install-Module Az.ResourceGraph -Force -Repository PSGallery } diff --git a/Scripts/Helpers/Set-AzRoleAssignmentRestMethod.ps1 b/Scripts/Helpers/Set-AzRoleAssignmentRestMethod.ps1 new file mode 100644 index 00000000..80e41886 --- /dev/null +++ b/Scripts/Helpers/Set-AzRoleAssignmentRestMethod.ps1 @@ -0,0 +1,45 @@ +function Set-AzRoleAssignmentRestMethod { + [CmdletBinding()] + param ( + $Scope, + $ObjectType, + $ObjectId, + $RoleDefinitionId, + $AssignmentDisplayName, + $RoleDisplayName, + [switch] $IgnoreDuplicateError + ) + + # Write log info + Write-Information "Assignment '$AssignmentDisplayName', principalId $ObjectId, role $RoleDisplayName($roleDefinitionId) at $scope" + + # Build the Path + $guid = New-Guid + $path = "$scope/providers/Microsoft.Authorization/roleAssignments/$($guid.ToString())?api-version=2022-04-01" + + # Build the REST API body + $body = @{ + properties = @{ + roleDefinitionId = $RoleDefinitionId + principalId = $ObjectId + principalType = $ObjectType + } + } + + # Invoke the REST API + $bodyJson = ConvertTo-Json $body -Depth 100 -Compress + $response = Invoke-AzRestMethod -Path $path -Method PUT -Payload $bodyJson + + # Process response + $statusCode = $response.StatusCode + if ($statusCode -lt 200 -or $statusCode -ge 300) { + $content = $response.Content + if ($IgnoreDuplicateError -and $statusCode -eq 409) { + $errorBody = $content | ConvertFrom-Json -Depth 100 + Write-Information $errorBody.error.message + } + else { + Write-Error "Role Assignment error $($statusCode) -- $($content)" -ErrorAction Stop + } + } +}