Public/Connect-SCC.ps1

function Connect-SCC {
    if (-not (Get-Module -Name ExchangeOnlineManagement -ErrorAction SilentlyContinue)) {
        Import-Module -Name ExchangeOnlineManagement -ErrorAction SilentlyContinue
    }
    if (Get-Command -Name ExchangeOnlineManagement\Connect-IPPSSession -ErrorAction SilentlyContinue) {
        # Ensure we have an account cached (MSAL) or credentials (legacy)
        if ( -not $script:myOffice365Services['Office365UPN'] -and -not $script:myOffice365Services['Office365Credential']) {
            Get-Office365Credential
        }

        Write-Host 'Connecting to Security & Compliance Center ..'

        # Same REST-mode logic as Connect-EXO: use ExchangeEnvironmentName for sovereign clouds,
        # omit ConnectionUri for O365Default so EOM v3 stays in REST mode (enables delegated AccessToken).
        $local:connectParams = @{
            PSSessionOption = $script:myOffice365Services['SessionOptions']
        }
        $local:eomEnv = $script:myOffice365Services['EOMEnvironmentName']
        if ($local:eomEnv -and $local:eomEnv -ne 'O365Default') {
            $local:connectParams['ExchangeEnvironmentName'] = $local:eomEnv
        }
        elseif (-not $local:eomEnv -and $script:myOffice365Services['SCCConnectionEndpointUri']) {
            # AzurePPE or unknown: use legacy ConnectionUri
            $local:connectParams['ConnectionUri'] = $script:myOffice365Services['SCCConnectionEndpointUri']
            $local:connectParams['AzureADAuthorizationEndpointUri'] = $script:myOffice365Services['AzureADAuthorizationEndpointUri']
        }
        # O365Default: omit both — EOM v3 auto-discovers the SCC REST endpoint

        # Pass UPN so EOM's own internal MSAL acquires the SCC token silently via WAM SSO.
        # External MSAL clients cannot request EXO/SCC tokens — AAD blocks first-party to
        # first-party token requests from external apps (AADSTS65002).
        if ($script:myOffice365Services['Office365UPN']) {
            $local:connectParams['UserPrincipalName'] = $script:myOffice365Services['Office365UPN']
        }
        elseif ($script:myOffice365Services['Office365Credential']) {
            $local:connectParams['UserPrincipalName'] = $script:myOffice365Services['Office365Credential'].UserName
        }

        try {
            $script:myOffice365Services['SessionCC'] = Connect-IPPSSession @local:connectParams
        }
        catch {
            # WAM (Windows Web Account Manager) broker requires a native window handle that
            # PowerShell console and terminal hosts never supply, causing a timeout.
            # Fall back to device code flow which only needs the console.
            if ($_.Exception.Message -like '*Operation did not start in the allotted time*' -or
                $_.Exception.Message -like '*Error Acquiring Token*') {
                Write-Warning 'WAM broker timed out — retrying with device code flow (check console for URL + code) ..'
                $local:connectParams.Remove('UserPrincipalName') | Out-Null
                $local:connectParams['Device'] = $true
                $script:myOffice365Services['SessionCC'] = Connect-IPPSSession @local:connectParams
            }
            else { throw }
        }
        if ( $script:myOffice365Services['SessionCC'] ) {
            Import-PSSession -Session $script:myOffice365Services['SessionCC'] -AllowClobber
        }
    }
    else {
        Write-Error -Message 'Cannot connect to Security & Compliance Center - module not installed or not loading.'
    }
}