{"id":7384,"date":"2023-09-22T18:10:33","date_gmt":"2023-09-22T17:10:33","guid":{"rendered":"https:\/\/rakhesh.com\/?p=7384"},"modified":"2023-11-03T10:16:51","modified_gmt":"2023-11-03T10:16:51","slug":"could-not-load-file-or-assembly","status":"publish","type":"post","link":"https:\/\/rakhesh.com\/azure\/could-not-load-file-or-assembly\/","title":{"rendered":"Could not load file or assembly"},"content":{"rendered":"

Continuing with my efforts to move all my Azure Automation Runbooks to PowerShell 7.2, yesterday I decided to tackle a couple of Runbooks that use the ExchangeOnlineManagement<\/code> module.<\/p>\n

I installed PowerShell 7.2 on the Hybrid Runbook Worker (HRW) and installed the latest version of the ExchangeOnlineManagement<\/code> and Az<\/code> modules on it (this is side by side with the existing version in PowerShell 5.x as I detailed previously<\/a>; long story short, install it via Install-Module<\/code> but with the -Force<\/code> switch).<\/p>\n

Running Connect-ExchangeOnline<\/code> by itself worked fine in the Runbook, but if I use any Az<\/code> cmdlet first (because I need to access the Key Vault etc.) then Connect-ExchangeOnline<\/code> complains:<\/p>\n

Could not load file or assembly 'Microsoft.Identity.Client, Version=4.41.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae<\/pre>\n

Similar results if I login to the HRW and Import-Module Az<\/code> followed by Connect-ExchangeOnline<\/code>:<\/p>\n

OperationStopped: Could not load file or assembly 'Microsoft.IdentityModel.Tokens, Version=6.22.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. Could not find or load a specific\r\nfile. (0x80131621)<\/pre>\n

Slightly different assembly this time, but same stuff I guess.<\/p>\n

Initially this forum post<\/a> seemed like it should do the trick. Apparently I should in an admin PowerShell 7 window (something I missed initially) do the following:<\/p>\n

# Replace the version with whatever the error message shows\r\nInstall-Module -Name Microsoft.Identity.Client -RequiredVersion 4.41.0.0<\/pre>\n

That didn’t work for me though, I kept getting the same error.<\/p>\n

I learnt of this cmdlet to identify the loaded assemblies though:<\/p>\n

[System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object Location | Sort-Object -Property FullName | Select-Object -Property FullName, Location, GlobalAssemblyCache, IsFullyTrusted | Out-GridView<\/pre>\n

This showed me that with the Microsoft.Identity.Client<\/code> assembly at least the file isn’t loaded. But this is probably a red-herring coz maybe ExchangeOnlineManagement<\/code> is failing before it reaches this stage, when I am trying this manually.<\/p>\n

\"\"<\/p>\n

The other assembly is present though.<\/p>\n

\"\"<\/p>\n

And as you can see it’s present both from the Az<\/code> module, as well as the ExchangeOnlineManagement<\/code> module – which is the cause of this conflict.<\/p>\n

Turns out you can load assemblies manually, like this for instance:<\/p>\n

Add-Type -Path 'C:\\Program Files\\PowerShell\\Modules\\ExchangeOnlineManagement\\3.3.0\\netCore\\System.IdentityModel.Tokens.Jwt.dll'<\/pre>\n

Or even:<\/p>\n

Add-Type -AssemblyName 'Microsoft.IdentityModel.Tokens, Version=6.22.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'<\/pre>\n

I tried that, but it didn’t help. ExchangeOnlineManagement<\/code> continued complaining.<\/p>\n

Hmm.<\/p>\n

What exactly is an assembly? From this post<\/a>:<\/p>\n

An assembly is a packaged chunk of functionality (the .NET equivalent of a DLL). Almost universally an assembly will consist of exactly one file (either a DLL or an EXE). To make things confusing, the naming convention for assemblies is very similar to the naming convention for namespaces. Be warned: they are not the same thing! An assembly may contain classes from many namespaces, and a namespace may cover many assemblies. Although not strictly correct, you can think of the assembly as the physical file containing the executable code and a namespace as a category to organize all the code that relates to a particular area.<\/p>\n

Namespaces are just labels that are used to allow classes from different assemblies to have the same name and not interfere with each other. Much like classes form a container, or boundary, for their members (so many different classes can have an \u201cOpen()\u201d method, for example), namespaces form a container for classes with the same name. Native .NET namespaces start with \u201cSystem\u201d (this is the root of the .NET namespace universe). The convention for application-specific namespaces is to start them with {CompanyName}.{ProductName}. So, for example, you may see Microsoft.Office.{some more stuff} for Office-related classes.<\/p><\/blockquote>\n

Ok, so we are back to the DLL hell days. \ud83d\ude42<\/p>\n

I then found this good article<\/a> about assemblies and conflicts.<\/p>\n

In .NET, dependency conflicts occur when two versions of the same assembly are loaded into the same Assembly Load Context<\/em>. This term means slightly different things on different .NET platforms, which is covered later<\/a> in this article. This conflict is a common problem that occurs in any software where versioned dependencies are used.<\/p>\n

Conflict issues are compounded by the fact that a project almost never deliberately or directly depends on two versions of the same dependency. Instead, the project has two or more dependencies that each require a different version of the same dependency.<\/p><\/blockquote>\n

Interestingly, turns out PowerShell’s own dependencies can conflict with the dependencies of its module<\/a>s. I have encountered this in the past, for the specific example given in that post, but hadn’t realized. That article has some good suggestions too<\/a> on what to do, but none of them apply to my situation of using HRWs… I think.<\/p>\n

While all this was good info, eventually I was still stuck without a real solution.<\/p>\n

I tried to remove all the Az<\/code> modules after they had done their work, and then run Connect-ExchangeOnline:<\/p>\n

Get-Module | Where-Object { $_.Name -match \"^Az\" } | Remove-Module\r\nConnect-ExchangeOnline @connectParams<\/pre>\n

Failed.<\/p>\n

Same if I remove the Az<\/code> modules and also ExchangeOnlineManagement<\/code>.<\/p>\n

What if I load the ExchangeOnlineManagement<\/code> module first?<\/p>\n

Import-Module ExchangeOnlineManagement\r\nImport-Module Az\r\n\r\n# Do the Azure stuff...\r\n\r\n# Then connect to ExO\r\nConnect-ExchangeOnline @connectParams<\/pre>\n

I have to load the Az<\/code> module before connecting, coz that’s how I get my certs from the Key Vault.<\/p>\n

Nope, doesn’t work!<\/p>\n

Any difference if I load the specific ones I need?<\/p>\n

Import-Module ExchangeOnlineManagement\r\nImport-Module Az.Accounts\r\nImport-Module Az.KeyVault\r\n\r\n# Do the Azure stuff...\r\n\r\n# Then connect to ExO\r\nConnect-ExchangeOnline @connectParams<\/pre>\n

No way, that worked!!<\/p>\n

But that was when I tested on the HRW directly. What if I do this in the Runbook? Does it work?<\/p>\n

It actually did! Whee! \ud83d\ude31\ud83e\udd73<\/p>\n

Weird thing is if I look at the loaded assemblies the version ExchangeOnlineManagement<\/code> wants isn’t even loaded:<\/p>\n

\"\"<\/p>\n

Update<\/strong>: This also solves issues with PnP.PowerShell<\/code>. Even though I am not specifically loading it above, in my code I am doing Connect-PnPOnline<\/code> after Connect-ExchangeOnline<\/code> and it works fine.<\/p>\n

Update (3rd Nov 2023)<\/strong>: While reading Tony Redmond’s blog I see that he too encountered this last month<\/a>. Like he said “…it\u2019s disappointing that two Microsoft engineering groups working in the Microsoft 365 ecosystem cannot agree on which version of a critical DLL to use.<\/em>” Disappointing indeed.<\/p>\n","protected":false},"excerpt":{"rendered":"

Continuing with my efforts to move all my Azure Automation Runbooks to PowerShell 7.2, yesterday I decided to tackle a couple of Runbooks that use the ExchangeOnlineManagement module. I installed PowerShell 7.2 on the Hybrid Runbook Worker (HRW) and installed the latest version of the ExchangeOnlineManagement and Az modules on it (this is side by … Continue reading Could not load file or assembly<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[887],"tags":[1018,1110,227],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/posts\/7384"}],"collection":[{"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/comments?post=7384"}],"version-history":[{"count":7,"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/posts\/7384\/revisions"}],"predecessor-version":[{"id":7426,"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/posts\/7384\/revisions\/7426"}],"wp:attachment":[{"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/media?parent=7384"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/categories?post=7384"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rakhesh.com\/wp-json\/wp\/v2\/tags?post=7384"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}