Private/Remove-365TuneElevation.ps1
|
function Remove-365TuneElevation { <# .SYNOPSIS Removes User Access Administrator elevation from root scope for the current user. #> $ctx = Get-AzContext $currentUser = $ctx.Account.Id $uaaRoleId = "18d7d88d-d35e-4fb5-a5c3-7773c20a72d9" # Decode OID from the current JWT - works for both user and MSI/SP accounts $currentOid = $null $meTokenStr = $null try { $meToken = Get-AzAccessToken -ResourceUrl "https://management.azure.com" -ErrorAction Stop $meTokenStr = if ($meToken.Token -is [System.Security.SecureString]) { [System.Net.NetworkCredential]::new("", $meToken.Token).Password } else { $meToken.Token } } catch { $meTokenStr = az account get-access-token --resource https://management.azure.com --query accessToken -o tsv 2>$null } if ($meTokenStr) { try { $jwtPayload = $meTokenStr.Split(".")[1] $pad = 4 - ($jwtPayload.Length % 4) if ($pad -ne 4) { $jwtPayload += "=" * $pad } $claims = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($jwtPayload)) | ConvertFrom-Json $currentOid = $claims.oid } catch { Write-Verbose "Could not decode token OID" } } # Primary: REST lookup by principalId - reliable for user AND MSI/SP (no SignInName dependency) $assignmentPath = $null if ($currentOid -and $meTokenStr) { try { $mgmtHeaders = @{ Authorization = "Bearer $meTokenStr" } $result = Invoke-RestMethod -Uri "https://management.azure.com/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&`$filter=principalId eq '$currentOid'" -Headers $mgmtHeaders -Method GET -ErrorAction Stop -TimeoutSec 30 $found = $result.value | Where-Object { $_.properties.scope -eq "/" -and $_.properties.roleDefinitionId -like "*$uaaRoleId*" } | Select-Object -First 1 if ($found) { $assignmentPath = $found.id.TrimStart('/') } } catch { Write-Verbose "REST OID lookup failed: $_" } } # Fallback: Az cmdlet - works when SignInName or ObjectId matches (user accounts at subscription scope) if (-not $assignmentPath) { $azAssignment = Get-AzRoleAssignment -RoleDefinitionId $uaaRoleId -Scope "/" -ErrorAction SilentlyContinue | Where-Object { $_.SignInName -eq $currentUser -or ($currentOid -and $_.ObjectId -eq $currentOid) } if ($azAssignment) { $assignmentPath = $azAssignment.RoleAssignmentId.TrimStart('/') } } if (-not $assignmentPath) { Write-Host " Elevation already removed." -ForegroundColor Gray return } # Wait for elevated token to propagate before DELETE Write-Host " Waiting for propagation..." -ForegroundColor Gray Start-Sleep -Seconds 20 $deleteUri = "https://management.azure.com/$assignmentPath`?api-version=2018-07-01" $delHeaders = @{ Authorization = "Bearer $meTokenStr" } $deleteStatus = $null try { $delResp = Invoke-WebRequest -Uri $deleteUri -Method DELETE -Headers $delHeaders -UseBasicParsing $deleteStatus = [int]$delResp.StatusCode } catch { if ($_.Exception.Response) { $deleteStatus = [int]$_.Exception.Response.StatusCode } else { throw } } if ($deleteStatus -in @(200, 204)) { Write-Host " [OK] Elevation removed." -ForegroundColor Green } elseif ($deleteStatus -eq 403) { # Retry once after additional propagation delay Write-Host " Retrying after additional propagation delay..." -ForegroundColor Gray Start-Sleep -Seconds 20 # Re-fetch token for retry; fall back to az CLI $retryTokenStr = $null try { $retryObj = Get-AzAccessToken -ResourceUrl "https://management.azure.com" -ErrorAction Stop $retryTokenStr = if ($retryObj.Token -is [System.Security.SecureString]) { [System.Net.NetworkCredential]::new("", $retryObj.Token).Password } else { $retryObj.Token } } catch { $retryTokenStr = az account get-access-token --resource https://management.azure.com --query accessToken -o tsv 2>$null } if (-not $retryTokenStr) { $retryTokenStr = $meTokenStr } $retryHeaders = @{ Authorization = "Bearer $retryTokenStr" } $retryStatus = $null try { $retryResp = Invoke-WebRequest -Uri $deleteUri -Method DELETE -Headers $retryHeaders -UseBasicParsing $retryStatus = [int]$retryResp.StatusCode } catch { if ($_.Exception.Response) { $retryStatus = [int]$_.Exception.Response.StatusCode } else { throw } } if ($retryStatus -in @(200, 204)) { Write-Host " [OK] Elevation removed." -ForegroundColor Green } else { Write-Warning " [WARN] Elevation removal returned status $retryStatus -- remove manually: Azure Portal > Properties > Access management for Azure resources." } } else { Write-Warning " [WARN] Elevation removal returned status $deleteStatus -- remove manually: Azure Portal > Properties > Access management for Azure resources." } } |