In case you have missed the news, last week a new flavor of PowerShell, namely PowerShell Core was officially released. In this article, we will take a closer look at PowerShell Core in the context of Office 365.
What is PowerShell Core?
If you are reading this article, you should be well aware what PowerShell is. So, what exactly is PowerShell Core then?
Windows PowerShell is built on .NET and can work with the actual .NET objects, which is one of its main strengths. The dependence on .NET has its downsides however. It’s a Microsoft proprietary technology and it is only available on Windows, which in today’s world is fairly limiting. And it has a very large footprint, especially if all your application needs are few basic HTTPS calls. To address all these and more, Microsoft released .NET Core – an open-source, cross-platform, minimal subset of .NET functionalities. And it’s what PowerShell Core is built on.
In turn, this means that PowerShell Core is available on Windows, macOS and Linux. It is open-source and you can get the source code directly on GitHub. It also means that Core offers only a subset of the features “standard” PowerShell has. For example, the lack of support for Windows Forms in .NET Core means that you cannot use any UI elements in PowerShell Core, such as Show-Command or Out-GridView. Workflows are another example of functionality built on top of .NET that is missing in its Core variant. In effect, PowerShell Core does NOT replace “standard” PowerShell – in fact you can run both side by side. And you will have to do so for a while, especially when it comes to Office 365 management. We will talk about this in the next sections, but first, let’s see how to get and use PowerShell Core.
Using PowerShell Core
As a first step, you will need to download and install PowerShell Core by getting the package appropriate for your OS. All packages are available on GitHub. For a Windows machine, you can either use the .MSI installer or the .ZIP archive, though only the former will take care of the prerequisites needed to run PowerShell Core.
Once installed, you can run PowerShell Core by clicking the corresponding item in the start menu (should be called PowerShell-6.0.0). From there, you can start exploring. The core elements are the same, for example the screenshot below illustrates the use of the variables PSDrive, the $Profile variable (different than the $Profile variable on “regular” PowerShell), the use of the Test-Path cmdlet to check whether the file exists and calling in an external program (Notepad) to open the file:
The lower part of the screenshot shows the effect of the changes I made to the profile – the PowerShell Core prompt has a new look, a new working directory is pushed on start and the PowerShell transcript is automatically started. In addition, the output of the $PSVersionTable variable is displayed, with the new PSEdition property designating this as “Core” version.
Almost every element of the PowerShell language will work the same and so will many of the existing PowerShell modules – PowerShell Core is designed to be backwards-compatible with previous Windows PowerShell version. That said, there are some easy to spot differences. As this is a separate PowerShell “installation”, the modules you have added to your “regular” Windows PowerShell will not be available. You will have to reinstall them or use the WindowsPSModulePath module as detailed here. The automatic module loading functionality is a bit unreliable, so you might want to revert back to using Import-Module with your scripts. Other changes or limitations are far worse, such as the lack of support for Windows Forms we mentioned above. A list of breaking changes with PowerShell Core can be found in this article.
On the other hand, some new features have been introduced. For example, support for SSH for PowerShell remoting, new cmdlets or cmdlet parameters and more, as detailed in this article. Looking at all the changes, you might notice a trend – every platform-dependent functionality is removed or de-emphasized, and the focus is shifted to cross-platform support. In other words, one should not aim to replace all the scripts and processes built around Windows PowerShell with PowerShell Core, but only use it where it makes sense.
The first clash with Office 365 – Files on demand
The first issue I run into when playing with PowerShell Core was the different way it handles the Files on demand feature. OK, technically Files on demand is not pure Office 365 feature, but bear with me.
Being part of the operating system, Windows PowerShell “understands” Files on demand. PowerShell Core on the other hand behaves differently – simply trying to enumerate items in a folder containing “online-only” files will cause OneDrive to fetch the file, because of the different methods used by PowerShell Core. In effect, an operation that should take just few milliseconds is prolonged by the amount of time it takes OneDrive to download each individual file:
Now imagine running that in a folder that features several hundred OneDrive items, or say few big “online-only” video files! Troubling, right? Now go check your scripts.
Using PowerShell Core with Exchange Online
Moving on to Office 365 and managing the different workloads. As connecting to Exchange Online Remote PowerShell via the old, basic authentication method does not require any module, PowerShell Core just works.
The exact same cmdlets were used to connect to Exchange Online and the exact same cmdlets will be available to manage Exchange objects, as this is a remote session and it does not depend on the host environment. Thus, most of your Exchange Online scripts should continue to work as expected. And so should Security and Compliance Center connectivity and scripts.
The good news end here. Did you notice the password prompt on the screenshot above? Instead of the familiar Credentials dialog, I was asked to provide credentials in the console. No big deal, right? Wrong.
It’s actually a very big deal and one of the major problems with PowerShell Core and Office 365 management. The lack of support for many of the .NET components means that you will run into a variety of issues with most of the Office 365 related modules, as they rely on additional assemblies, most of which are not compatible with Core. For example, connecting with the ADAL-enabled Exchange Online module will fail as a required assembly cannot be loaded:
You can try working around this by manually obtaining a token using the ADAL DLLs (which actually load fine), but this will result in a similar error message for another dependency, this time the ‘System.Security.Cryptography.SHA256Cng’ component.
Use PowerShell Core with other Office 365 workloads
The issue gets more irritating when you try connecting to other Office 365 workloads. The same error will pop up if you try to run the ADAL-enabled version of the MSOnline module (or the AzureAD module):
It gets even worse - because of unhandled exceptions in many of the modules, PowerShell Core will not only fail to connect, but will crash every time you try to run the Connect-MsolService or the Connect-AzureAD cmdlet without providing the -Credentials parameter. Remember the bit we mentioned above about the lack of support for Windows Forms? Well, running those cmdlets without the -Credentials parameter means an authentication dialog should pop up. PowerShell Core cannot handle that and just crashes.
Trying the other Office 365 related modules doesn’t yield better results. Neither the SharePoint Online, SharePoint Online PnP, Skype for Business Online, Teams nor the Azure Information Protection PowerShell modules will work. Only connecting to Exchange Online and the Security and Compliance Center is possible, and only if you are using the legacy authentication methods. Which you should *not* be using now that we have the ADAL-enabled (Windows PowerShell) module for ExO/SCC.
The issues listed above are related to the fact that all of these modules rely on one or more additional assemblies to handle authentication to Azure AD. Every ADAL-enabled module will have trouble because of the UI components, and even if you try to bypass them there are other dependencies which will cause PowerShell Core to fail. If you obtain an access token via other methods, say calling the ADAL APIs via Windows PowerShell, you can bypass the login prompts. Unfortunately, the module cmdlets themselves might rely on methods not available with PowerShell Core. For example, the below screenshot illustrates successful connection to Azure AD via access token, however any Get- cmdlet fails with JSON parsing error:
The Microsoft Teams module will have similar issues, which is not surprising considering both AzureAD and Teams PowerShell modules are just wrappers for the Graph API calls.
PowerShell Core is now generally available and you might want to start looking into it, especially if you are managing a heterogenous environment with different flavors of Windows and Linux devices. Keep in mind that Core is not positioned as a replacement for Windows PowerShell, so there is no immediate pressure on starting to port your scripts and tools to it. And there might never be! As this is the first public release, it seems like a good idea to wait for a while and give the authors of the different modules time to adapt their code to work within the constraints of .NET Core.
When it comes to using PowerShell Core for Office 365 management, things are currently… challenging. Of all the common workloads, only Exchange Online works flawlessly with PowerShell Core, but you must use the old basic authentication route. Working with the other workloads is currently not possible, as the ADAL libraries and their dependencies do not work with PowerShell Core, thus making authentication next to impossible. Another incompatibility is added by handling the JSON payload from any Graph API calls, which means that even if you do connect, you might not be able to execute any cmdlet.
Overall, the Office 365 administrators should stick to using Windows PowerShell for the time being. Do keep an eye on PowerShell Core though, this is its initial Public version and compatibility with older PowerShell modules should improve in the future.