Module import loads wrong assembly version - powershell

I have a module manifest that specifies an explicit path to some Visual Studio 2015 assemblies (version 14.0.0.0) in the "RequiredAssemblies" array. After running Import-Module I am getting the 12.0.0.0 version assemblies added to the session instead.
RequiredAssemblies = #(
"${env:ProgramFiles(x86)}\Microsoft Visual Studio 14.0\Common7\IDE\TestAgent\Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
)
After running the following command:
[appdomain]::currentdomain.getassemblies() | sort -property fullname | format-table fullname
I can see that only the 12.0.0.0 version of the assembly was added to my current session:
Microsoft.TeamFoundation.WorkItemTracking.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
I have ensured that the *.dll at the path is the version I am looking for... Why is powershell forcing the previous version? Is there any way to prevent it from doing so?

Related

Why does a newer version of the PowerShell module run when I import with -MaximumVersion set to a lower version?

I have a build script that depends on an older version of one of our modules. Version 1.0.1. I added -MaximumVersion 1.0.1 to the Import-Module command. When the build script runs it fails and error shows that it is running code in version 2.1.0 of the module.
Import-Module DrilQuip.Build -MaximumVersion 1.0.1 -Force
Creating next version number...
The property 'VersionFilePath' cannot be found on this object. Verify that the
property exists.
At C:\Users\svcTFSBuildProd\Documents\WindowsPowerShell\Modules\DrilQuip.Build\
2.1.0\DrilQuip.Build.psm1:253 char:5
I have tried with and without the -Force switch but that makes no difference.
I used Get-Module DrilQuip.Build -ListAvailable to confirm that version 1.0.1 is present on the computer.
How can I insure that the script imports and uses the older version of the modules?
Update 1
Added -Verbose switch to get more details about what is happening. Here are the results:
VERBOSE: Loading module from path 'C:\Program
Files\WindowsPowerShell\Modules\DrilQuip.Build\1.0.1\DrilQuip.Build.psd1'.
VERBOSE: Populating RepositorySourceLocation property for module
DrilQuip.Build.
Creating next version number...
The property 'VersionFilePath' cannot be found on this object. Verify that the
property exists.
At C:\Users\svcTFSBuildProd\Documents\WindowsPowerShell\Modules\DrilQuip.Build\
2.1.0\DrilQuip.Build.psm1:253 char:5
+ $Matches = Select-String -Path $global:BuildConfig.VersionFilePat ...
This shows that the same module has been installed into 2 different locations. The location C:\Users\svcTFSBuildProd... seems to trump the location C:\Program Files\WindowsPowerShell...
I think this has to do with Machine vs User scope on the module installation. I'll go back and remove the User scoped modules and install all versions of the module with Machine scope and see if that helps.
Update 2
I removed all of the versions of the module from user scope folder and then tried the script again. It is still failing but now both versions of the module are coming from the same module folder location.
VERBOSE: Loading module from path 'C:\Program
Files\WindowsPowerShell\Modules\DrilQuip.Build\1.0.1\DrilQuip.Build.psd1'.
VERBOSE: Populating RepositorySourceLocation property for module
DrilQuip.Build.
Creating next version number...
The property 'VersionFilePath' cannot be found on this object. Verify that the
property exists.
At C:\Program
Files\WindowsPowerShell\Modules\DrilQuip.Build\2.0.4\DrilQuip.Build.psm1:251
char:5
Since the new version is still trumping the max version I requested my theory that user scope trumps machine scope is not the real problem. Something else is going on.
I ran Get-Module -Name DrilQuip.Build -ListAvailable again and I notice that the ModuleType is different. On version 1.0.1 the type is Manifest but on versions 1.1.1 and 2.0.4 the type is Script. Maybe this difference is causing the problem.
ModuleType Version Name
---------- ------- ----
Script 2.0.4 DrilQuip.Build
Script 1.1.1 DrilQuip.Build
Manifest 1.0.1 DrilQuip.Build
I will remove all of the modules and reinstall them from the repository.
The older version of the module 1.0.1 has the type Manifest and all the versions after that are type Script. The next version of the module 1.0.2 is also compatible with my build script so I changed the -MaximumVersion parameter to 1.0.2.
Before trying this out I also uninstalled all versions of the module on the computer and then installed just versions 1.0.2 and 2.1.0 that are really needed. I ran PowerShell as administrator so both modules installed into the folder C:\Program Files\WindowsPowerShell\Modules
PS C:\Program Files\WindowsPowerShell\Modules\DrilQuip.Build> get-module DrilQuip.Build -li
Directory: C:\Program Files\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 2.1.0 DrilQuip.Build {Start-Build, Write-FileCopyResult, Invoke-MSBuild, New-Da...
Script 1.0.2 DrilQuip.Build {Get-NextVersion, Set-TfsWorkspaceFileTime}
After these changes the build script works and uses the 1.0.2 version of the code as expected.
VERBOSE: Loading module from path 'C:\Program
Files\WindowsPowerShell\Modules\DrilQuip.Build\1.0.2\DrilQuip.Build.psd1'.
VERBOSE: Populating RepositorySourceLocation property for module
DrilQuip.Build.
VERBOSE: Loading module from path 'C:\Program
Files\WindowsPowerShell\Modules\DrilQuip.Build\1.0.2\DrilQuip.Build.psm1'.
VERBOSE: Importing function 'Get-NextVersion'.
VERBOSE: Importing function 'Set-TfsWorkspaceFileTime'.
Creating next version number...
New version: 10.2.10928.11004
Based on mklement0's comment it seems that whole problem is that version 1.0.1 was not setup correctly and so no functions got imported. The verbose output from Import-Module confirms that. When the script called the function Get-NextVersion PowerShell used module auto loading to find and load a version of the module that did have the function.
Version 1.0.1 was missing a value for RootModule in the manifest. That error was fixed in version 1.0.2. The module uses Export-ModuleMember to export the functions instead of the FunctionsToExport setting in the manifest. Since 1.0.1 did not have root module set to the psm1 file it had no way to export the functions..

How to package a multi-architecture .NET library that targets the Universal Windows Platform?

How do I package a Universal Windows Platform library written in C# that offers only architecture-dependent builds? For the sake of illustration, let's say that I have some architecture-specific code conditionally compiled in for each architecture (using #if ARM and equivalents).
To be clear, no AnyCPU build exists for my library - only x86, x64 and ARM.
An equivalent and potentially more common situation is one where I have a dependency on an external library that is only provided as architecture-specific builds (e.g. Win2D). To keep the context simple, let's assume there are no dependencies and only my own code is involved - the solution should reduce to the same thing either way.
This is a series of questions and answers that document my findings on the topic of modern NuGet package authoring, focusing especially on the changes introduced with NuGet 3. You may also be interested in some related questions:
How to package a .NET Framework library?
How to package a .NET library targeting the Universal Windows Platform?
How to package a portable .NET library targeting .NET Core?
How to package a .NET library targeting .NET Framework and Universal Windows Platform and include platform-specific functionality?
How to package a .NET library that targets the Universal Windows Platform and depends on Visual Studio extension SDKs?
This answer builds upon the principles of .NET Framework library packaging and the principles of Universal Windows Platform library packaging. Read the linked answers first to better understand the following.
I will assume that all your architecture-specific builds expose the same API surface, with only the implementation of those APIs differing.
The main complication with this scenario is that the build toolchain requires an AnyCPU assembly for compile-time reference resolution, even if this assembly is never used at runtime. Since your scenario does not have AnyCPU build output, we need to find a workaround. The concept that applies here is of reference assemblies - AnyCPU assemblies only used at compile-time for reference validation. Therefore, to publish your library, you will need to create a reference assembly and package the assets as outlined below.
For simplicity, I will assume that your library has no dependencies on other NuGet packages. This is not likely to be the case in practice but dependency management is already covered by the other answers linked above and is therefore omitted from this answer.
The desired structure of the NuGet package is as follows:
+---ref
| \---uap10.0
| | MultiArchitectureUwpLibrary.dll
| | MultiArchitectureUwpLibrary.pri
| | MultiArchitectureUwpLibrary.XML
| |
| \---MultiArchitectureUwpLibrary
| ArchitectureControl.xaml
| MultiArchitectureUwpLibrary.xr.xml
|
+---runtimes
| +---win10-arm
| | \---lib
| | \---uap10.0
| | MultiArchitectureUwpLibrary.dll
| | MultiArchitectureUwpLibrary.pdb
| |
| +---win10-x64
| | \---lib
| | \---uap10.0
| | MultiArchitectureUwpLibrary.dll
| | MultiArchitectureUwpLibrary.pdb
| |
| \---win10-x86
| \---lib
| \---uap10.0
| MultiArchitectureUwpLibrary.dll
| MultiArchitectureUwpLibrary.pdb
If you have familiarized yourself with the answers linked above, the files should all be known to you already, although the directory structure is rather unusual in this case. The ref directory contains the reference assembly, the XML documentation and the resource files, while the architecture-specific assets are structured under the runtimes directory.
Most of this is pretty straightforward and can be accomplished by using a nuspec file created based on the following template:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.2">
<id>Example.MultiArchitectureUwpLibrary</id>
<version>1.0.0</version>
<authors>Firstname Lastname</authors>
<description>Example of library that is published as a set of architecture-specific assmeblies for the UWP platform.</description>
</metadata>
<files>
<!-- Architecture-independent reference library for use at compile-time; generated by the PowerShell script. -->
<file src="..\bin\Reference\Release\MultiArchitectureUwpLibrary.dll" target="ref\uap10.0" />
<!-- XML documentation file goes together with the reference library. -->
<file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.xml" target="ref\uap10.0" />
<!-- Resource files go together with the reference library. -->
<file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.pri" target="ref\uap10.0" />
<file src="..\bin\x86\Release\MultiArchitectureUwpLibrary\*" target="ref\uap10.0\MultiArchitectureUwpLibrary" />
<!-- The architecture-specific files go in architecture-specific directories. -->
<file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-x86\lib\uap10.0" />
<file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-x86\lib\uap10.0" />
<file src="..\bin\x64\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-x64\lib\uap10.0" />
<file src="..\bin\x64\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-x64\lib\uap10.0" />
<file src="..\bin\arm\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-arm\lib\uap10.0" />
<file src="..\bin\arm\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-arm\lib\uap10.0" />
</files>
</package>
The missing piece, of course, is the reference assembly. Thankfully, this is rather simple to solve - a reference assembly is an AnyCPU assembly that defines the same classes and methods that the runtime assemblies contain. Its main purpose is to provide the compiler a reference to work with, so the compiler can verify that all method calls are actually referencing methods that will exist at runtime. The actual code in the reference assembly (if any) is not used for anything.
Since all your architecture-specific builds expose the same API surface, we can simply take any one of them and instruct the compiler to use it as the reference assembly. The Windows SDK contains a utility named CorFlags.exe that can be used to convert an x86 assembly to an AnyCPU assembly, making this possible.
Below is a package creation script that creates the required reference assemblies before packaging the library. It assumes the Windows SDK is installed in the standard location. To understand the details of the logic, see the inline comments.
# Any assembly matching this filter will be transformed into an AnyCPU assembly.
$referenceDllFilter = "MultiArchitectureUwpLibrary.dll"
$programfilesx86 = "${Env:ProgramFiles(x86)}"
$corflags = Join-Path $programfilesx86 "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\x64\CorFlags.exe"
If (!(Test-Path $corflags))
{
Throw "Unable to find CorFlags.exe"
}
$solutionRoot = Resolve-Path ..\..
$topLevelDirectories = Get-ChildItem $solutionRoot -Directory
$binDirectories = $topLevelDirectories | %{ Get-ChildItem $_.FullName -Directory -Filter "bin" }
# Create reference assemblies, because otherwise the NuGet packages cannot be used.
# This creates them for all outputs that match the filter, in all output directories of all projects.
# It's a bit overkill but who cares - the process is very fast and keeps the script simple.
Foreach ($bin in $binDirectories)
{
$x86 = Join-Path $bin.FullName "x86"
$any = Join-Path $bin.FullName "Reference"
If (!(Test-Path $x86))
{
Write-Host "Skipping reference assembly generation for $($bin.FullName) because it has no x86 directory."
continue;
}
if (Test-Path $any)
{
Remove-Item -Recurse $any
}
New-Item $any -ItemType Directory
New-Item "$any\Release" -ItemType Directory
$dlls = Get-ChildItem "$x86\Release" -File -Filter $referenceDllFilter
Foreach ($dll in $dlls)
{
Copy-Item $dll.FullName "$any\Release"
}
$dlls = Get-ChildItem "$any\Release" -File -Filter $referenceDllFilter
Foreach ($dll in $dlls)
{
Write-Host "Converting to AnyCPU: $dll"
& $corflags /32bitreq- $($dll.FullName)
}
}
# Delete any existing output.
Remove-Item *.nupkg
# Create new packages for any nuspec files that exist in this directory.
Foreach ($nuspec in $(Get-Item *.nuspec))
{
.\NuGet.exe pack "$nuspec"
}
You may need to adjust the paths in the script to match the conventions used in your solution.
Run this script to create a NuGet package that enables your library to be used in all its architecture-specific variants! Remember to build your solution using the Release configuration for all the architectures before creating the NuGet package.
A sample library and the relevant packaging files are available on GitHub. The solution corresponding to this answer is MultiArchitectureUwpLibrary.

Update-Module duplicates module instead of updating

I'm using PowerShellGet to install my own PowerShell module from my local Nuget feed (packed and published as here).
I'm installing it once like this:
Install-Module MyModule -Scope CurrentUser
And then I'm updating it like this:
Update-Module MyModule
It works as expected, but after every version update I'm getting duplicate of module instead of override:
Get-Module MyModule -ListAvailable
# Output
Directory: C:\Users\user\Documents\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 1.0.0.40 MyModule {...}
Manifest 1.0.0.39 MyModule {...}
Manifest 1.0.0.38 MyModule {...}
Functions which were removed in latest version remain available and PowerShell ISE auto complete shows duplicates for every function:
Update
#CmdrTchort proposed additional checks for investigation, here are results.
Check where modules are installed
Get-Module -Name MyModule -ListAvailable | %{ $_.ModuleBase }
As expected, PowerShellGet installed them to %USERPROFILE%\Documents\WindowsPowerShell\Modules:
C:\Users\user\Documents\WindowsPowerShell\Modules\MyModule\1.0.1.1
C:\Users\user\Documents\WindowsPowerShell\Modules\MyModule\1.0.0.40
C:\Users\user\Documents\WindowsPowerShell\Modules\MyModule\1.0.0.39
C:\Users\user\Documents\WindowsPowerShell\Modules\MyModule\1.0.0.38
I see that $PSScriptRoot contains that path.
Check what Update-Module will do
Update-Module -Name MyModule -WhatIf
# Result
# What if: Performing the operation "Update-Module" on target "Version '1.0.1.1' of module 'MyModule', updating to version '1.0.1.2'".
Check, which version is actually imported:
Start new powershell console
Run Get-Module to ensure that MyModule not imported at all
Run any cmdlet from MyModule
Check, that only one, latest version of MyModule was imported by executing Get-Module again
For me it is:
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 1.0.1.2 MyModule {...
Conclusion
It seems, that there is no issue here, as #CmdrTchort wrote it is expected that Get-Module -ListAvailable shows all versions installed.
I think that behavior of PowerShell auto-complete in powershell and powershell_ise is a bit confusing though since it shows duplicates of same function if few versions available and functions removed from latest version, probably will be changed soon or somehow configurable.
How are you bundling your module when you're packaging your new versions?
Powershell supports multiple versions of the same module to be loaded (ListAvailable gives you the various versions).
Can you do a Get-module MyModule -ListAvailable and print the full-module path to check how they're installed?
Are you updating your Module-manifest for each version and bundling this in the new folder?
You can load both versions of the same module into your Powershell Session; if so the last imported cmdlets are the one that are effective.
Import-Module also supports -MimimumVersion og RequiredVersion.
As you're experiencing trouble with the regular Import-Module I'm suspecting something messy with how it's structured and the PSModulePath.
Maybe the $PSModulePath is concated with the "version" path for each new upgrade instead of updating it as it usually does? In which case; the "first" version might be the one loaded, getting the presedence rule over the other. I would do a quick check too see what's in $PSModulePath.
When you do a Update-Module check to see what it updates with the -WhatIf switch.
Some links for installing modules/working with modules:
https://msdn.microsoft.com/en-us/library/dd878350(VS.85).aspx
https://technet.microsoft.com/en-us/library/dn807166.aspx
Hope this helps :)

How do I reference an assembly from the GAC in a PowerShell module?

I'm writing a PowerShell module which depending on the SMO assemblies in SQL Server. (The only one I need to reference is Microsoft.SqlServer.Smo.dll)
When I've been developing module. I've just taken a copy of the assembly I need and referenced it in my manifest file similar to this:
RequiredAssemblies = #(
"$env:userprofile\Documents\WindowsPowerShell\Modules\Dependencies\Microsoft.SqlServer.Smo.dll"
)
I would assume that in a production environment you'd want to reference assemblies from a standard location. I'd assume a standard location would be the GAC.
If I reference the assembly from the GAC, I get this, but this looks like it could break if a new version of the assembly is installed:
RequiredAssemblies = #(
'C:\WINDOWS\assembly\gac_msil\Microsoft.SqlServer.Smo\12.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Smo.dll'
)
I could also reference the assembly directly from the SDK:
RequiredAssemblies = #(
'C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.Smo.dll'
)
Another option I've considered (but possibly might be breaking a EULA somewhere) is to copy the assemblies to a server share and reference that like so:
RequiredAssemblies = #(
'\\MyServer\PowerShellDependencies\Microsoft.SqlServer.Smo.dll'
)
But how should I be doing this?
If it's relevant, all computers that this module will be installed on are 64-bit and will have the SMO libraries installed. Also, this isn't a publicly available piece of software, it's being deployed on a company network.
Update: I've tried only specifying the name of the assembly in the manifest and this appears to work.
RequiredAssemblies = #(
'Microsoft.SqlServer.Smo.dll'
)
Unless you're going to deploy the SMO assemblies as private assemblies, then I wouldn't recommend loading them from direct references.
To load from the GAC, use the Add-Type cmdlet with the fully qualified assembly name...
Add-Type -AssemblyName "Microsoft.SqlServer.Smo, Version=12.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"
Related:
How do I use Add-Type to load Microsoft.Web.Deployment?
You can also specify just the assembly without the version info:
Add-Type -AssemblyName "System.Xml.Linq"

Is it possible to generate a NuGet package with the same version as the assembly?

I would like to automatize the release of NuGet packages for a private repository.
For the version it would be great to be able to use the same version as the one generated by VisualStudio, with
[assembly: AssemblyVersion("1.0.*")]
For now, the only way I found is :
(Get-ChildItem -Filter .\bin\Release\MyPackage.dll | Select-Object -ExpandProperty VersionInfo).ProductVersion | ForEach-Object { nuget pack -properties configuration=Release -version $_}
Do you have any better solution?