Long-Term Stability for using Microsoft 365 and Azure Modules

Hi

We are using PowerShell Universal heavily to automate tasks in the Microsoft 365 and Azure cloud. Over time, we face many issues with PowerShell Universal regarding compatibility between PowerShell Universal and the Microsoft Cloud modules (Exchange Online, Teams, Az.*, Graph, etc.).

Normally it’s like this:

Are there any plans to improve the stability of running PowerShell scripts in PSU with Microsoft Cloud modules? I’m not sure how it’s implemented, but I think about ideas like running the scripts out of process and directly calling pwsh.exe. If this is the case, maybe try to reduce the dependencies loaded in the script to connect to PSU, so that only PSU private code is required without any Microsoft Cloud DLL dependencies.

I may be making this a little too easy, but as a user it would be ideal if a script that runs locally on pwsh.exe also runs on PSU (assuming the same module versions are installed).

Looking forward to your feedback.

Bests,
Claudio

Product: PowerShell Universal
Versions: 5.3.2 / 5.4.4
1 Like

TLDR: I know. We need to test very specific versions of these modules against very specific versions of PSU and publish a compatibility matrix. The technical solution isn’t an easy fix and ever changing.

Long story:

It is a bit more complicated than just running the modules in a separate process as that is what PSU is already doing. If you set up minimal environments in PSU that use pwsh.exe, it won’t load any PSU libraries like you are suggesting. If you setup PSU with environments that use pwsh.exe but aren’t minimal, it loads host DLLs that have an assembly base path of the libraries in the PSU install dir and not necessarily pwsh.exe install dir.

The greater problem is that this is a moving target with at least 4 packs of assemblies that are constantly changing. For example, EXO, Graph and Teams all used different Microsoft.Identity.Client assembly versions, while PSU used the latest version. One example of this moving target is that Microsoft actually introduced a breaking change in the assembly in a non-major build. For example, 1.30.0 works but if you upgrade to 1.50.0, it no longer has the methods required by the module. They removed a public method in the latter versions (MissingMethodException). This is exactly what caused one issue between PSU and these modules.

This isn’t limited to just PSU, as Microsoft is also struggling to keep these modules working in their own products.

The problem gets worse if you start to mix the modules together. Because PowerShell requires modules to implement an assembly load context per module, if any of the modules don’t implement it properly, we get assembly binding issues. It’s also problematic if Microsoft makes breaking changes in non-major versions because, even with ALCs implemented, they may be implemented in a way that doesn’t expect this to happen.

All this to say, it’s a very annoying technical problem because it’s one that you can fix on your end only to be broken by an update to the Module. Additionally, it’s very easy to make a change to PSU at a later time via a Nuget package update to an unrelated package, that just happens to update a dependency of one of the modules. Additionally, mixing modules or loading modules in different orders can cause issues so tests even become a bit tricky.

We’ve taken some steps in 5.5.0 that make this work but also hopefully help prevent this from happening in the future. I don’t think it’s a golden ticket.

  • Reduce dependencies in our host and cmdlets assemblies
  • Pin to a specific version of Microsoft.Identity.Client that works for all modules and PSU
  • Setup tests to ensure we catch updates to problematic assemblies even if they aren’t directly referenced

We are also looking to setup a test to verify modules directly in PSU. Effectively, we could setup a matrix of module versions, load them in PSU, perform a few operations to validate they work and then publish that as a simple table for our users to have with every release.

I also want to just state that the argument that is works in pwsh.exe on the same server and not in PSU is valid. I wouldn’t expect our users to try anything different.

The problem is that PSU also integrates with a lot of Azure stuff, like Authentication, so it brings in a bunch of assemblies that stock PowerShell doesn’t have.

Hi Adam

Thank you very much for your detailed response and explanation. Good to hear, that our goals are aligned.

We never tried minimal environments, that’s a good point, we will check that out. And we are planning to update to PSU 5.5.0 this week.

I agree with you, that the root cause of all our issues lies within Microsoft and the inability of the M365 product teams to harmonize the PowerShell module development and that they don’t stick strictly to schematic versioning. In my understanding, there should be a core module for the core workload like authentication (a Microsoft-managed successor of MSAL.PS). And all other Microsoft Cloud modules should be able to depend “the PowerShell way” with cmdlets on that core module. I hope such an improvement or something similar is planned within Microsoft (of course, the problem is much more complex, but there has to be a better solution as the current state).

And yes, it’s also a problem even outside of PSU directly in pwsh.exe - as you pointed out, it’s hard to get multiple Microsoft Cloud modules to work in the same session. We have to choose the versions carefully. And if we found a version mix of all modules and PSU, we normally keep that for months until a new feature or a change triggers us to upgrade a component.

Better testing is good, importing the modules and connecting to the services should show the major issues.

Thank you!

Bests
Claudio

1 Like