Modern Auth and Unattended Scripts in Exchange Online PowerShell V2
Published Jun 30 2020 08:14 AM 100K Views

Today, we are happy to announce the Public Preview of a Modern Auth unattended scripting option for use with Exchange Online PowerShell V2. This feature provides customers the ability to run non-interactive scripts using Modern Authentication. This feature requires version 2.0.3-Preview or later of the EXO PowerShell V2 module, available via PowerShellGallery.

Check out the detailed guide on how to install/update the new EXO PowerShell V2 Module here.

As previously announced, Basic Authentication for Exchange Online Remote PowerShell will be retired in the second half of 2021. Customers who currently use Exchange Online PowerShell cmdlets in unattended scripts should switch to adopt this new feature. This new approach uses AzureAD applications, certificates and Modern Authentication. You can find detailed step-by-step instructions available here.

It’s simple to create and use sessions using this new feature. For example, if you are currently using Basic Authentication for unattended scripting, you are probably using something like this in your scripts;


New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection


Once you have changed to Certificate Based Authentication, the above cmdlet pattern will need to be changed to use Connect-ExchangeOnline along with other necessary parameters. For example:


Connect-ExchangeOnline -CertificateFilePath "C:\Users\johndoe\Desktop\automation-cert.pfx" -CertificatePassword (ConvertTo-SecureString -String "<My Password>" -AsPlainText -Force) -AppID "36ee4c6c-0812-40a2-b820-b22ebd02bce3" -Organization ""


Please note the feature does not support delegation. Unattended scripting in delegation scenarios is supported with the Secure App Model which is documented here.

We hope this new feature makes it possible for you to move away from using Basic Authentication for your unattended scripting needs and appreciate the increased security this new option provides. Please do give us feedback, we really do want to hear what you think.

The Exchange Team

Senior Member

@ManicInfinity on our side we simply open a basic Runspace with no WSMan remote info, and run the raw PowerShell command inside, so for you:


var certId = ID

var appId = ID

var org = ID


// To create a basic runspace:

var initSession = InitialSessionState.CreateDefault();

Runspace = RunspaceFactory.CreateRunspace(initSession);


// Then run the connect inside your Powershell session directly:

session.AddCommand($"Connect-ExchangeOnline -CertificateThumbPrint {certId} -AppID {appId} -Organization {org}")


// Then run any other commands ...


Just make sure the module is installed on the local computer.

Senior Member

@JrouziesM Thank you very much for this... and it appears to work (in that I no longer get an error when I connect).


However, once I have connected, and when I then try to use the cmdlet Get-EXOMailbox an exception gets thrown with the following unhelpful error

System event notifications are not supported under the current context. Server processes, for example, may not support global system event notifications.

which doesn't make any sense to me, and neither can I find anything useful about it on the Internet.


[InvalidOperationException: System event notifications are not supported under the current context. Server processes, for example, may not support global system event notifications.]
   Microsoft.Win32.SystemEvents.EnsureSystemEvents(Boolean requireHandle, Boolean throwOnRefusal) +566
   Microsoft.Exchange.Management.RestApiClient.AdminCmdlet`2.BeginProcessing() +546
   System.Management.Automation.Cmdlet.DoBeginProcessing() +85
   System.Management.Automation.CommandProcessorBase.DoBegin() +378

[CmdletInvocationException: System event notifications are not supported under the current context. Server processes, for example, may not support global system event notifications.]
   System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input) +365
   System.Management.Automation.Runspaces.Pipeline.Invoke() +12
   ACS.Web.Email.MailboxForward.Business.MailboxForwardBLL.GetUsersUsingCertificate() in C:\Projects\ACS.Web.Email.MailboxForward\ACS.Web.Email.MailboxForward.Business\MailboxForwardBLL.cs:154
   ACS.Web.Email.MailboxForward.Default.Page_Load(Object sender, EventArgs e) in C:\Projects\ACS.Web.Email.MailboxForward\ACS.Web.Email.MailboxForward\Default.aspx.cs:65


Have you come across this before?


Regards, Ade

Senior Member

@ManicInfinity hmmm never saw it, but it looks like it creates a pop-up for the user:

ACS.Web.Email.MailboxForward.Business.MailboxForwardBLL.GetUsersUsingCertificate() in C:\Projects\ACS.Web.Email.MailboxForward\ACS.Web.Email.MailboxForward.Business\MailboxForwardBLL.cs:154
   ACS.Web.Email.MailboxForward.Default.Page_Load(Object sender, EventArgs e) in C:\Projects\ACS.Web.Email.MailboxForward\ACS.Web.Email.MailboxForward\Default.aspx.cs:65

With an ASPX file.

When you run all this in a script, do you get any login pop-up? Maybe the previous command didn't log correctly, or it is a perticularity of V2 Cmdlets, can you try with a classic Get-Mailbox?


I never used the certificates to log in, always with login / password (stored safely as Secure String and decoded in another DLL), so there might be a behavior here.


Regular Contributor

@The_Exchange_Team It may have already been said, but doesn't the -CertificateFilePath and -CertificatePassword approach undo the whole security aspect of this great feature?  It would lead people to start storing the PFX and plain text password for that PFX...  I feel like this is a pretty big oversight.  Maybe I'm missing something.  I would have to assume that passwords sitting in plain text on local systems, or even in Azure Automation etc., would be just as risky as using Basic Authentication would be in the first place.


What are you thoughts on this issue?




Senior Member

@Jeremy Bradshaw I will let Microsoft reply all the details about how is it safer or not during the transaction, but for storing the password in clear this problematic is real for any kind of passwords. If the user store it in clear, it would have stored a normal login / password in clear also.


On our side we store those passwords in a Keypass with limited access, and when required to use them we write the scripts / programs to accept only a SecureString as string version of that password, and if possible with a C# Program, we use a separate DLL and force user to pass a SecureString to not be tempted to convert the password as clear himself in the code.


I guess this is an eternal question on how to secure a password for a script or program, and the answer is mostly about how to actually encrypt it and really limit who / what can decrypt it to the minimum required (e.g. when we deploy in PROD, only super admins have the service account password that will run the program and be used to encrypt then decrypt the password).

Regular Contributor

@JrouziesM I guess I should have clarified that I was comparing the option of -CertificateFilePath against either of the other options -Certificate or -CertificateThumbprint.  The latter options generally line up with the certificate being in the certificate stores (Cert:\CurrentUser\My for -CertificateThumbprint, more options available like Cert:\LocalMachine\My for -Certificate), taking away the need to use or store a password, encrypted or not.


I guess I'm not sure anyways about whether it's any more difficult for an intruder to gain access to the certificate in the local stores, if they already have enough access to the disk to capture the stored password for the PFX file.  If the stored password for the PFX file is encrypted, let's say via Export-Clixml, then they intruder would need to be logged in as the account that performed the Export-Clixml, and in this case, I 100% agree there's no added security by having the certificate stored in the local store vs having the PFX and encrypted password.  I'm not sure what happens if somebody got their hands on the disk (physical / virtual) itself - would they have easy access to the certificate in the local stores (I don't know)?


If all these points of comparison are equal in strength/weakness, then I guess I'm harping about nothing here.

Occasional Contributor

Any news on getting app-only authentication to work with the Set-UserPhoto cmdlet? Looks like in the v1 module when using MFA you had to use New-PSSession with ?proxyMethod=RPS at the end of the ConnectionURI. How can this be accomplished with the v2 module?

New Contributor
Please note the feature does not support delegation. Unattended scripting in delegation scenarios is supported with the Secure App Model which is documented here

Hi :)

any update on this? We would like to use the modern auth for unattended scripts for our delegated tenants (CSP).
Is there a roadmap for enabling delegation support using certificate authentication?



New Contributor

I too am wondering how I can set ?proxyMethod=RPS in order to use Set-UserPhoto.

Senior Member



Hi @JrouziesM 


Happy New Year and apologies for not getting back to you sooner.


In Powershell Script connecting to MFA enabled Exchange Online using a certificate thumbprint (i.e. no password or user interaction or pop up) and then executing the following statement works fine (before disconnection of course);


Get-EXOMailbox -UserPrincipalName -Properties DeliverToMailboxAndForward, ForwardingAddress | Out-String



As I said, in C# code I am able to connect to Exchange Online (using the solution you gave me on 16th December with no password or user interaction or pop up) but get that strange error I mentioned above on 17th Dec (about System Notification Events not being supported by the Current Context) when I execute the following;

session.AddScript("Get-EXOMailbox -UserPrincipalName -Properties DeliverToMailboxAndForward, ForwardingAddress | Out-String");
Collection<PSObject> results = session.Invoke();


However, following your advice I tried the same in C# code using the old cmdlet Get-Mailbox and the code (below) works perfectly fine;

session.AddScript($"Get-Mailbox '' | Format-List DeliverToMailboxAndForward, ForwardingAddress | Out-String");
Collection<PSObject> results = session.Invoke();

(basically the code correctly returns that the mailbox has forwarding enabled and the email address the forwarding is set to).


So @The_Exchange_Team it would seem the issue is with the new cmdlet Get-EXOMailbox. For the time being I can create my solution using the Get-Mailbox and Set-Mailbox cmdlets but I am aware that they are less efficient. As such @The_Exchange_Team are you able to a) confirm whether these older cmdlets are actually depreciated or will continue to be supported and b) help out at all with my issue with Get-EXOMailbox working fine in PowerShell script but not inside a C# PowerShell session?


Thanks in advance, Ade




Not applicable

Yeah, nice, but when can we finally expect support for passwordless accounts (as highly recommended by MS)?

Occasional Contributor

Any update on the set-userphoto issues? its really annoying:

Connecting to remote server failed with the following error message : 䋀誯Ȁ For more information, see the
about_Remote_Troubleshooting Help topic. [Server=GVAP278MB0136,RequestId=83527e72-0a1b-42ae-8a24-afee1eaf5198,TimeStamp=10.09.2020 08:35:31] .
+ CategoryInfo : NotSpecified: (:) [Set-UserPhoto], CmdletProxyException
+ FullyQualifiedErrorId : [Server=GVAP278MB0136,RequestId=83527e72-0a1b-42ae-8a24-afee1eaf5198,TimeStamp=10.09.2020 08:35:31] [FailureCategor
y=Cmdlet-CmdletProxyException] DFC3D95C,Microsoft.Exchange.Management.RecipientTasks.SetUserPhoto
+ PSComputerName :

Is there any way to do this with the new Skype Online or Teams modules?  We have some scripts that need this and it seems like basic auth no longer works with the newer modules.

New Contributor

Hi.  I am trying to convert a c# unattended application from using basic auth to the new certificate based method and have tried using several suggested options, the latest being a process based on that indicated by JrouziesM 12-16-2020.  However I still have problems with the following:

var certId = "xxxxxx";
var appId = "yyyyy";
var org = "zzzzz";

var proxyType = "IEconfig";

InitialSessionState initSession = InitialSessionState.CreateDefault();

initSession.ImportPSModule(new[] { "ExchangeOnline" });

Runspace Runspace = RunspaceFactory.CreateRunspace(initSession);


PowerShell powershell_2 = PowerShell.Create();

powershell_2.Runspace = Runspace;

PSCommand session = new PSCommand();

session.AddCommand($"Connect-ExchangeOnline -ConnectionUri {strRemoteComputerUri} -ProxyAccessType {proxyType} -CertificateThumbPrint {certId} -AppID {appId} -Organization {org}");

session.AddScript("get-mailbox -identity xyz");

Collection<PSObject> output = powershell_2.Invoke();


However, this just seems to time out or not proceed to an output being generated from the Invoke().  No errors are trapped when I include it in a try/catch.


Is there something that I am missing in this approach?

Senior Member

@pmtelstra nothing is being returned by powershell_2.Invoke(); because you have added your commands to a different session object (session), and just left it hanging in the air. Try;


powershell_2.Runspace = Runspace;

powershell_2.AddCommand($"Connect-ExchangeOnline -ConnectionUri {strRemoteComputerUri} -ProxyAccessType {proxyType} -CertificateThumbPrint {certId} -AppID {appId} -Organization {org}");

powershell_2.AddScript("get-mailbox -identity xyz | Out-String");

Collection<PSObject> output = powershell_2.Invoke();


Personally I do something like this....


using (PowerShell session = PowerShell.Create())
// 3. And finally attached the Runspace to the Session, so that EXO cmdlets are supported
session.Runspace = Runspace;

// Inject the scriptlet that needs to be executed between connecting and disconnecting to Exchange Online
string script =
($"Connect-ExchangeOnline -CommandName 'Get-Mailbox','Set-Mailbox' -CertificateThumbprint {Config.Azure.CertID} -AppID {Config.Azure.AppID} -Organization {Config.Azure.Organisation};" +
psScriptlet + ";Disconnect-ExchangeOnline -Confirm:$false");

// Add the script

// Invoke the script
Collection<PSObject> psOutput = session.Invoke();



where psScriptlet might be a command like $"Get-Mailbox '{MailboxOwner.UPN}' | Format-List DeliverToMailboxAndForward, ForwardingAddress | Out-String". Note in my posts above I always get an error if I try using the new cmdlet Get-EXOMailbox

Occasional Visitor

@ManicInfinityThank you so much for your comments!

I have also been searching all day for an explanation and finally came upon this post.


Will keep tabs on this thread in the future for any development regarding Exo cmdlets.

Occasional Contributor

Now that the v2 module has gone GA, the Set-UserPhoto cmdlet still doesn't work with app-only authentication.

Senior Member

I opened a ticket with support on this and here is what I got back:


As per our research from the back end team the issue you are experiencing is a known issue and our team is working on this to get a fix.

  1. For now we do not have a ETA on this, our team is already working on this we hope we find a solution soon, we will be sharing the communications with our users via articles or updates once we have a fix for the same.
  2. Unfortunately for now we do not have a work around on this and there is nothing I can share with you.

I apologize for the inconvenience caused to you and assure you that we are working on finding a fix for you."


That being said, I would highly recommend opening up support tickets through your Premier Support and other channels so they know this is a bigger issue and putting pressure on you.


Set-UserPhoto has design challenges which are being worked upon. In cases where Photo data can be bigger, we hit the limits of HTTP. 


We still don't have an ETA. We will update the documentation once we have a plan to support Set-UserPhoto in CBA.



Exchange Online PowerShell Team

Occasional Contributor

Thanks for the reply regarding set-userphoto. Good to know its being worked on.


Not sure I understand how http limitations are an impact to this. Set-UserPhoto works fine when authenticating with a user account with modern auth. How would switching to CBA introduce http limits?

Senior Member

@navgupta / team, please share the status on the implementation of authenticating with cached adal access token. You mentioned this is in the backlog. This is needed way ahead of 2021 H2 to ensure smooth transition of applications. 

New Contributor

Hi.  I have now got the module working in a c# web application running in IIS. However I am finding the delay associated with ImportPSModule very significant when it is needing to be done for each hit.  Is it possible to load ExchangeOnlineManagement as an IIS module so that it is already loaded for the application and there would be no need to load it explicitly through the application code each time a connection is needed?  I have tried adding ExchangeOnlineManagement as a managed module in IIS but that causes IIS to generate an error: 

Faulting application name: w3wp.exe, version: 8.5.9600.16384, time stamp: 0x5215df96
Faulting module name: KERNELBASE.dll, version: 6.3.9600.19724, time stamp: 0x5ec5262a
Exception code: 0xe0434352

Perhaps I am not loading the module correctly? or perhaps it cannot be loaded through the IIS configuration the way I am attempting.

Any advice would be appreciated?

New Contributor

Actually, in relation to my Feb 21 2021 07:45 PM question.  The issue may not be caused by the import-module.  It seems the old connection method used in the web app ( using WSManConnectionInfo) had similar connection delays (around 12 seconds for an initial connection) but somehow the connection was being cached so subsequent "connection" calls were much faster (~10x).  Apparently that's just not happening for me with the new method as subsequent calls all still take over 12 seconds.


I am using:


InitialSessionState initSession = InitialSessionState.CreateDefault();
initSession.ImportPSModule(new[] { "ExchangeOnlineManagement" });
Runspace rs = RunspaceFactory.CreateRunspace(initSession);
using (PowerShell session = PowerShell.Create())

session.Runspace = rs;

session.AddParameter("CommandName", cmdlet.CommandText); // optimise load time by limiting cmds
session.AddParameter("CertificateThumbprint", ActiveDirectory.O365OAuth2CertificateThumbprint);
session.AddParameter("AppID", ActiveDirectory.O365OAuth2AppID);
session.AddParameter("Organization", ActiveDirectory.O365OAuth2Organization);

if (ActiveDirectory.O365RemotePSConnectionUseIEConfigProxy == "Y")
session.AddParameter("PSSessionOption", new PSSessionOption() { ProxyAccessType = ProxyAccessType.IEConfig });


output = session.Invoke();

Any shared wisdom would be appreciated.  Thanks.

@The_Exchange_Team @navgupta How many parallel sessions can we create with single app registration? I wan to run the instances of Azure PowerShell functions where I connect to Exchange and get the mail box info. How many instances can I safely run if I want to have only one app registration for authentication?

New Contributor



I am trying to install ExchangeOnlineManagement 2.0.4-Preview9 into a project in Visual Studio 2017.  The project is set to use .NET 4.5.  When I try I get the error::


"Could not install package 'ExchangeOnlineManagement 2.0.4-Preview9'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.5', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author." 


I have also tried with the project set to .NET 4.6.  What version of .NET does the package require?  I have assumed 4.5 from what I have read, but now I am confused...  

New Contributor

I am using nuget package manager in VS2017 with a downloaded package.

Occasional Visitor

My main problem is , i dont want to use certificate based authentication... It want to use client id / secret with connect-exchangeonline.


What is working, is this :


With this i am able to connect with client id and secret but i only can use Get-Mailbox, and not Get-ExoMailbox ( this says i need to connect first with connect-echangeonline).


But Exchange Online Module is giving me unknown user type if i add the credentials (even if i use the appid). 

I can live for the moment with Get-Mailbox, but i want to use the superiour REST based modules from Get-ExoMailbox. 


I cannot use New-ExoPSSession as its not recognized as a cmdlet.



New Contributor

Extremely slow connection times using OAuth2 makes my app almost impossible to use.

Ok I have now (finally) been able to convert a web application which is used for managing Office 365 mailboxes and their settings from Basic auth to OAuth2 using certificate thumbprint.  However the response times are terrible. 

  • Connections which previously took under 2 seconds now consistently take between 12 and 20 seconds each. 
  • Because this app needs to run multiple sequentially called cmdlets in o365 to get the required data or to write the required changes, this added delay is compounded. 
  • A page load which previously gathered all info using calls to get-mailbox, get-mailboxpermission, Get-RecipientPermission & get-user which previously loaded in under 30 seconds (just acceptable) now takes almost 5 minutes

As we hurtle towards a basic-auth shut-down date (apparently only a handful of months away) I am desperate for a solution for this.  This is an enterprise wide app which is used by 35,000 staff globally and the new slow experience makes it almost impossible to use for most functions. 

I have heard that the Graph API is supposedly being extended to allow several basic mailbox cmdlet-type actions which may or may not be what we need(?), and is assumed(?) will have much faster response times, but nobody seems to know anything much about that.

  • Does anyone know what can be done to reduce the connection time for OAuth2 using cert thumbprint to somewhere near what it was for basic auth?
  • Does anyone have a time-line for the new Graph API functionality and what capabilities it will have?

Given the fact that shut down date is getting close and if either of the above 2 possible solutions actually materialize, we will need significant development and test time.  Is there a plan to extend the cut-off date further, given the slow delivery of workable solutions?

New Contributor

Will this code work after the deprecation of basic-auth?

 var connectionUri = new Uri($"" + tenantDomain + "&amp;BasicAuthToOAuthConversion=true&amp;HideBannerMessage=true");

            WSManConnectionInfo connectionInfo = new WSManConnectionInfo(connectionUri, "", tenantCredentials.Credential);
            connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;

@asdasdadqwdq - The code provided by you uses BasiAuthToOAuthConversion = true which means OAuth should be used. But then it also uses AuthenticationMechanism.Basic. 


I would suggest checking AAD Signin Reports to see if the call corresponding to this App are shown as Basic Auth in Sign-in reports. If it shows Basic Auth, then it will not be supported once Basic Auth is deprecated in Exchange Online.


 var connectionUri = new Uri($"" + tenantDomain + "&amp;BasicAuthToOAuthConversion=true&amp;HideBannerMessage=true");

            WSManConnectionInfo connectionInfo = new WSManConnectionInfo(connectionUri, "", tenantCredentials.Credential);
            connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
Occasional Visitor

There seems to be a memory leak when connecting using certificate thumbprint. We currently doing tests with this in our IAM system and when connecting every 15 min ,  reading the members of a shared mailbox and disconnecgting the memory usage of our webserver is rising and rising and is only being released when recycling our website in IIS.

I have seen others reporting this, but I cannot understand that it's not begin picked up, especially when it's stated that basic authentication will be removed. It basically makes it not suitable for production environments.

Occasional Contributor

What are the plans (if any) to be able to limit the scope of access when using app-only authentication? Currently we use service accounts with Exchange admin roles and management scopes to limit which objects a given user/script can act upon. From what I've read about the current app-only authentication, it doesn't appear that we're able to replicate this. Is this correct? If so, that's something we're going to need to move over.

Not applicable

@Scott Ladewig not sure if it answers your question:


"Administrators can use ApplicationAccessPolicy cmdlets to control mailbox access of an app that has been granted any of the following application permissions"


Occasional Contributor

@Deleted Thanks for the link. I'll check that out.

Occasional Contributor

@Deleted That link was about controlling access to mailbox and calendar items. The write scopes we're using currently control which objects can be modified, so not the equivalent. Thanks for the suggestion though.

Frequent Visitor

Hi, we are currently trying to automate things a little.


Connection with thumbprint works well. We will probably run scripts using this method a lot. That means it will establish many connections to the app, and I couldn't find a way to...

a) close a specific connection (only via "Disconnect-ExchangeOnline -Confirm:$false") which will cause...

Removed the PSSession ExchangeOnlineInternalSession_1 connected to
Removed the PSSession ExchangeOnlineInternalSession_2 connected to
Removed the PSSession ExchangeOnlineInternalSession_3 connected to
Removed the PSSession ExchangeOnlineInternalSession_4 connected to

b) or connect to an existing connection / session


What is a good way to "pool" the connection to the app if a user runs several scripts that make changes or download info from EXO at the same time?


Regular Visitor

@Arkin , I'm not too sure if this the "good way". But I did some experimenting related to this (in PowerShell), maybe you can use it as inspiration:


Create a remote session to localhost - means your session runs locally but will be accessible from multiple consoles/instances:


# From Console 1:

# Create new remote session to local host with idle timeout of 15 minutes
$session = New-PSSession -ComputerName . -SessionOption (New-PSSessionOption -IdleTimeout 900000)
# Get session InstanceId and save it for reconnecting later
$guid = $session.InstanceId


Establish an Exchange Online session inside the session you just created:


# Establish credentials variable in new remote session:
Invoke-Command -Session $session -ScriptBlock {$cred = Get-Credential}
# Establish EXO session in new remote session:
Invoke-Command -Session $session -ScriptBlock {$EXOSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $cred -Authentication Basic -AllowRedirection}

(replace EXO connection with your preferred style/format)


You now have a $EXOSession inside your $Session and both will exist (in connected state) until the timeout expires. The TTL get's refreshed every time you use it.


# Get mailbox from EXO using EXO session inside remote session (which is actually local)
Invoke-Command -Session $session -ScriptBlock {Invoke-Command -Session $EXOsession {get-mailbox ole.roemer}}


# Disconnect from remote session:
Disconnect-PSSession $session

(this doesn't end the session, it stays fresh and dormant) 


Open up another PowerShell console to simulate another script running:


# From Console 2:
# View existing sessions:
Get-PSSession -ComputerName localhost | fl


Get a hold on the session created in Console 1:

# Get reference to the session from before:
$session = Get-PSSession -ComputerName localhost -InstanceId ([GUID]"enter-guid-from-before")


# Connect to the session:
Connect-PSSession $session


# get mailbox using the EXO session in the remote session:
Invoke-Command -Session $session -ScriptBlock {Invoke-Command -Session $EXOsession {get-mailbox ole.roemer}}
Disconnect-PSSession $session


It's not pretty. At all. But it saves your application the trouble of reconnecting. Lot's of reconnects might get throttled.


Hope you can use this as inspiration

/ Morten

Easy Office 365 administration:

New Contributor

Thanks a lot. It works great.

How can I do the same connecting to different environment like:




using the same registered application and, of course, the same certificates?


Regular Visitor

@EnricoGiacomin It works quite smooth with other Office 365 services.


This example shows how to authenticate/connect to MSOnline in one session and use that session from another session:


# Session 1 (establish new MSOnline session):

$session = New-PSSession -ComputerName . -SessionOption (New-PSSessionOption -IdleTimeout 900000)
$guid = $session.InstanceId

# Output = a02b4ae5-65f1-4fad-a139-73eb211be80f

Invoke-Command -Session $session -ScriptBlock {$cred = Get-Credential}

Invoke-Command -Session $session -ScriptBlock {Connect-MsolService -Credential $cred}
Disconnect-PSSession $session



Then use the already established MSOnline session from another PowerShell session:

# Session 2: (connect using the session GUID from before)

$session = Get-PSSession -ComputerName localhost -InstanceId ([GUID]"a02b4ae5-65f1-4fad-a139-73eb211be80f")
Connect-PSSession $session

Invoke-Command -Session $session -ScriptBlock {Get-MsolUser -UserPrincipalName | ft}

# Output:

#UserPrincipalName DisplayName isLicensed
#----------------- ----------- ---------- Jens M. Knudsen True


I guess you can probably translate this to an application/certificate authenticated scenario? I haven't worked on that.


/ Morten

Easy Office 365 administration:

Frequent Visitor


I have another question. 

We are calling a powershell script that connects via Thumbprint through IIS. The application pool runs as the user that has the certificate in its store.

While the user is also connected via RDP, everything works fine.

If the user does not have an RDP session the connection fails (IIS website output):



Error Acquiring Token:
Could not use the certificate for signing. See inner exception for details. Possible cause: this may be a known issue with apps build against .NET Desktop 4.6 or lower. Either target a higher version of .NET desktop - 4.6.1 and above, or use a different certificate type (non-CNG) or sign your own assertion as described at 



I don't get more output at this point.

Having a locked user session on this server is not really an option (reboot etc.). Any idea how this could work withour requiring an active session?





Can't delete... so here is the solution:

IIS App Pool -> Advanced Settings -> Load Profile: Cahnge "False" to "True"
Regular Contributor

@navgupta @The_Exchange_Team  is there any hope that app-only/certificate authentication will be added to Connect-IPPSSession?  I have a use case for it, which is:

- A customer is using an org-wide retention policy in order to simplify the process to ensure terminated accounts' mailboxes become Inactive mailboxes.

- This same customer is hitting the 100GB Recoverable Items quota (in either the primary and/or archive (if an archive is present).

- They are not so much concerned about retaining user-deleted content, so we need to temporarily exclude mailboxes from the retention policy in order to allow RecoverableItems\DiscoveryHolds to purge items and get back down below 100GB.  Once down to some minimum number of GB's, we'd remove the exclusion.


With ~70,000K mailboxes and growing, automating this will be key.  The ability to do so using the same technique with Connect-IPPSSession as with Connect-ExchangeOnline would be ideal.

Senior Member

@The_Exchange_Team Currently the EXO V2 module uses the Active Directory Authentication Library, but this is being depreciated June 2022. Will there be a module update to support Microsoft Authentication Library (MSAL)?




@MikeJ9 I confirmed with the team that we do not use ADAL in the EXOV2Module anymore and have completely migrated to MSAL on all horizontals.



Senior Member

There seems to be a problem using the V2 module in Azure Automation with a RunAs account - any attempts to use Connect-ExchangeOnline cmdlet to connect to a tenant other than where the RunAs account resides (after retrieving credentials in the Automation account using RunAs or other account) result in the error;


Admin account chosen for authentication is different from the one provided as parameter during Connect-ExchangeOnline. Please choose the same account during authentication as well

How do we use this cmdlet in an Automation account if we are connecting to a different tenant? 

Senior Member

Please note the feature does not support delegation. Unattended scripting in delegation scenarios is supported with the Secure App Model which is documented here.

We hope this new feature makes it possible for you to move away from using Basic Authentication for your unattended scripting needs and appreciate the increased security this new option provides.


Is there any feature update planned to support unattended scripting in delegation scenarios?

Currently the Secure App Model relies on basic authentication which will be permanently disabled  in all tenants after 1st october 2022.


Occasional Visitor

can i use exchange online (exo v2) PowerShell module with app registration to allow unattended actions with federated domain ?

Senior Member

I'm trying to update my scripts to modern auth and am getting this error....

New-ExoPSSession : Certificate is not accessible to the current user.

cert is local on my pc

Connect-ExchangeOnline -CertificateFilePath "C:\cert\mycertp.pfx"



Senior Member


  • You want to make sure this below are completed on a highly secured dedicated computer/server. Anyone with Administrator access to the server can use the connection, using the APID.

There are two certificate based authenticate options  using APID

  1. Option1 - Connect using Thumbprint
  2. Option2 - Connect using PFX

Both methods are well document online ( 

However, I prefer the Thumbprint option, which is easier  to manage - no need for pfx or private key password.

See detail implementation  steps below:


1. Register the application in Azure AD.

2. Assign Exchange Online API permissions to the application.

3. Generate a self-signed certificate

4. Export self-signed certificate public key (.cer)

5. Attach the certificate to the Azure AD application (This will be the .cer field generated in step 4 above.)

6. Assign Azure AD roles to the application

7. Make a note of the certificate thumbprint because you'll need it in the next step. See How to: Retrieve the Thumbprint of a Certificate.

8. Give the domain based service account used for running the script instance ('xxxx' ) permission to access the certificates private key.
To do this using the MMC:
8a. Open the MMC snap-in for certificates. See How to: View Certificates with the MMC Snap-in.
8b. Expand the Certificates (Local Computer) node, expand the Personal node, and then select the Certificates subfolder.
8c. In the right pane, right-click the self signed certificate (created in step 3 above), select "All Tasks", and then choose Manage Private Keys.
8d In the Security dialog box, choose Add.
8e. In the Select Users, Computers, Service Accounts, or Groups dialog box, enter the name of the dedicated user account (script will run under the context of this account - usually, this is a dedicated service account from your Active directory domain/ldap server). Then, choose the OK button.
8f. In the Full Control field, select Allow, and then choose the OK button.


Sample connection: Connect Exchange Online, cmdlet example - using certificate thumbPrint (no need for pfx)


$CertificateThumbprint='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'  <# This is the thumbprint for the self-signed cert in step 3. #>
$Organization = '' <# This is your organizations'' FQDN. #>
$AppId = 'xxxxxxxxxxxxx'

Connect-ExchangeOnline  -CertificateThumbprint $CertificateThumbprint  -AppId $AppId  -Organization $Organization


Version history
Last update:
‎Jul 30 2020 05:41 AM
Updated by: