Tuesday, January 10, 2012

Loading Remote Assemblies in Powershell with .NET 4

We recently updated our entire codebase to .NET 4.0 from 3.5. There are plenty of blog posts about that process in general, so I'm going to keep this one specific to our Powershell deployment scripts.

We build our code to a central server and then use Powershell to install those build on our Development server. The upgrade to .NET 4 caused 2 problems with Powershell when we called [Reflection.Assembly]::LoadFile()

First:
Powershell by default runs in .NET 2.0. When we tried to load our new 4.0 assemblies, we got this error:


Exception calling "LoadFile" with "1" argument(s): "Could not load file or assembly 'file://\\buildServer\Application\assembly.dll' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded."
At C:\Scripts\Deployment\Deploy.ps1:XX char:XX
+ $assembly = [Reflection.Assembly]::LoadFile <<<< ($file); + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : DotNetMethodException


We need to tell Powershell to run in .NET 4.0 mode. To do that we need to create an app.config file for Powershell. In our case the file was created here: C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe.config, but YMMV based on Powershell's location. The new powershell.exe.config file looks like this:


<?xml version="1.0"?>
<configuration>
 <startup useLegacyV2RuntimeActivationPolicy="true">
  <supportedruntime version="v4.0.30319"/>
  <supportedruntime version="v2.0.50727"/>
 </startup>
</configuration>

Now Powershell can run with both 2.0 and 4.0 assemblies.

Second:
The security features are a bit different when running 2.0 vs 4.0. This difference caused a problem when running assemblies from our Build server on our Development server:


Exception calling "LoadFile" with "1" argument(s): "An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for
more information."
At C:\Scripts\Deployment\Deploy.ps1:XX char:XX
+ $assembly = [Reflection.Assembly]::LoadFile <<<< ($file); + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : DotNetMethodException


Now we simply add the loadFromRemoteSources switch to the powershell.exe.config file we just created so that the entire file looks like this:

<?xml version="1.0"?>
<configuration>
 <startup useLegacyV2RuntimeActivationPolicy="true">
  <supportedruntime version="v4.0.30319"/>
  <supportedruntime version="v2.0.50727"/>
 </startup>
<runtime>
 <loadfromremotesources enabled="true"/>
</runtime>
</configuration>

Now we are successfully loading remote .NET 4.0 assemblies in Powershell. Hope this helps.

4 comments:

miggy said...

Thanks a million - just starting with PS, got stuck, read this and am immediately unstuck :-).
Just an FYI, I'm on PS3 and I needed this config as well.

Unknown said...

Greetings... I found this post to be very exciting to resolve an error I get trying to load a Powershell module .. the import fails and I get a message to use a loadFromRemoteSources switch. I try to load the xml into powershell_ise.exe.config, but it fails with access denied. I'm using Powershell v3 in Win 7 Enterprise. Do you have any suggestions on how I can load the switch?

Thank you very much for your assistance.

Dean

Anonymous said...

I am using Powershell v3, and it gives an access denied error as well when I try to edit powershell_ise.exe.config...

Cameron Ove said...

@continuousdevelopment
You have to take ownership of the file.

Also I just want to say that if you copy and paste this into PowerShell 5 it won't work because it seems to be case sensitive.

this is what I copied and pasted from this post:


This is what actually worked (different case):