Private/Confirm-GraphConnection.ps1
|
function Confirm-GraphConnection { <# .SYNOPSIS Checks that there is a Microsoft Graph connection with the scopes the calling function needs, and returns the context - or writes an error saying exactly which Connect-MgGraph command to run, and returns $null. .DESCRIPTION The module never calls Connect-MgGraph itself, so it can run anywhere the caller controls the sign-in: interactively, with a certificate, or with a managed identity in an Azure Function or Automation runbook. A higher-privileged permission is accepted in place of the one asked for (e.g. Group.Read.All for GroupMember.Read.All). Delegated (signed-in user): a missing scope stops the run. App-only: a missing scope is a warning, because an app can be given an Entitlement Management role instead of the permission. .PARAMETER Scopes The scopes the calling function needs. Default: User.Read.All, EntitlementManagement.ReadWrite.All (Add-/Remove-SiSAccessPackageAssignment). .NOTES Internal helper. #> [CmdletBinding()] param( # Least privilege: each function checks only for what it uses. The # default is what Add-/Remove-SiSAccessPackageAssignment needs. [Parameter(Mandatory = $false)] [string[]]$Scopes = @( "User.Read.All", "EntitlementManagement.ReadWrite.All" ) ) $requiredScopes = @($Scopes | Where-Object { $_ } | Select-Object -Unique) $connectHint = "Connect-MgGraph -Scopes $($requiredScopes -join ', ')" # The module never signs in by itself. The caller decides how: interactive, # certificate, managed identity in an Azure Function... $context = Get-MgContext if (-not $context) { Write-Error -Message "Not connected to Microsoft Graph. Connect first, e.g.: $connectHint" -Category ConnectionError return $null } # A higher-privileged permission covers a scope too, so someone with # Group.Read.All isn't stopped for lacking GroupMember.Read.All $coveredBy = @{ 'User.Read.All' = @('User.ReadWrite.All', 'Directory.Read.All', 'Directory.ReadWrite.All') 'GroupMember.Read.All' = @('GroupMember.ReadWrite.All', 'Group.Read.All', 'Group.ReadWrite.All', 'Directory.Read.All', 'Directory.ReadWrite.All') 'EntitlementManagement.Read.All' = @('EntitlementManagement.ReadWrite.All') } $granted = @($context.Scopes) $missingScopes = @($requiredScopes | Where-Object { $accepted = @($_) + @($coveredBy[$_] | Where-Object { $_ }) -not @($accepted | Where-Object { $granted -contains $_ }) }) $who = if ($context.Account) { $context.Account } else { "app $($context.ClientId)" } if ($missingScopes.Count -gt 0) { if ("$($context.AuthType)" -eq 'AppOnly') { # An app can hold an Entitlement Management role (e.g. Access package # manager) instead of the permission - that never shows in the token Write-Warning "The app's token doesn't include: $($missingScopes -join ', '). Continuing - the app may have an Entitlement Management role instead. Graph rejects the calls if it has neither." } else { Write-Error -Message "Connected as $who, but the token is missing: $($missingScopes -join ', '). Reconnect with: $connectHint" -Category PermissionDenied return $null } } Write-Host "Using Microsoft Graph connection: $who" -ForegroundColor Green return $context } |