- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe to Topic
- Printer Friendly Page
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
Retirement of the Legacy Exchange Online Public Client ID (app ID a0c73c16-a7e3-4564-9a95-2bdf47383716)
I figured I'd make a new topic for this. Per Fridays announcement, the Exchange app we use to get accesstokens will cease to function/exist by March 31st.
January 2023 announcements - Partner Center | Microsoft Learn
As mentioned in the other topic the Exchange team added a -Accesstoken parameter in the final weeks of 2022 which made the following possible:
# Define Exchange Access Token splat
$exchangeAccessTokenSplat = @{
ApplicationId = 'a0c73c16-a7e3-4564-9a95-2bdf47383716' # Exchange Online app
RefreshToken = $ExchangeRefreshToken
Scopes = 'https://outlook.office365.com/.default'
Tenant = $TenantID # Customer tenant ID
}
# Get Exchange Online access token
$exoToken = New-PartnerAccessToken @exchangeAccessTokenSplat
# Connect to Exchange Online
Connect-ExchangeOnline -DelegatedOrganization $TenantID -AccessToken $exoToken.AccessToken
Given the availability of the -AccessToken parameter on the preview version I would expect there to be some new authentication flow to request the refresh token and access token.
@JanoschUlmer is there anything else we can do to put pressure on the topic?
cc: @ClaudioStallone @sansbacher @Leon-anspired @KoenHalfwerk
Solved! Go to Solution.
- Labels:
-
CSP
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
So... in hindsight this feels very obvious. But with some hints (thanks @KelvinTegelaar!) I've put together the following,
To add to the list of grants in New-PartnerCustomerApplicationConsent:
$ExchangeGrant = New-Object -TypeName Microsoft.Store.PartnerCenter.Models.ApplicationConsents.ApplicationGrant
$ExchangeGrant.EnterpriseApplicationId = "00000002-0000-0ff1-ce00-000000000000"
$ExchangeGrant.Scope = "Exchange.Manage"
Also add the same "Exchange.Manage" grant to the app in your own CSP tenant. (Use "Add a Permission" > "APIs my organization uses" > search for "Office 365 Exchange Online" or "00000002-0000-0ff1-ce00-000000000000" and add "Exchange.Manage").
After that you can use the following method to authenticate to EXO:
# Define ExchangeTokenSplat parameters
$ExchangeTokenSplat = @{
ApplicationId = $CSPappId # AppID in CSP tenant
Scopes = 'https://outlook.office365.com/.default'
ServicePrincipal = $true
Credential = (New-Object System.Management.Automation.PSCredential ($CSPappId, (ConvertTo-SecureString $CSPappSecret -AsPlainText -Force)))
RefreshToken = $PartnerCenterRefreshToken
Tenant = $TenantID # Customer TenantID
}
# Get $ExchangeToken
$ExchangeToken = New-PartnerAccessToken @ExchangeTokenSplat
# Connect to MgGraph
Connect-ExchangeOnline -DelegatedOrganization $TenantID -AccessToken $ExchangeToken.AccessToken
From some quick testing I can both request data and make changes so it feels like everything works.
So I guess my conclusion is that the folks writing the learn articles for ExchangeOnlineManagement just haven't considered the Partner angle at all?
Edit: So far I haven't seen a way to update existing PartnerCustomerApplicationConsents so I guess removing the svc principal in customer tenants and running New-PartnerCustomerApplicationConsent again is the only option?
Edit2: I went the way of connecting to the customer tenants and using New-MgOauth2PermissionGrant to add the permission grant.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
So after zero help from Microsoft. I managed to solve the issue myself. In the documentation: https://learn.microsoft.com/en-us/powershell/partnercenter/exchange-online-gdap-app?view=partnercenterps-3.0
It says to use this:
$token = New-PartnerAccessToken -ApplicationId $AppId -Scopes 'https://outlook.office365.com/.default' -ServicePrincipal -Credential $appcredential -Tenant $CustomerTenantId -RefreshToken $PartnerAccesstoken.refreshToken
Connect-ExchangeOnline -DelegatedOrganization $CustomerTenantId -AccessToken $token.AccessToken
For $CustomerTenantId I was using the customers tenant id, e.g. the guid. To fix my issue, I instead used the customers domain. e.g.
$token = New-PartnerAccessToken -ApplicationId $AppId -Scopes 'https://outlook.office365.com/.default' -ServicePrincipal -Credential $appcredential -Tenant "contoso.com" -RefreshToken $PartnerAccesstoken.refreshToken
Connect-ExchangeOnline -DelegatedOrganization "contoso.com" -AccessToken $token.AccessToken
Might be worth Microsoft updating their documentation to reflect this.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
@aconn21 I am so sorry you have been having troubles here. What is your support ticket number? I would like to try and escalate it for you.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
Thank you for sharing, this works for me!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
@aconn21 can you submit your feedback here? You never know, maybe it will help them take a look and make some changes! 😁
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
Sure, once you've given me the correct link 😂
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
I think that's the wrong link 😂
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
I can't imagine that's intended behavior.
By chance did you test different variations? (just the DelegatedOrganization as the domain or just the -tenant as the domain)?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
I've tested this now. With New-PartnerAccessToken you can use the tenant id or the tenant domain. With Connect-ExchangeOnline you have to use the tenant domain.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
For what its worth, I've since ran into the same issue when directly hitting the Exchange Online API. Requesting the token for it as the domain instead of the id doesn't make a difference for that.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
I was testing this myself, and it seems that it doesn't matter whether you use the Tenant ID or domain name in either of the token request or the Exchange Online request. The call succeeds randomly regardless. I tested the endpoint by repeatedly requesting, and had 22 successes out of 200 while trying to run `Get-InboxRule {mailboxname}`. That's a 90% failure rate. Really stand-up job from Microsoft as expected. I would have used a higher sample size, but the requests are so ungodly slow that I didn't want to wait any longer.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
Hi @calebstewart , I had the same problem: inconsistent access when connecting using the ExO V3 module, especially with any commands that tried to write/change/delete/create/modify anything! I would get errors like:
Write-ErrorMessage : Ex34AFDD|Microsoft.Exchange.Data.Directory.ADOperationException|An RPC error occurred while the member count of an address list was being read. The error is "Error 0x5 (Access is denied) from cli_NspiBind
or
or
Ex574388|Microsoft.Exchange.Configuration.Tasks.TaskException|The parent object for 'Parent Company Org Sharing Name' could not be found. Please check that childTenant.onmicrosoft.com\Federation exists.
I followed @aconn21 's suggestion to use the Customer's Primary Domain Name and that helps, but not entirely. Then I found a post indicating you HAD to use the Customer's .onmicrosoft.com domain - no matter what! And that fixed my issues.
Unfortunately it's NOT true that abccompany.com always has abccompany.onmicrosoft.com, and sometimes the Domain Name returned from either Get-PartnerCustomer or Get-AzureADContract is not the onmicrosoft.com domain. So here's my solution:
# Assumptions: $Customer is from Get-AzureADContract, $AzureADAppId is the ClientID of your Secure App in your CSP/Partner tenant, and $pcCreds is that ClientID + AppSecret, and of course $RefreshToken is your normal Refresh Token for the Secure App (not the one tied to the old ExO App ID, a0c7....3716)
# Also assumes you have added the 'Exchange.Manage' API Permission for 'Office 365 Exchange Online' to BOTH your Secure App AND to the list of Grants you create with New-PartnerCustomerApplicationConsent for each Customer
# Get an Access Token for the Customer. Using the Customer's TenantID (ContextId) is fine here.
$exTok = New-PartnerAccessToken -Tenant $Customer.CustomerContextId -RefreshToken $RefreshToken -ServicePrincipal -Scopes "https://outlook.office365.com/.default" -ApplicationId $AzureADAppId -Credential $pcCreds
# Get the Customer's .onmicrosoft.com domain (ignoring any .mail.onmicrosoft.com domains)
# Assumes you have already done the Connect-AzureAD bit for the Customer
# NOTE: this should really be an MS Graph call...
$onDomain = Get-AzureAdDomain | where Name -match '^(?!.*\.mail\.onmicrosoft\.com$).*onmicrosoft\.com$'
# Connect to ExO using V3 module, which must use the .onmicrsoft.com domain
Connect-ExchangeOnline -AccessToken $exTok.AccessToken -DelegatedOrganization $onDomain.Name -ShowBanner:$False -ShowProgress:$false
That seems to work in all my testing! And yes, migrating all my AzureAD code to MSGraph is next on my list! This does NOT appear to be mentioned in the official MS documentation, they just mention Customer Tenant ID (which I take to be either the GUID or the Primary Domain Name - the name that appears in the CSP Partner Center).
Big thanks to @rvdwegen for first posting this. I meant to comment earlier but I've been super busy with other stuff the last few months. Finally getting to this just before the deadline! Has @JanoschUlmer moved on? I wanted to also thank him for all his work! And to everyone else who has helped get this working!
--Saul
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
(Invoke-RestMethod -Method 'GET' -Headers $header -Uri "https://graph.microsoft.com/v1.0/domains?").value | Where-Object { $_.isInitial -eq $true }
Since the AzureAD module is also on its way out haha.
Unfortunately all the commands and endpoints that I'm aware of that you can hit from your CSP tenant context including Get-PartnerCustomer and Get-AzureADContract list the *default domain*, not the onmicrosoft domain. So the only way to always get the onmicrosoft is to go looking in the customer tenant itself. (this is where that benefit of on the fly context switching I mentioned comes into play).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
@rvdwegen I just found this same endpoint! 🙂
And I also found Invoke-GraphRequest - which is part of just the base "Microsoft.Graph.Authentication" module. With it I can avoid having to install all those auto-generated MgGraph commands (but still choose to use a few such as Microsoft.Graph.Users or Microsoft.Graph.Groups if I wanted). And it eliminates some of the boiler plate, so you can narrow that down to:
(Invoke-GraphRequest -Method GET -Uri '/v1.0/domains').value | where IsInitial
Since it knows the URL, and doesn't need to re-auth if you use Connect-MgGraph. For my usage I don't (currently) need to switch back and forth to my CSP tenant and the Customer's tenant, so I think this is a good compromise. The data comes back as a Hashtable, but it can be cast to a [PSCustomObject] if needed.
And yeah, I couldn't see any way to get this info from my tenant. But most of my scripts will involve connecting to both (AAD or now Graph) and Exch Online so it's not a big deal.
Now I just have to go through each script looking for any -AzureAD or -Msol commands and update them!.... (which, I know, I should have done ages ago...)
--Saul
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
fyi, last I tried there's some incompatibility between Microsoft.Graph.Authentication and 7.1/7.2 Automation runbooks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
@sansbacher yest @JanoschUlmer has moved on but you can find him on LinkedIn and I will forward this post on to him as well. So glad you got it up and working!! YAY!! 🔥
- Mark as New
- Bookmark
- Subscribe
- Mute
- Permalink
- Report Inappropriate Content
@rvdwegen : Thanks for providing this info - I have a call later today with some folks responsible for thism where I hope I can get some clarity on supported options, or options planned to be supported. The example of a working approch might help a lot here.
Note that as of today,the only approach officially documented is using the app-only authentication as called out in Exchange documentation: App-only authentication in Exchange Online PowerShell and Security & Compliance PowerShell | Microsoft Learn - but I hope your approach will also be considered supported.
