Connect to Exchange Online PowerShell with Connect-ExchangeOnline cmdlet and a service principal with client secret

Connecting to Azure with a service principal with client secret is quite simple - you can get the credentials and pass them to the Connect-AzAccount cmdlet and specify the -ServicePrincipal parameter.  

$credential = Get-Credential
Connect-AzAccount -Credential $credential -TenantId "your-tenant-identifier" -ServicePrincipal 


Connecting to Exchange Online however is somewhat more problematic - there is no "ServicePrincipal" parameter and using the cmdlet with just a credential and organization shows the following error.

The user is not recognized as a managed user, or a federated user.Azure AD was not able to identify the IdP that needs to process the user U/P: Wrong username

It looks like Microsoft haven't implemented the ability to use client secrets as they're not as secure as certificates (and the Connect-ExchangeOnline cmdlet has good support for certficiates).

Microsoft doesn't appear to have implemented any of their online services with any kind of consistency.

The cmdlets are all using the Microsoft Authentication Library (MSAL) under the hood which in turn creates and posts a message to the login.microsoftonline.com to generate a JWT (JSON Web Token) also seen as a bearer token so you can do this directly yourself bypassing the cmdlets and MSAL.


$clientId = "your-service-principal-app-identifier"
$clientSecret = "your-client-secret" 
$tenantId = "your-tenant-id" 

$tokenBody = @{ 

    Grant_Type    = "client_credentials" 

    Scope         = "https://outlook.office365.com/.default"

    Client_Id     = $clientId 

    Client_Secret = $clientSecret 

}  

 

$tokenResponse = Invoke-RestMethod -Uri https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token -Method POST -Body $tokenBody 

You can then pass this bearer (JWT) token to the Connect-ExchangeOnline cmdlet using the AccessToken parameter

Connect-ExchangeOnline -AccessToken $tokenResponse.access_token -Organization "your-organization-name.onmicrosoft.com"


You still need to use the Exchange PowerShell cmdlets rather than access the REST API directly because 

  • The REST API isn't public 

  • The REST API is in BETA (and presumable the PowerShell cmdlets will switch to production when a production API is available)

  • The REST API is not a proper REST API - if you look at the endpoint the say the Get-AddressList cmdlet is called you'd expect something like GET /AddressLists - however all the cmdlets that don't start with Get-EXO* actually sent a command to this one /InvokeCommand address

    https://outlook.office365.com/adminapi/beta/identifier/InvokeCommand

    The inside of the message posted to this address is some JSON that includes the cmdlet being run. This is pretty poor.

    {"CmdletInput":{"CmdletName":"Get-AddressList","Parameters":{}}}


While you're here why not check out our Exchange audit and documentation tool?





Comments

Popular posts from this blog

Windows Server 2016, 2019, 2022, Windows 10 and Windows 11: Date and time "Some settings are managed by your organization".

TFTPD32 or TFTPD64 reports Bind error 10013 An attempt was made to access a socket in a way forbidden by its access permissions.

Windows Server 2019 desktop icons such as My Computer, Windows cannot access the specified device, path, or file.