Public/Permissions/Invoke-365TuneRevokeAzure.ps1
|
function Invoke-365TuneRevokeAzure { <# .SYNOPSIS Revokes Reader permissions previously granted to the 365TUNE Enterprise App in Azure. .DESCRIPTION Removes Reader access from root scope "/" and AAD IAM scope "/providers/Microsoft.aadiam". Safe to re-run — exits cleanly if no assignments are found. Run from local PowerShell or Cloud Shell. Your account must have Global Administrator rights and "Access management for Azure resources" enabled in Entra ID > Properties. .EXAMPLE Invoke-365TuneRevokeAzure .NOTES Author : Metawise Consulting LLC Module : 365TUNE Version : 2.1.5 #> [CmdletBinding()] param( [switch]$SkipAuth ) $displayNameProd = "365TUNE - Security and Compliance" $displayNameBeta = "365TUNE - Security and Compliance - Beta" Write-Host "`n══════════════════════════════════════════════════════" -ForegroundColor Cyan Write-Host " 365TUNE — Revoke Azure Permissions" -ForegroundColor Cyan Write-Host "══════════════════════════════════════════════════════`n" -ForegroundColor Cyan # Step 1 — Check modules Write-Host "[1/5] Checking required modules..." -ForegroundColor Cyan foreach ($module in @("Az.Accounts", "Az.Resources")) { if (-not (Get-Module -ListAvailable -Name $module)) { Write-Host " Installing $module..." -ForegroundColor Yellow Install-Module -Name $module -Force -Scope CurrentUser -AllowClobber } } Import-Module Az.Accounts, Az.Resources Write-Host " ✅ Modules ready." -ForegroundColor Green # Step 2 — Authenticate Write-Host "`n[2/5] Authenticating..." -ForegroundColor Cyan $inCloudShell = ($env:ACC_CLOUD -eq "PROD") -or ($env:POWERSHELL_DISTRIBUTION_CHANNEL -like "*CloudShell*") -or ($env:AZUREPS_HOST_ENVIRONMENT -like "*cloud-shell*") if (-not $SkipAuth) { if ($inCloudShell) { Write-Host " Cloud Shell detected — using existing session." -ForegroundColor Gray } else { Disconnect-AzAccount -ErrorAction SilentlyContinue | Out-Null Connect-AzAccount -WarningAction SilentlyContinue | Out-Null } } $context = Get-AzContext if (-not $context) { throw "Not authenticated. Please try again." } Write-Host " Tenant : $($context.Tenant.Id)" -ForegroundColor Gray Write-Host " Account : $($context.Account.Id)" -ForegroundColor Gray Write-Host " ✅ Authenticated." -ForegroundColor Green # Step 3 — Fetch Service Principal via Graph API (avoids Az.MSGraph dependency issues) Write-Host "`n[3/5] Looking up 365TUNE Service Principal..." -ForegroundColor Cyan $graphTokenObj = Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com" if ($graphTokenObj.Token -is [System.Security.SecureString]) { $graphToken = [System.Net.NetworkCredential]::new("", $graphTokenObj.Token).Password } else { $graphToken = $graphTokenObj.Token } $graphHeaders = @{ Authorization = "Bearer $graphToken"; "Content-Type" = "application/json" } function Find-365TuneSP ($name) { $encoded = [Uri]::EscapeDataString("displayName eq '$name'") $response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=$encoded" -Headers $graphHeaders -Method GET $response.value | Select-Object -First 1 | Select-Object id, appId, displayName } $sp = Find-365TuneSP $displayNameProd if (-not $sp) { Write-Host " '$displayNameProd' not found — trying Beta..." -ForegroundColor Yellow $sp = Find-365TuneSP $displayNameBeta } if (-not $sp) { throw "Service Principal not found. Tried '$displayNameProd' and '$displayNameBeta'. Ensure the 365TUNE app has been consented to in this tenant." } $displayName = $sp.displayName $servicePrincipalId = $sp.id Write-Host " Object ID : $servicePrincipalId" # Step 4 — Pre-flight check Write-Host "`n[4/5] Checking for existing assignments..." -ForegroundColor Cyan $existing = Invoke-AzRestMethod ` -Path "/providers/Microsoft.aadiam/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&`$filter=principalId eq '$servicePrincipalId'" ` -Method GET $existingValues = ($existing.Content | ConvertFrom-Json).value if ($existingValues.Count -eq 0) { Write-Host "`n══════════════════════════════════════════════════════" -ForegroundColor Cyan Write-Host " No assignments found — nothing to revoke. ✅" -ForegroundColor Green Write-Host "══════════════════════════════════════════════════════`n" -ForegroundColor Cyan return } Write-Host " Found $($existingValues.Count) assignment(s) — proceeding with removal." -ForegroundColor Yellow # Step 5 — Elevate and remove Write-Host "`n[5/5] Elevating and removing Reader permissions..." -ForegroundColor Cyan Invoke-365TuneElevation try { Remove-AzRoleAssignment ` -ObjectId $servicePrincipalId ` -Scope "/" ` -RoleDefinitionName "Reader" ` -SkipClientSideScopeValidation ` -ErrorAction Stop Write-Host " ✅ Reader removed from '/'" -ForegroundColor Green } catch { if ($_.Exception.Message -like "*does not exist*" -or $_.Exception.Message -like "*NotFound*" -or $_.Exception.Message -like "*does not map*" -or $_.Exception.Message -like "*Forbidden*") { Write-Warning " ⚠️ Reader at '/' not found — already removed" } else { throw } } $aadIamValues = $existingValues | Where-Object { $_.properties.scope -eq "/providers/Microsoft.aadiam" } if ($aadIamValues) { foreach ($assignment in $aadIamValues) { $del = Invoke-AzRestMethod ` -Path "/providers/Microsoft.aadiam/providers/Microsoft.Authorization/roleAssignments/$($assignment.name)?api-version=2022-04-01" ` -Method DELETE if ($del.StatusCode -in @(200, 204)) { Write-Host " ✅ Reader removed from '/providers/Microsoft.aadiam'" -ForegroundColor Green } else { Write-Warning " ⚠️ aadiam removal returned status $($del.StatusCode)" } } } else { Write-Warning " ⚠️ Reader at '/providers/Microsoft.aadiam' not found — already removed" } $remaining = Invoke-AzRestMethod ` -Path "/providers/Microsoft.aadiam/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&`$filter=principalId eq '$servicePrincipalId'" ` -Method GET $remainingValues = ($remaining.Content | ConvertFrom-Json).value if ($remainingValues.Count -eq 0) { Write-Host " ✅ Verified — no assignments remain." -ForegroundColor Green } else { Write-Warning " ⚠️ $($remainingValues.Count) assignment(s) still remain — check Azure Portal." } Remove-365TuneElevation Write-Host "`n══════════════════════════════════════════════════════" -ForegroundColor Cyan Write-Host " 365TUNE Azure permissions revoked. ✅" -ForegroundColor Green Write-Host " Tenant : $($context.Tenant.Id)" -ForegroundColor Green Write-Host " Account : $($context.Account.Id)" -ForegroundColor Green Write-Host "══════════════════════════════════════════════════════`n" -ForegroundColor Cyan } |