A question over at Experts Exchange prompted me to refresh my memory on the process of disabling access to PowerShell for Exchange Online users. The proper way to do that, as detailed by Tony in this article, is to toggle the corresponding flag on the user object. Microsoft is however switching to a new name for said property/parameter, so we should update our guidance accordingly. In addition, we will address why this is the proper way, and what challenges to look for.
So, the quick answer to the question on how to disable PowerShell access in Exchange Online is to use the Set-User cmdlet to toggle the value of the EXOModuleEnabled property. To do so, you can the corresponding –EXOModuleEnabled parameter, like in the example below.
Set-User pesho -EXOModuleEnabled $false
As noted above, Microsoft has switched to using the EXOModuleEnabled property/parameter now, as opposed to the “old” RemotePowerShellEnabled one. For now both can be used interchangeably – toggling the value of EXOModuleEnabled will also toggle the value of RemotePowerShellEnabled , and vice versa. Going forward though, Microsoft will likely remove the RemotePowerShellEnabled parameter/property, so you should update your scripts to use EXOModuleEnabled instead.
This was the first point I wanted to make. The second one is that this flag overrides any additional permission the user might have, in other words you can block access to PowerShell even for users with admin role. It’s not as bad as locking yourself out due to a faulty conditional access policy though and you can easily remedy any error. This fact is actually mentioned in the official documentation, along with some workarounds, so make sure to check those.
Next, as the method relies on the Set-User cmdlet, we don’t have a way to pre-configure this for any newly created accounts. We can easily toggle the flag for groups of users, or even all users, but the object must already exist in order to do so. Thus, a proper solution will have to rely on scheduled tasks/scripts, which is exactly what Tony describes in the article cited above – make sure to give it a read. You can also find multiple examples on how to toggle the flag on a group of users in the official documentation, so I will just omit those here for the sake of brevity.
Because of the inconvenience of having to periodically rerun the solution in order to cover newly provisioned accounts, often times another method is considered. In a nutshell, it revolves around restricting access to the built-in service principal that the Exchange Online PowerShell module uses. You can find the details in, you guessed it, another one of Tony’s articles. The note I want to make here is that this method only works on the authentication level. In other words, it prevents the user from obtaining an access token for the built-in Microsoft Exchange REST API Based Powershell application.
This is not the same thing as blocking access via the EXOModuleEnabled/RemotePowerShellEnabled property. In fact, you can obtain and provide a valid access token from any other app that has the necessary permissions and will still be able to execute Exchange Online PowerShell cmdlets. For example, until recently one could have also used the “old” service principal (with client ID of a0c73c16-a7e3-4564-9a95-2bdf47383716). Given the huge number of built-in service principals, there is no guarantee that another one cannot be used to the same effect.
And of course there is the possibility of using LOB/third-party apps. In fact, the Exchange Online PowerShell module offers built-in support for passing an access token. Moreover, the permissions needed for obtaining a valid access token for the purpose of executing Exchange Online PowerShell cmdlets, Exchange.Manage, do not require admin consent. If you need a refresher on how the process works, you can check this article.
To illustrate the issue with this method, we can do a simple test. We can follow the steps for restricting access to the built-in service principal but at the same time leave the EXOModuleEnabled flag enabled for the user. As you’d expect, this will prevent them from logging in via the “standard” method. However, if one reruns the Connect-ExchangeOnline cmdlet and passes a valid access token for a LOB app with the Exchange.Manage permission, the connection will be established and the user will still be able to run any PowerShell cmdlet he has access to, as shown below:
On the other hand, toggling the EXOModuleEnabled flag will result in blocking access regardless of how authentication was performed. This is illustrated on the screenshot below, where we are again passing an access token. This time however, the authorization process fails and we get the corresponding error message:
So, we now know what the “proper” way to block access to Exchange Online PowerShell is. While there are some challenges in configuring the EXOModuleEnabled property for newly created accounts, this method is still the one you should aim for, even if you are using other approaches. And of course, you can combine multiple methods. Better safe than sorry!
One final note. There is a reason why the EXOModuleEnabled property is controlled via the Set-User cmdlet – a user doesn’t need to have a mailbox in order to connect to Exchange Online PowerShell. Thus, if you are aiming to control this via some “provisioning” script or other automated solution, be sure to include not just mailboxes, but all user objects!