Workoho.Automation.Graph/Public/Connect-Auto_MgGraph.ps1
<# .SYNOPSIS Connects to Microsoft Graph and performs authorization checks. .DESCRIPTION This script connects to Microsoft Graph using the specified scopes and performs authorization checks to ensure that the required scopes are available. The script also creates the following environment variables so that other scripts can use them: - $env:MG_PRINCIPAL_TYPE: The type of the principal ('Delegated' or 'Application'). - $env:MG_PRINCIPAL_ID: The ID of the principal. - $env:MG_PRINCIPAL_DISPLAYNAME: The display name of the principal. This is in particular useful during local development when an interactive account is used, while in Azure Automation, a service principal is used. By using the environment variables, other scripts can easily determine the type of the principal and use the principal ID and display name without having to call Microsoft Graph again. .PARAMETER Scopes An array of Microsoft Graph scopes required for the script. .PARAMETER TenantId The ID of the tenant to connect to. .EXAMPLE PS> Connect-Auto_MgGraph -Scopes @('User.Read', 'Mail.Read') -TenantId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' Connects to Microsoft Graph using the specified scopes and the specified tenant ID. #> function Connect-Auto_MgGraph { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Required for device code authentication.')] [CmdletBinding()] Param( [Array]$Scopes, [string]$TenantId ) Write-Auto_FunctionBegin $MyInvocation -OnceOnly Import-Auto_Module @( @{ Name = 'Microsoft.Graph.Authentication'; MinimumVersion = '2.0'; MaximumVersion = '2.65535' } ) function Get-MgMissingScope ([Array]$Scopes) { $MissingScopes = [System.Collections.ArrayList]::new() foreach ($Scope in $Scopes) { if ($WhatIfPreference -and ($Scope -like '*Write*')) { Write-Verbose "[Connect-Auto_MgGraph]: - What If: Removed $Scope from required Microsoft Graph scopes" [void] $Script:Scopes.Remove($Scope) } elseif ($Scope -notin @((Get-MgContext).Scopes)) { [void] $MissingScopes.Add($Scope) } } return $MissingScopes } $params = @{ NoWelcome = $true ContextScope = 'Process' ErrorAction = 'Stop' } if ($TenantId) { if ( $TenantId -notmatch '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' -or $TenantId -eq '00000000-0000-0000-0000-000000000000' ) { Throw '[Connect-Auto_MgGraph]: - Invalid tenant ID. The tenant ID must be a valid GUID.' } $params.TenantId = $TenantId } if ( -Not (Get-MgContext) -or ( $null -ne $params.TenantId -and $params.TenantId -ne (Get-MgContext).TenantId ) ) { if ($IsAzureAutomationJob) { Write-Verbose '[Connect-Auto_MgGraph]: - Using system-assigned Managed Service Identity' $params.Identity = $true } elseif ($IsNonUserInteractive) { Throw '[Connect-Auto_MgGraph]: - Non-interactive mode is not supported for Microsoft Graph connection.' } elseif ($IsContainerized) { Write-Verbose '[Connect-Auto_MgGraph]: - Using device code authentication' $params.UseDeviceCode = $true if ($Scopes) { $params.Scopes = $Scopes } } else { Write-Verbose '[Connect-Auto_MgGraph]: - Using interactive sign in' if ($Scopes) { $params.Scopes = $Scopes } } try { if ($params.UseDeviceCode) { Write-Host "Please select the account you want to login with.`n" -ForegroundColor Yellow Write-Host -NoNewline "`e[1;37;44m[Login to Graph]`e[0m " Microsoft.Graph.Authentication\Connect-MgGraph @params | ForEach-Object { if ($_ -is [string] -and $_ -cmatch ' ([A-Z0-9]{9}) ') { $_ -replace $Matches[1], "`e[4m$($Matches[1])`e[24m" } else { $_ } } | Out-Host } else { Write-Information 'Connecting to Microsoft Graph ...' -InformationAction Continue Microsoft.Graph.Authentication\Connect-MgGraph @params 1> $null } } catch { Write-Error "Microsoft Graph connection error: $($_.Exception.Message)" -ErrorAction Stop exit } } $MissingScopes = Get-MgMissingScope -Scopes $Scopes if ($MissingScopes) { if ( $IsAzureAutomationJob -or (Get-MgContext).AuthType -ne 'Delegated' ) { Write-Error "Missing Microsoft Graph authorization scopes:`n`n$($MissingScopes -join "`n")" -ErrorAction Stop exit 1 } if ($Scopes) { $params.Scopes = $Scopes } try { Write-Information 'Missing scopes, re-connecting to Microsoft Graph ...' -InformationAction Continue if ($params.UseDeviceCode) { Write-Host "Please select the account you want to login with.`n" -ForegroundColor Yellow Write-Host -NoNewline "`e[1;37;44m[Login to Graph]`e[0m " Microsoft.Graph.Authentication\Connect-MgGraph @params | ForEach-Object { if ($_ -is [string] -and $_ -cmatch ' ([A-Z0-9]{9}) ') { $_ -replace $Matches[1], "`e[4m$($Matches[1])`e[24m" } else { $_ } } | Out-Host } else { Microsoft.Graph.Authentication\Connect-MgGraph @params 1> $null } } catch { Write-Error $_.Exception.Message -ErrorAction Stop exit } if ( -Not (Get-MgContext) -or (Get-MgMissingScope -Scopes $Scopes).Count -gt 0 ) { Write-Error "Missing Microsoft Graph authorization scopes:`n`n$($MissingScopes -join "`n")" -ErrorAction Stop exit } } if ( [string]::IsNullOrEmpty($env:MG_PRINCIPAL_ID) -or [string]::IsNullOrEmpty($env:MG_PRINCIPAL_DISPLAYNAME) ) { try { $Context = Get-MgContext -ErrorAction Stop -Verbose:$false -Debug:$false if ($Context.AuthType -eq 'Delegated') { [Environment]::SetEnvironmentVariable('MG_PRINCIPAL_TYPE', 'Delegated', 'Process') Write-Verbose "[Connect-Auto_MgGraph]: - Getting user details for $($Context.Account) ..." $Principal = Invoke-MgGraphRequest -Uri "/v1.0/users/$($Context.Account)?`$select=id,displayName" -ErrorAction Stop -Verbose:$false -Debug:$false } else { [Environment]::SetEnvironmentVariable('MG_PRINCIPAL_TYPE', 'Application', 'Process') Write-Verbose "[Connect-Auto_MgGraph]: - Getting service principal details for $($Context.ClientId) ..." $Principal = (Invoke-MgGraphRequest -Uri "/v1.0/servicePrincipals?`$select=id,displayName&`$filter=appId eq '$($Context.ClientId)'" -ErrorAction Stop -Verbose:$false -Debug:$false).Value[0] } Write-Verbose "[Connect-Auto_MgGraph]: - Setting environment MG_PRINCIPAL_ID to '$($Principal.Id)' and MG_PRINCIPAL_DISPLAYNAME to '$($Principal.DisplayName)' ..." [Environment]::SetEnvironmentVariable('MG_PRINCIPAL_ID', $Principal.Id, 'Process') [Environment]::SetEnvironmentVariable('MG_PRINCIPAL_DISPLAYNAME', $Principal.DisplayName, 'Process') } catch { Write-Error $_.Exception.Message -ErrorAction Stop exit } } Write-Auto_FunctionEnd $MyInvocation -OnceOnly } New-Alias -Name 'Connect-Auto_Graph' -Value 'Connect-Auto_MgGraph' -Force $ModuleMemberExport.Alias.Add('Connect-Auto_Graph') |