Hero Banner

Secure Application Model

Learn and ask questions on how to implement secure application model

Level 4 Contributor

MS Graph and Partner GDAP - access customer tenant via graph

I am trying to get some scripting ready for Granular Delegated Admin permissions that we are all forced to change to this year from DAP and really struggling to work out how to get this working to access customer tenants.

I have created an app in our CSP tenant with relevant permissions. Has relevant graph permissions (like directory.read.all) and partner center user impersonation.

Global admin has granted consent.

I can generate access tokens and connect to the graph for our own tenant. Along with querying contracts etc to get a list of all our customer tenants etc. However it seems impossible to connect to a customer MSGraph when using GDAP.

For a tenant that is still on DAP, I can use

https://login.microsoftonline.com/<customer tenant ID>/oauth2/v2.0/token

To get an access token and then the graph will respond their tenant.

But if I do the same for a tenant that has GDAP, I get:

"error": {
"code": "Authorization_IdentityNotFound",
"message": "The identity of the calling application could not be established.",
"innerError": {
"date": "2022-05-30T11:24:11",
"request-id": "aa4d75a5-03ed-4f5a-811d-e09e63e346ed",
"client-request-id": "aa4d75a5-03ed-4f5a-811d-e09e63e346ed"


The Azuread App service principal is in the group which has been given access to the tenant.
User management role so can access the msgraph to get a list of users. But just get the above error.

Struggling to find anything online and not sure where to go from here.

Hope someone has some insight into how to make this work.

Level 6 Contributor

Hello @Leon-anspired@EliK@KoenHalfwerk@Glenndsq 


this week at the "CSP Security Updates and Q&A Session" the solution was presented as already explained by @JanoschUlmer here.

So this is now the Final solution to continue to access customer environments using Secure Application Model.


Here the further information:
Recording from the session


Documentation around the new variant for Secure Application Model


Registration for the "CSP Security Updates and Q&A Sessions"

View solution in original post

Level 5 Contributor

The reasons for going full REST API are faily simple. The graph module is auto generated so *a lot* of the cmdlets don't properly pipe into each other and behavior can be very inconsistent once you move beyond the basic Get-MgUser type commands. The documentation also tends to be moderately better on the REST API side vs the module side.


And of course the Partner Center module itself hasn't seen an update in years and after the end of this month will even contain deprecated functionality in the form of the -module param on New-PartnerAccessToken.


It also makes it easier to switch authentication contexts. There are scripts where I need information from my own tenant while I'm running through a customer tenant, instead of calling Connect-MgGraph twice to switch I can just keep different $header objects in memory and switch on the fly for a singular API call.


Another benefit is that you don't get stuck on PS5.1 because of module incompatibility.


Currently the way I auth looks like this



function Get-MicrosoftToken {
        # tenant Id

        # Scope
        [string]$Scope = 'https://graph.microsoft.com/.default',

        # ApplicationID

        # ApplicationSecret

        # RefreshToken

    if ($tenantId) {
        $Uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
    else {
        $Uri = "https://login.microsoftonline.com/common/oauth2/v2.0/token"  

    # Define the parameters for the token request
    $Body = @{
        client_id       = $ApplicationID
        client_secret   = $ApplicationSecret
        scope           = $Scope
        refresh_token   = $RefreshToken
        grant_type      = 'refresh_token'    

    $Params = @{
        Uri = $Uri
        Method = 'POST'
        Body = $Body
        ContentType = 'application/x-www-form-urlencoded'
        UseBasicParsing = $true

    try {
        $AuthResponse = (Invoke-WebRequest @Params).Content | ConvertFrom-Json
    } catch {
        Write-Error "Authentication Error Occured $_"

    return $AuthResponse

$commonTokenSplat = @{
    ApplicationID = $SAMAppId 
    ApplicationSecret = $SAMAppSecret 
    RefreshToken = $PartnerCenterRefreshToken

    if ($partnertoken = (Get-MicrosoftToken @commonTokenSplat -Tenantid '' -Scope "graph, partner center, etc").Access_Token) {
        $partnerheader = @{
            Authorization = 'bearer {0}' -f $partnertoken
            Accept        = "application/json"
    } else {
        throw "Unable to authenticate to fill in what is appropriate"


the $header object name will change depending on if I need to do the previously mentioned context switching

Level 5 Contributor

I expect I'm making a stupid mistake somewhere.

I have:

A test tenant

Management app that exists in our own tenant is consented in the test tenant using New-PartnerCustomerApplicationConsent with the above listed grants and more

Management app is a member of a group that has been added as Global Admin through the GDAP relationship with the test tenant

When I then auth to the test tenant using Connect-AzureAD with the appid and cert for the app I run into:

Code: Authorization_RequestDenied

Message: Insufficient privileges to complete the operation.

Level 5 Contributor

@KelvinTegelaar @sansbacher @ClaudioStallone I figured this would be interesting for you guys too, Microsoft added a -AccessToken parameter 7 days ago to the latest preview version of the ExchangeOnlineManagement module (3.1.0-Preview2).

I can confirm that the following works:

# 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
Level 6 Contributor

Atrocious documentation as usual:


Here's what you need, and here's a sample, but we aren't telling you any details about it...  I'm trying desperately to use my GDAP creds to script Exchange Shared Mailboxes to no avail. I've validated I authenticated properly in the Audit logs, but the only thing PowerShell throws me is:

+ throw $_.Exception;
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], UnauthorizedAccessException
+ FullyQualifiedErrorId : UnAuthorized


Is this related to being unable to access Exchange now also?  I am using the latest v3 and -AccessToken auth, but I'm seeing something about certificates required for Exchange now only? I've traced the error and it's on Getting the EXO Banner in the actual PSM (v.3.1.0)

$BannerContent = Get-EXOBanner -ConnectionContext:$ConnectionContext -IsRPSSession:$UseRPSSession.IsPresent
This is failing?  I shouldn't need to be in the module files themselves, should I?

Edit: I added 'Exchange.Manage' and it got further (got the banner), but it's still not doing what I need..
Level 5 Contributor

Solved: Re: Retirement of the Legacy Exchange Online Publi... - Microsoft Partner Community

You need to add Exchange.Manage on both sides (your app, and the consented svc principal of your app in the customer tenant)

Level 5 Contributor

Did you add Exchange.Manage on both your side and the customer side (the enterprise app/svc principal in the customer tenant)?

Level 6 Contributor

Thanks @rvdwegen , that's great!


Because as far as I know the current supported method (the Secure App Model using New-PSSession) will not be available after June 2023 as MS is blocking the RPS Protocol and we'll have to move to the new EXO V3 PowerShell module (using Connect-ExchangeOnline).

[note, that some of the comments (from GruberMaGruberMa) in the second link indicate that EXO V3 is lacking some features, so we'll have to check scripts carefully]


I just noticed that the main Exch online PS page under the third bullet point about "Exchange Online PowerShell for unattended scripts using AzureAD applications" it directs us to a recently updated page on App-only auth for unattended scripts that at the end of the purple block of text mentions "Delegated scenarios" which used to say "App-only auth does not support delegation" (from the WayBackMachine) and pointed us to the current supported method using the Secure App Model (that I linked above)...


...But NOW says "Delegated scenarios are supported in Exchange Online using multi-tenant applications." However the page does NOT mention the -AccessToken switch! I read the (new, updated article Jan 3, 2023) article and it mentions certificates, even under app-only.


So this makes me think it's a mistake, we're not currently using any certs to access our customer tenants using the Secure App Model. And Roel's method in the EXO V3.1-preview using the -AccessToken switch makes much more sense.


@JanoschUlmer : do you happen to have any info on this you can share? I assume things will be updated after EXO V3.1 comes out (I don't really like the idea of using preview modules in production scripts anyway), which I assume will be before June 2023. I'm not going to update things yet, but I assume I'll have to soon.


Also, how/where should we be keeping up to date on all this? DAP to GDAP was mentioned in the Partner Center announcements. But the Oct GDAP change to the new Grant/Consent method I only found out from this forum, same for the likely change to -AccessToken for EXO V3. If I wasn't subscribed to this forum my scripts would have all just stopped working. Is there an official place where all this is posted and documented? (this GDAP October Consent/Grant stuff isn't anywhere I've seen) Or is this forum the only place?





@sansbacher : Currently involved in a couple of discussions on this (or trying to get involved since it is not my call and not my role to make decisions on that).

I agree with @rvdwegen's comment that it would be great to have some better/formal communication what is the roadmap with Secure app model and GDAP, since I agree as well that what was shared is more an ad-hoc effort by a coule of people (including me).


As it stands currently, my understanding is that Exchange product team is only planning to support delegated, unattended admin using the multi-tenant app approach outlined here: https://learn.microsoft.com/en-us/powershell/exchange/app-only-auth-powershell-v2?view=exchange-ps - which is "app-only" authentication, so not using any GDAP permissions and the consent for this app can not happen using the Partner Center consent API, but customer admin needs to explicitly consent by clicking on the consent link. 

You are correct that using the old documented option to generate the token for Exchange Online will not work anymore in the future because of deprecation of the RPS and resp. AppIds- so even when the new PowerShell modules allow using an access token directly, there is no way to generate a token as Partner, at least none I could tell. 


I'm still hoping that an alternative approach using an access token with delegated auth, that leverages GDAP permissions grantd to Partner, is officially documented (and build/supported) - but as of today I don't know if this will by the case.



Kind regards, Janosch (Note: Leaving role as of March 2023, don't expect further answers. Connect with me via LinkedIn: https://linkedin.com/in/janoschulmer)
Level 6 Contributor

Thanks for your info, and your continued work on this! Please let us know how you make  out with things, and if there is something we can do to help (such as contacting them via the PS Gallery) let us know - maybe many voices will help?


If the MS Exch Team is going to stick with the new App-Only approach using multi-tenant apps I don't see how this is going to work! 

It says we'll "need to grant admin consent to the Exchange.ManageAsApp permission ... in each tenant organization. To do this, generate an admin consent URL for each customer tenant. Before anyone uses the multi-tenant application to connect to Exchange Online in the tenant organization, an admin in the customer tenant should open the" URL!


That would be nuts with many customers, we have several hundred! And some have thousands!!


I guess we just have to wait and see what happens, and cross our fingers...


(It appears this is all very new: https://github.com/MicrosoftDocs/office-docs-powershell/issues/10077 )



@sansbacher : Yes, many voices will help influencing things - maybe also using the feedback options on the Exchange documentation itself. Will also forward the feedback from this thread directly to responsible people.

Kind regards, Janosch (Note: Leaving role as of March 2023, don't expect further answers. Connect with me via LinkedIn: https://linkedin.com/in/janoschulmer)
Level 4 Contributor

What i got from that is that you are still only going to be able to use the MS Exchange app not your own.

But if it is your own, you dont need to do that. Look at control panel vendor doco. You can preconsent the app in all your tenants via partner center api as long as you have at least azure application admin permission with them.


@Leon-anspired : Unfortunately the consent API only works to to do consent for delegated permissions, not app-only permissions - which are, as it currently stands, required for unattended scripting scenarios going forward. 

Kind regards, Janosch (Note: Leaving role as of March 2023, don't expect further answers. Connect with me via LinkedIn: https://linkedin.com/in/janoschulmer)
Level 2 Contributor

The exchange team has been rogue for as long as I remember when it comes to automation.

Level 5 Contributor

I used the Powershell Gallery contact option to send a message asking for clarification on the issue. I'll be surprised if I get a response but I'll post any response I get here.

Level 5 Contributor

What I would love to see is something like a central timeline chart for <next 12 months> that contains all the Partner relevant dates like NCE/GDAP/Module deprecation/etc with mouseover links to the articles.

I don't particularly want to spend finite time to keep up with a dozen blogs/Youtube channels/sit in on the security Q&A just to get that 20% of highly relevant "this will break everything" information.


@sansbacher the consent/grant stuff was specifically shared in the weekly GDAP Q&A/Security office hours event but it very much feels like that was a personal initiative from one of the organizers.


@rvdwegen : Note that you can also get 1:1 guidance from TP&D when raising a ticket via https://aka.ms/technicalservices (You need to have Action Pack Pack, legacy Silver/Gold MPN benefits or a Solution designation to be abel to leverage advisory hours - or contact your PSAM if you have ASfP support agreement to raise a cloud consult).

While I have nothing to add to @sansbacher , it might be easier to clarify if your approach is correct when looking at this together.

Kind regards, Janosch (Note: Leaving role as of March 2023, don't expect further answers. Connect with me via LinkedIn: https://linkedin.com/in/janoschulmer)
Level 3 Contributor

You're probably trying to use Application permissions, that's no longer possible. You'll need to use Delegated Permissions and get a token "On behalf of". The easiest way to PowerShell this would be to use the Secure Application model guides I've posted here:


Level 6 Contributor

Hi , as Kelvin said: you will have to use the Delegated permissions. And without seeing any code there's nothing but speculation from us. Also, was it working before (with DAP?) or is this all net-new?

Follow his link to create the Secure App Model app registration. That's from a few years ago, it's still the process and works 100% with DAP. I would definitely confirm you have your stuff working with DAP first (while it's still available).

For GDAP you need to make the changes I mentioned above: The delegated permissions are assigned to the App in your tenant (Kelvin's script will assign some, you'll likely need to assign more - eg. to access Azure Resource Management - some earlier posts in this thread have screen shots of various permissions we've added).

The NEW stuff for GDAP is that you'll need to do New-PartnerCustomerApplicationConsent in each Customer tenant (or via API, as Koen shows below). The permissions granted only need to be the basic "Directory.Read.All,Directory.AccessAsUser.All" permissions (my code is from Janosch's original).

Also, you do NOT need to add your App Registration to any GDAP groups, it ONLY uses the Delegated App Permissions - for Azure AD, Azure, Graph, etc. You DO need to add your User Account (UPN in the scripts) used for Consenting to the Exch Online app to a GDAP Group, but it ONLY needs Exchange Administrator (not Global Admin, I confirmed this yesterday).

So: if this was all working before with DAP, post a simplified test version of your code that isn't working. If you're new to this, go through Kelvin's link and confirm things work with DAP. Then switch to GDAP (and remove DAP) and try the New-PartnerCustomerApplicationConsent and see what happens, posting a simplified test case.

Also, when you say "cert" what do you mean? If you're doing Delegated Permissions I don't believe there's any certificate (maybe that's what Kelvin meant when he said you're likely using Application permissions). For DELEGATED permissions you'll need the Secure App Model, which will use the App ID, App Secret (password), Tokens (Refresh and Access), and a UPN (AAD User that you Consented with, that has MFA on their account). No certificate, that's for non-delegated unattended access to YOUR tenant, not to your Customers for whom you have Delegated Permissions).

Hope that helps!


(is it me, or do these forums no longer allow you to use bold/italic/formatting? I get "invalid HTML has been removed" but there was none!)


@sansbacher The invalid HTML message also happens to me time to time for no obvious reason - usually it helps making a small edit somewhere and try again.

Kind regards, Janosch (Note: Leaving role as of March 2023, don't expect further answers. Connect with me via LinkedIn: https://linkedin.com/in/janoschulmer)
Level 2 Contributor

Thank you Sansbacher for that summary and especially the examples.

For those that can't use the partnercenter module in production:

$ApplicationID = 'YOUR csp appID'
$customertenant = 'Tenant ID of your customer'
$AccessToken = 'Access token for YOUR tenant with https://api.partnercenter.microsoft.com/user_impersonation endpoint'

$header = @{
    Authorization = 'bearer {0}' -f $AccessToken
    Accept        = "application/json"
    "Content-Type" = "application/json" # ow look the undocumented cause of a 415

$body = @"
    "applicationId": "$ApplicationID",
    "applicationGrants": [
Invoke-RestMethod -Uri "https://api.partnercenter.microsoft.com/v1/customers/$customertenant/applicationconsents" -Method Post -Headers $header -Body $body -debug -verbose
Level 3 Contributor

So I've been pretty invested in this with Microsoft and have helped the GDAP team develop guidance around this. I got confirmation I was allowed to share this documentation. It's actually pretty straight forward.


In DAP situations, a service principal added to AdminAgents got "Application Preconsent" and this means that the application was allowed to reach into client tenants without the client even knowing of the existence of the app. The client was unaware how many apps had access to their tenants and could not block this in any way if required. All around, Application Pre-consent was a bad idea security-wise.


With GDAP, the application pre-consent is going away. There will be no more method to reach into client tenants without explicit consent. There's two approaches for this.


1.) The CPV API has been made available to all CSPs. You can now use this API to create a service principal within your clients tenants for your applications, using your GDAP credentials. 


The CPV API allow you to send a POST request which creates the service principal in your client tenants. this allows you to use all delegated application permissions set on the app inside of your tenant, as long as you request these permissions. Using the CPV API requires no interaction of the client, but the client is able to delete the service principal at will and thus, blocking your access.


2.) Explicit consent.

See the documentation I've included on how to generate a URL that allows you to give explict consent. This is a manual action you'll have to perform for each tenant you manage, from that point forward, your application is registered at the client and you can use application and delegated permissions set on your own application.


The first flow of course has the preference, as you'll be able to do this scripted for all tenants in one go. For examples, check out the included Microsoft PDF.