PowerShell has long been the standard tool for managing everything from Windows Server to Exchange to Office 365 and Azure resources and has more recently ventured into the cross-platform world with support for Linux and macOS via its PowerShell Core fork. With its robust scripting language, automation and remoting capabilities, PowerShell has proven to be invaluable and many IT Pros feel helpless without it. Therefore, it has become very important to ensure access to a PowerShell console wherever possible.

Using PowerShell in the browser is hardly something new, in fact, this functionality has been available for many years. Starting with Windows Server 2012, the Windows PowerShell Web Access feature allowed us to run PowerShell cmdlets and scripts on virtually any device, without needing to install additional software. That said, the PowerShell Web Access feature hasn’t received much love since its original release, and while it’s still part of the Windows Server install, there’s hardly anything new to talk about.

So, why am I writing about it now, six years later? Azure Cloud Shell is one of the reasons. I’ve been meaning to do a comparison between the two, especially when it comes to managing things in the Office 365 world. After more announcements made at this year’s Ignite, I have a few more reasons to explore this topic further. Let’s dig right in.

PowerShell Web Access

As PowerShell Web Access (abbreviated as PSWA) has been available for years, I won’t go into too much detail about installing and configuring the feature, you can find this information in the official documentation or on numerous blog posts. However, I will briefly talk about how you can use it, and what you can’t do with it. 

To start with, you can use any browser to connect to the PSWA endpoint you exposed internally in your organization or to the outside world. Internet Explorer will do just fine, even versions as old as IE 8.0, not that you should actually be using them. Most mobile browsers will be fine too, as long as they support JavaScript. As you can imagine, the tradeoff of the relaxed requirements is a “not so snappy” interface, as shown in the screenshot below. The screenshot also illustrates the amount of attention given to this feature by Microsoft in the recent years, as I am actually running on a Windows Server 2019 box, even though the webpage says Windows Server 2016.

PowerShell Web Access Screenshot

Cosmetics aside, once you log in to the PSWA page, you end up at a terminal window and you can start issuing cmdlets. Under the covers, PSWA uses remoting to connect to a given machine, which is why the Computer Name parameter is mandatory when logging in. Once connected, you have access to a fully-fledged PowerShell console, as indicated by the output of $PSVersionTable, and you can do more than run cmdlets. For example, you can invoke .NET classes and methods, such as the System.Environment class and its OSVersion property, showing the version/build of the current server. You can also call console applications such as ipconfig:

PowerShell Console Access Screenshot

You get support for tab completion and execution history as well as most other PowerShell features you are familiar with. Finding and installing modules via the cmdlets exposed by the PowerShellGet module is one example, provided you are connected to a computer running a recent PowerShell version. You can quickly download the MSOnline module if it is not already present on the system. Once the module is installed, you can connect to Office 365 and start managing users.

PowerShell Web Access Module

While connectivity to Office 365 did work, you might have noticed that I used the -Credential switch for the Connect-MsolService cmdlet above. Doing so results in using a special OAuth flow, allowing me to connect via Modern authentication while at the same time bypassing the interactive login dialog. The reason is simple – since I’m running PowerShell in what’s effectively a terminal, there is no way for the interactive ADAL dialog to be shown. This, in turn, means that any module that requires you to do a fully interactive login will not work, and PowerShell will inform you accordingly with an error message. Connecting to services that accept $Credentials variable will work just fine though, for example the case of Exchange Online:

PowerShell Web Access Module Example Screenshot

So in effect, I used my regular “connect to Office 365 script” in a browser session, and I’m now able to perform (almost) all the operations I’m used to running on my desktop machine. Other modules, such as MicrosoftTeams will also run just fine, as long as you are able to pass credentials directly. For modules that only support interactive login, you might have to resort to workarounds such as getting an Access token directly via ADAL and passing it to connect via Modern authentication, as shown below for ExO:

PowerShell Web Access EXO Screenshot

This is not the only limitation. For example, as this is a remote session and you are running in a different PowerShell host, there are no profiles available. It also means that you can’t interactively remote to another machine using Enter-PSSession, for example in order to access a module installed on that machine. One alternative would be to relog to the PSWA gateway and choose the other machine instead. You can also use other methods, such as New-PSSession, Invoke-Command or the -ComputerName parameter, depending on the task.

Since you are bound to the terminal, copying and pasting information can be a drag, and so is viewing or editing files, and transferring files is an almost impossible task. Other “goodies” you might have become used to include: syntax highlighting, function keys and persistent cmdlet history are also unavailable, or limited. Overall, PSWA is a viable substitute for those rare cases where you have to run a specific PowerShell task from a tablet or mobile device. So how practical is it? I would say a solid 8/10 when put in the context of Office 365 tasks.

Azure Cloud Shell

While Microsoft has barely touched PSWA in the past few years, they’ve put a lot of effort into releasing and polishing PowerShell Core, bringing the power of PowerShell to new modalities. One modality is Azure Cloud Shell (ACS for short), a browser-based PowerShell experience for managing Azure resources. Under the covers, Azure Cloud Shell is a web interface for a PowerShell Core (or Bash) instance running on a Linux VM running on top of a Hyper-V host. Running on Linux allows Microsoft to minimize the resource consumption and costs, and in effect ACS is free, with the only charges incurred by the cloud storage account it uses to host the VM image holding the file share.

Azure Cloud Shell can be accessed from multiple endpoints. It’s integrated into the Azure portal and the Azure mobile app, and also has a standalone website accessible via https://shell.azure.com. If you are accessing ACS via the Azure portal or the app and have already authenticated, you will be automatically logged in and granted access to Azure PowerShell for the current directory. If you are accessing it via the ACS webpage, you will have to authenticate and select the subscription/directory as part of the process. You can also select the type of shell, either Bash or PowerShell.

For the purpose of this article, we are of course interested in PowerShell. The below screenshot reveals some basic information about the environment, such as the version of PowerShell used, the OS on which it’s running, the fact that it’s running in a Hyper-V VM, the IP address of the machine. The PowerShell modules currently available are shown on the right, and additional modules can be added via Install-Module. In addition, some of the environmental variables are shown, which is something that will be important later on.

Azure Cloud Shell

As this is a PowerShell Core instance running on Linux, there are some limitations. For example, even though I can download, install and import the MSOnline module just fine, I’m unable to use it to connect to Azure AD due to an underlying dependency on the ADAL binaries, as illustrated below:

PowerShell Web Access – Just how practical is it?

The same goes for any other module that has a dependence on ADAL, unless it has been ported to .Net Core. Luckily, that’s exactly what Microsoft has done with the Azure modules, including a (preview) version of the Azure AD module you might have spotted in the output of Get-Module above. Therefore, we can use it instead of the MSOnline module in order to manage our Office 365 objects. In fact, there are a few methods you can use to connect to Azure AD, as shown below:

Another example of Azure Cloud Shell

The first method is to simply use the Connect-AzureADService function, which will populate the AccountId and TenantId values based on the environmental variables and log you in automatically. Alternatively, you can run the Connect-AzureAD cmdlet without any parameters, which will trigger the device code OAuth flow, allowing you to complete the interactive authentication process from another endpoint and therefore bypass the limitations of PowerShell Core. Another method is to pass an Access token directly. Regardless of the method you choose, upon successful connection you can run all the familiar AzureAD cmdlets without any issues. This is in contrast with my earlier attempts to run Azure AD cmdlets on PowerShell Core, which resulted in various errors related to the parsing of JSON responses and whatnot.

When it comes to managing Exchange Online objects, PowerShell Core should do just fine, if you can get past the authentication part that is. If you are still using Basic auth, things will just work. If your account is protected by MFA or you have blocked legacy auth and must use the ADAL-enabled ExO module, you are out of luck, as the module is not yet ported to work on .NET Core and any attempt to use the Connect-ExOPSSession cmdlet will fail. But, you can still use various tricks, such as obtaining a token outside of the module and passing it:

Azure Cloud Shell Script

Another nuisance of the Linux VM/PowerShell Core combo is also shown on the screenshot above, namely the rather annoying “ERROR_WSMAN_INVALID_SELECTORS” errors you often run into. This issue was first reported over a year ago by fellow MVP Michel de Rooij on GitHub, and was supposedly fixed this July, however, it doesn’t seem like ACS has received the updates just yet. Issues with other modules can be even more frustrating. For example, the MicrosoftTeams module will fail to install with a DLL dependency error, the SharePoint PnP module will install correctly but fail to connect because of a method dependency, and so on.

Leaving the Office 365 troubles aside, with ASC some of the annoyances with using PowerShell in the browser have been addressed. A basic syntax highlighter is available, and so is cross-session execution history. Instead of having to use command-line utilities such as vi or emacs, we get a built-in script editor. Transferring files in and out of the environment is also possible. And, ACS also supports PowerShell profiles. The downside is stricter browser requirements, which might limit the availability on some systems. Among issues that still plague the experience, here are some highlights: function keys are unusable, the Escape key doesn’t work, copy and paste leaves something to be desired in terms of usability. The case-sensitive file system can quickly get on your nerves when trying to use tab completion.

The “end user” experience aside, ACS can present some administrative challenges. Being a part of the Azure portal, ACS is available anytime, on any device and from any location, which might not be something you desire. In a controlled access environment, restricting access to ACS can be done by blocking the endpoint on your network devices. If you are whitelisting locations from which users can authenticate, such as “named locations” for Conditional Access, you will have to whitelist entire Azure subnets, as the external IP used by the ACS environment is not static. ACS lacks the granularity of PSWA authorization rules and session configurations, or even basic branding controls outside of the Azure AD login branding.

So how practical is Azure Cloud Shell when it comes to performing your daily Office 365 tasks? If all you care about are Azure AD tasks, I’d say the experience is on par with running PowerShell on desktop. If you add Exchange tasks into the mix, things start to fall apart, unless you are still using basic auth. Almost all other Office 365 related modules have issues with running in an ACS environment, so if those are vital to you, I would advise waiting until a .NET Core version of the module is officially released by Microsoft.

Because of the problems with different modules, I would have to give ACS a 6/10, but that’s only when put in the context of Office 365 tasks. That score should change for the better in the coming months, as Microsoft continues developing the shell and ports more modules for PowerShell Core.

Summary and what’s next for Office 365 PowerShell in the browser

In this article, we explored the viability of using browser-based access to PowerShell environments, in the context of performing common Office 365 related tasks. Our first stop was PowerShell Web Access, a proven and reliable gateway allowing you to connect to a PowerShell session running on a machine in your on-premises or cloud environment. While PSWA hasn’t received any major updates in years and the interface can feel a bit clunky nowadays, it still gets the job done in most scenarios.

The other option we examined was Azure Cloud Shell, a PowerShell Core environment running on a Linux VM in Azure. While ACS offers a better experience overall, including file transfers and built-in editor, when it comes to Office 365 tasks it falls short due to the fact that most of the relevant modules are still not ported to .NET Core. Microsoft is working on that task and we already have a preview version of the Azure AD module available and pre-installed in the ACS environment. Moreover, we know that the ExO module is also being worked on. This is evident from the recent code changes made to the CreateExOPSSession script that detects whether the script is running in an ACS environment and updates the script parameters dynamically. A short core excerpt is shown below:

CreateExOpSSession Script

Yes, those are the exact same environmental variables we saw in the previous section, and this is how I know the module is being prepared for ACS. While the ExO module version doesn’t seem to be fully functional on ACS just yet, it should be available early next year. Until then, if you want to see it in action check out the following recording from Ignite 2018. Who knows, perhaps the team can go beyond what’s shown in the video, for example by integrating ACS as part of the Exchange Admin Center, where it can interoperate nicely with the existing “Show cmdlet logging” feature? Here’s hoping they do just that!

About the Author

Vasil Michev

Vasil Michev is an Office Servers and Services MVP, specializing in Office 365. He's currently employed as a Technical Product Manager, and in his free time he can be found helping others in the Office 365 community.

Leave a Reply