Public/Connect-NSPGraph.ps1
|
function Connect-NSPGraph { <# .SYNOPSIS Connects to Microsoft Graph, or validates an existing delegated context, with the required scopes. .DESCRIPTION Extracted from NSP-IntuneApps (Private\Connect-NSPGraph.ps1) so every NSP M365 tool shares one implementation. Pass -ClientId/-TenantId to authenticate as a registered app instead of the Microsoft Graph PowerShell SDK's default app. A pre-consented registration can sign in silently. -Connect only calls Connect-MgGraph when the existing session is insufficient: missing a scope, or authenticated as a different app or tenant than requested. Reconnecting unconditionally causes repeated sign-in prompts even when a good session already exists. Callers should always pass their tool's single, complete scope list. -UseDeviceCode requests the device-code flow instead of the Windows broker (WAM). The WAM popup can open behind other windows and time out, which surfaces as "User canceled authentication". Connect-MgGraph keeps one context per process, so this serves one tenant at a time. .EXAMPLE Connect-NSPGraph -Scopes 'Policy.Read.All', 'Group.Read.All' -Connect .EXAMPLE $context = Connect-NSPGraph -Scopes 'Policy.Read.All' -Optional #> [CmdletBinding()] param( [Parameter(Mandatory)][string[]]$Scopes, [switch]$Connect, [switch]$Optional, [string]$ClientId, [string]$TenantId, [switch]$UseDeviceCode ) if ($Connect) { if (-not (Get-Module -ListAvailable Microsoft.Graph.Authentication)) { if (-not (Get-Module -ListAvailable NSP.Bootstrap)) { throw 'Microsoft.Graph.Authentication is not installed. Install it, or install NSP.Bootstrap so it can be installed for you.' } Import-Module NSP.Bootstrap -ErrorAction Stop Install-NSPModule -Name Microsoft.Graph.Authentication -Scope CurrentUser } Import-Module Microsoft.Graph.Authentication -ErrorAction Stop $existingContext = if (Get-Command Get-MgContext -ErrorAction SilentlyContinue) { Get-MgContext } else { $null } $hasRequiredScopes = $existingContext -and (@($Scopes | Where-Object { $_ -notin @($existingContext.Scopes) })).Count -eq 0 $matchesRequestedApp = (-not $ClientId -or $existingContext.ClientId -eq $ClientId) -and (-not $TenantId -or $existingContext.TenantId -eq $TenantId) if (-not ($existingContext -and $hasRequiredScopes -and $matchesRequestedApp)) { $connectArgs = @{ Scopes = $Scopes; NoWelcome = $true } if ($ClientId) { $connectArgs.ClientId = $ClientId } if ($TenantId) { $connectArgs.TenantId = $TenantId } if ($UseDeviceCode) { $connectArgs.UseDeviceCode = $true } Connect-MgGraph @connectArgs | Out-Null } } $context = if (Get-Command Get-MgContext -ErrorAction SilentlyContinue) { Get-MgContext } else { $null } if (-not $context) { if ($Optional) { return $null } throw 'No Microsoft Graph context is available. Use -Connect for delegated interactive login.' } if (-not $Optional) { $missingScopes = @($Scopes | Where-Object { $_ -notin @($context.Scopes) }) if (@($missingScopes).Count -gt 0) { throw "The current Graph context lacks required scope(s): $($missingScopes -join ', '). Reconnect with -Connect." } } $context } |