Public/Connect-SPO.ps1
|
function Connect-SPO { if (-not (Get-Module -Name Microsoft.Online.SharePoint.PowerShell)) { Import-Module -Name Microsoft.Online.SharePoint.PowerShell -ErrorAction SilentlyContinue } if (Get-Command -Name Connect-SPOService -ErrorAction SilentlyContinue) { if ( -not $script:myOffice365Services['Office365UPN'] -and -not $script:myOffice365Services['Office365Credential']) { if ($script:myOffice365Services['NoAutoConnect']) { Write-Error 'No credentials cached. Run Get-Office365Credential first or supply credentials explicitly.' return } Get-Office365Credential } $local:upn = if ($script:myOffice365Services['Office365UPN']) { $script:myOffice365Services['Office365UPN'] } else { $script:myOffice365Services['Office365Credential'].UserName } Write-Host 'Connecting to SharePoint Online ..' # Resolve tenant name for the admin URL when not already cached. # Priority: onmicrosoft.com UPN → Graph domains API → error if (-not $script:myOffice365Services['Office365Tenant']) { if ($local:upn -like '*@*.onmicrosoft.com') { $script:myOffice365Services['Office365Tenant'] = ($local:upn -split '@')[1] -replace '\.onmicrosoft\.com$', '' } else { # Resolve the initial domain (*.onmicrosoft.com) from Graph using the cached MSAL token. # Fetch all domains and filter client-side — avoids OData advanced query permission requirements. $local:graphToken = Get-Office365AccessToken -Scope 'https://graph.microsoft.com/.default' if ($local:graphToken) { try { $local:domainResp = Invoke-RestMethod ` -Uri 'https://graph.microsoft.com/v1.0/domains' ` -Headers @{ Authorization = "Bearer $local:graphToken" } ` -ErrorAction Stop $local:initialDomain = $local:domainResp.value | Where-Object { $_.isInitial -eq $true } | Select-Object -First 1 -ExpandProperty id if ($local:initialDomain -match '^(.+)\.onmicrosoft\.com$') { $script:myOffice365Services['Office365Tenant'] = $Matches[1] } } catch { Write-Verbose ('Graph domain lookup failed: {0}' -f $_.Exception.Message) } } } } if (-not $script:myOffice365Services['Office365Tenant']) { Get-Office365Tenant } if (-not $script:myOffice365Services['Office365Tenant']) { Write-Error 'Cannot determine SharePoint Online tenant name. Run Get-Office365Tenant.' return } $local:adminUrl = 'https://{0}-admin.sharepoint.com' -f $script:myOffice365Services['Office365Tenant'] $local:Parms = @{ Url = $local:adminUrl } if ($script:myOffice365Services['SharePointRegion']) { $local:Parms['Region'] = $script:myOffice365Services['SharePointRegion'] } # Prefer MSAL token injection; fall back to PSCredential so SPO triggers its own # modern auth flow (MFA prompt only) rather than failing with no auth context. # SPO's OAuth resource is the tenant root URL, not the admin URL. $local:spoToken = Get-Office365AccessToken -Scope ('https://{0}.sharepoint.com/.default' -f $script:myOffice365Services['Office365Tenant']) $local:spoCmd = Get-Command -Name Connect-SPOService -ErrorAction SilentlyContinue $local:connected = $false # Auth priority 1: inject MSAL access token when the parameter is available. if ($local:spoToken -and $local:spoCmd.Parameters.ContainsKey('AccessToken')) { try { Connect-SPOService @local:Parms -AccessToken $local:spoToken -ErrorAction Stop $local:connected = $true } catch { Write-Verbose ('SPO access-token auth failed, trying credential fallback: {0}' -f $_.Exception.Message) } } # Auth priority 2: PSCredential, or let SPO trigger its own modern-auth browser prompt. if (-not $local:connected) { if ($script:myOffice365Services['Office365Credential'] -and $local:spoCmd.Parameters.ContainsKey('Credential')) { $local:Parms['Credential'] = $script:myOffice365Services['Office365Credential'] } try { Connect-SPOService @local:Parms -ErrorAction Stop $local:connected = $true } catch { Write-Error ('Cannot connect to SharePoint Online: {0}' -f $_.Exception.Message) } } if ($local:connected) { $script:myOffice365Services['ConnectedSPO'] = $true } } else { Write-Error -Message 'Cannot connect to SharePoint Online - module not installed or not loading.' } } |