The 1-2-3 of Exchange Online Certificate Based Authentication for PowerShell

Exchange Online CBA One Way to Replace Basic Authentication

The writing team for Office 365 for IT Pros very much appreciate the work done by our redoubtable technical editor, Vasil Michev, when he probes and questions text in the book chapters. It’s nice to get some of our own back when Vasil commits himself to print.

Take his recent article on certificate-based authentication for Exchange Online PowerShell. There’s a lot of good stuff here, but Vasil’s mind runs at such a fast rate that he sometimes omits details when he explains something that end up stopping people being able to master a topic. This is when people with slower CPUs, like me, step in to ask irritating questions. In this instance, the topic is important. Microsoft will remove support for basic authentication for PowerShell in mid-2021. Any batch job that uses PowerShell to interact with Exchange Online needs to be upgraded before this point to keep working.

Three Steps to Certificate-Based Authentication

The solution is to move to the Exchange Online Management module (known as EXO V2) and use certificate-based authentication. Microsoft has a preview of the module available to allow tenants to test how this works. The mechanism might seem complex when you first read Microsoft’s instructions, but it can be boiled down to three points:

  • An app registered with Azure AD. The app is the entry point to Exchange Online PowerShell because it is granted permission (as a service principal) to perform administrative actions. You might have used apps registered with Azure AD to interact with the Microsoft Graph.
  • A self-signed X.509 certificate used to authenticate the application against Azure AD when access is needed.
  • Office 365 is a massive multi-tenant environment, so you need to tell Exchange Online which tenant to connect to be able to run PowerShell in a background process.

Creating the App and Assigning Permissions and Role

Creating the app and assigning the necessary Exchange.AsManageApp permission and administrative role is quickly done in the Azure AD portal using the steps outlined in Vasil’s article. These are one-time operations that don’t need to be automated in code. However, it’s worth noting that role assignments can be made with PowerShell. It’s useful to know how to do this because you might use the technique in future to assign a role to a user.

This code fetches the object identifier for the app (it’s called “Exo Background process”) and assigns the Exchange Service Administrator directory role to the app.

$ExoAppSp = (Get-AzureADServicePrincipal -Filter "DisplayName eq 'Exo Background Process'").ObjectId
$ExoRoleId = (Get-AzureADDirectoryRole | ? {$_.DisplayName -eq "Exchange Service Administrator"}).ObjectId
Add-AzureADDirectoryRoleMember -ObjectId $ExoRoleId -RefObjectId $ExoAppSp

The same approach is used to assign a role to a user. The difference is that the object identifier for the user is fetched with the Get-AzureUserAD cmdlet. This example shows how to assign the Global Reader role to a user.

$UserId = (Get-AzureADUser -ObjectId Oisin.Johnston@Office365itpros.com).ObjectId
$RoleId = (Get-AzureADDirectoryRole | ?{$_.DisplayName -eq "Global Reader"}).ObjectId
Add-AzureADDirectoryRoleMember -ObjectId $RoleId -RefObjectId $UserId

With that diversion taken care of, we can proceed to obtaining a self-signed X.509 certificate, which is where people sometimes become stuck (I did).

Creating the Certificate

You need to upload a suitable X.509 self-signed certificate to the Azure AD app to create an association between the two. Certificates are not the easiest of objects to work with, but in this case it’s straightforward.

If you don’t have a suitable certificate to hand, you must generate one. Vasil was short on detail on this point (until I asked him how he had generated a certificate). Microsoft recommends using either the Create-SelfSignedCertificate.ps1 script or the MakeCert command-line utility. These are certainly viable options, but the easiest way is to run the New-SelfSignedCertificate cmdlet using a command like this:

New-SelfSignedCertificate -Subject "Exo Background Process" -CertStoreLocation "cert:\CurrentUser\My" -KeySpec KeyExchange -FriendlyName "For EXO V2 Background Jobs"

This command creates a certificate valid for a year in the personal store of the user, which is fine for testing (Figure 1). Obviously, in a production environment, you’d create the certificate in the personal store of the account that will be used to run batch jobs.

 self-signed certificate created with the New-SelfSignedCertificate cmdlet

Exchange Online CBA
Figure 1: A self-signed certificate created with the New-SelfSignedCertificate cmdlet

To associate the certificate with the app, export the certificate as a DER-encoded binary X.509 file (Figure 2). You can call the file anything you like, but you should give it a .CER extension.

Exporting the self-signed certificate
Figure 2: Exporting the self-signed certificate

Finally, upload the exported file to the app in the Azure AD portal. This will generate a thumbprint that you need to note (Figure 3).

Importing the self-signed certificate into the registered Azure AD app
Figure 3: Importing the self-signed certificate into the registered Azure AD app

Bringing it Together with Exchange Online CBA

After setting up the app, the three vital pieces of information we need to connect are:

  • The AppId of the application you created.
  • The thumbprint of the certificate loaded into the app.
  • The service domain for your tenant (like tenant.onmicrosoft.com).

With these values, you can connect to Exchange Online using certificate-based authentication with a command like:

Connect-ExchangeOnline -CertificateThumbprint "40EED7993F65D8CF13D5ABAC87F3AAD307012D22" -AppId "b83c46c6-044e-40e5-929c-634f80045a11" -ShowBanner:$false -Organization tenant.onmicrosoft.com

There are obviously more complications that await the unwary along the way, but this is enough to connect and play with Exchange Online PowerShell in batch jobs. Vasil’s post contains a lot of detail and there will be more articles and guidance published as we approach the deadline for basic authentication to disappear next year, which is a good reason to subscribe to a reliable source of information like the Office 365 for IT Pros eBook.

18 Replies to “The 1-2-3 of Exchange Online Certificate Based Authentication for PowerShell”

  1. I found I needed to enable the Exchange Service Administrator Role.

    $ExoAppSp = (Get-AzureADServicePrincipal -Filter “DisplayName eq ””).ObjectId

    $ExoRoleId = (Get-AzureADDirectoryRole | ? {$_.DisplayName -eq “Exchange Service Administrator”}).ObjectId

    If ($ExoRoleId –eq “”) {

    $RoleTemplate = Get-AzureADDirectoryRoleTemplate | Where-Object {$_.DisplayName -eq “Exchange Service Administrator”}

    Enable-AzureADDirectoryRole -RoleTemplateId $RoleTemplate.ObjectId

    $ExoRoleId = (Get-AzureADDirectoryRole | ? {$_.DisplayName -eq “Exchange Service Administrator”}).ObjectId

    }

    Add-AzureADDirectoryRoleMember -ObjectId $ExoRoleId -RefObjectId $ExoAppSp

    1. What are you trying to do with this command?

      $ExoAppSp = (Get-AzureADServicePrincipal -Filter “DisplayName eq ””).ObjectId

  2. Hi Tony,

    I trying to grant the Exchange.AsManageApp permission but this option seems to be missing from the Supported legacy APIs.
    I did assign this role before, but seems is not be available. Has there been any changes recently with regards to this ?

  3. Please ignore, I found out to my surprise that now you will need to search it within the “APIs my organization uses” by the name : Office 365 Exchange Online. Than, you will be able to assign the necessary permission afterwards.

    1. I’ve been looking for this tidbit for 3 days. You have to go to the “APIs my organization uses” section and you can’t find it by searching for “Exchange” because that returns nothing for some reason. Search for “Office 365” and it’s in “Office 365 Exchange Online”. It’s no wonder this has been delayed so long.

  4. Is there a way to specify ?proxyMethod=RPS with this connection method? In the past I’ve needed this for Set-UserPhoto to work and it seems to error out when I add the -connectionuri with RPS.

    1. I shouldn’t think so (but haven’t tested). This is designed to work with the Exchange Online management module, not the older remote PowerShell connections.

      1. As Navin Gupta (one of the program managers for the Exchange Online PowerShell module) notes, there is a DCR (design change request) to include Set-UserPhoto in the scope for CBA. I guess we shall have to wait for the process to work through. This isn’t uncommon because there are hundreds of cmdlets in the EXO set, each of which has to be validated against CBA.

  5. I was just informed by our unified support contact that the DCR request for supporting set-userphoto with CBA has been denied.

    Not sure how to really react to the message from MS on getting rid of legacy auth, while not supporting CBA for automation scenarios.

      1. I didn’t get any details directly from the product team, but just a message relayed through support as below
        —–
        unfortunately, we were informed that the engineering team will no longer make the investment in this feature. The decision was took after an internal analysis on the feature was performed.
        —–

  6. Done all the stuff, i receive “Access Denied” while trying to connect with ps . Certificate is ok, permissions also … any idea ?

  7. It seems that Vasil’s post is no longer on the Quadrotech blog. Are you aware of an alternate URL to try?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.