Public/Connect-ExchangeOnline.ps1

function Connect-ExchangeOnline {
    [CmdletBinding()]
    param()

    DynamicParam {
        if (-not (Get-Module -Name ExchangeOnlineManagement -ErrorAction SilentlyContinue)) {
            Import-Module -Name ExchangeOnlineManagement -ErrorAction SilentlyContinue
        }
        $local:cmd = Get-Command -Name ExchangeOnlineManagement\Connect-ExchangeOnline -ErrorAction SilentlyContinue
        if ($local:cmd) {
            $local:dict = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
            foreach ($local:p in $local:cmd.Parameters.Values) {
                if ([System.Management.Automation.Cmdlet]::CommonParameters -contains $local:p.Name) { continue }
                $local:dict.Add($local:p.Name,
                    [System.Management.Automation.RuntimeDefinedParameter]::new(
                        $local:p.Name, $local:p.ParameterType, $local:p.Attributes))
            }
            return $local:dict
        }
    }

    process {
        if (-not (Get-Module -Name ExchangeOnlineManagement -ErrorAction SilentlyContinue)) {
            Import-Module -Name ExchangeOnlineManagement -ErrorAction SilentlyContinue
        }
        if (-not (Get-Command -Name ExchangeOnlineManagement\Connect-ExchangeOnline -ErrorAction SilentlyContinue)) {
            Write-Error -Message 'Cannot connect to Exchange Online - module not installed or not loading.'
            return
        }

        # Inject module defaults when caller did not supply them
        if (-not $PSBoundParameters.ContainsKey('ConnectionUri')) {
            $PSBoundParameters['ConnectionUri'] = $script:myOffice365Services['ConnectionEndpointUri']
        }
        if (-not $PSBoundParameters.ContainsKey('AzureADAuthorizationEndpointUri')) {
            $PSBoundParameters['AzureADAuthorizationEndpointUri'] = $script:myOffice365Services['AzureADAuthorizationEndpointUri']
        }
        if (-not $PSBoundParameters.ContainsKey('PSSessionOption')) {
            $PSBoundParameters['PSSessionOption'] = $script:myOffice365Services['SessionOptions']
        }

        # Credential handling — skip when modern/cert/app auth params were supplied
        If ( $PSBoundParameters.ContainsKey('UserPrincipalName') -or $PSBoundParameters.ContainsKey('Certificate') -or
            $PSBoundParameters.ContainsKey('CertificateFilePath') -or $PSBoundParameters.ContainsKey('CertificateThumbprint') -or
            $PSBoundParameters.ContainsKey('AppId')) {
            Write-Host ('Connecting to Exchange Online ..')
        }
        Else {
            If ( $PSBoundParameters.ContainsKey('Credential')) {
                Write-Host ('Connecting to Exchange Online using {0} ..' -f $PSBoundParameters['Credential'].UserName)
                $script:myOffice365Services['Office365Credential'] = $PSBoundParameters['Credential']
            }
            Else {
                # Ensure we have an account cached (MSAL) or credentials (legacy)
                If ( -not $script:myOffice365Services['Office365UPN'] -and -not $script:myOffice365Services['Office365Credential']) {
                    Get-Office365Credential
                }

                # Modern auth: acquire EXO-scoped token and pass via -AccessToken + -UserPrincipalName
                $local:exoToken = Get-Office365AccessToken -Scope 'https://outlook.office365.com/.default'
                If ($local:exoToken) {
                    $PSBoundParameters['AccessToken']       = ConvertTo-SecureString $local:exoToken -AsPlainText -Force
                    $PSBoundParameters['UserPrincipalName'] = $script:myOffice365Services['Office365UPN']
                    Write-Host ('Connecting to Exchange Online using {0} ..' -f $script:myOffice365Services['Office365UPN'])
                }
                ElseIf ( $script:myOffice365Services['Office365UPN']) {
                    # MSAL auth done but EXO scope unavailable — use UPN for WAM SSO
                    $PSBoundParameters['UserPrincipalName'] = $script:myOffice365Services['Office365UPN']
                    Write-Host ('Connecting to Exchange Online using {0} ..' -f $script:myOffice365Services['Office365UPN'])
                }
                ElseIf ( $script:myOffice365Services['Office365Credential']) {
                    # Legacy PSCredential path
                    Write-Host ('Connecting to Exchange Online using {0} ..' -f $script:myOffice365Services['Office365Credential'].UserName)
                    $PSBoundParameters['Credential'] = $script:myOffice365Services['Office365Credential']
                }
                Else {
                    Write-Host ('Connecting to Exchange Online ..')
                }
            }
        }

        $script:myOffice365Services['Session365'] = ExchangeOnlineManagement\Connect-ExchangeOnline @PSBoundParameters
        If ( $script:myOffice365Services['Session365']) {
            Import-PSSession -Session $script:myOffice365Services['Session365'] -AllowClobber
        }
    }
}