Quick Learn – Search PowerShell Gallery Module for #Requires Statement

The first part of this post began on PowerShell.org. Start there, unless you already have: http://powershell.org/wp/2015/11/30/search-powershell-gallery-module-for-requires-statement.

Please use the connect.microsoft.com link here, or at the bottom of this post, to up vote my feedback.

Desired State Configuration (DSC) and Just Enough Administration (JEA) are two topics that have recently piqued my interest. This, after becoming involved in Windows PowerShell constrained endpoints, and writing proxy functions, to better control how cmdlets are used. While working with DSC and xJEA (x, as in experimental), I ran up against an error message in the Event Viewer on my Server 2012 R2 target node running Windows Management Framework (WMF) 4.0 (the installation package that contains PowerShell 4.0). Before I get too deeply involved in JEA in WMF 5.0 on Server 2016 — what appears to be constrained endpoints of the past, with the benefits of JEA, but without the need for DSC, and some additional new features — I wanted to ensure I was able to deploy JEA endpoints with DSC, while running WMF 4.0.

Knowing this, the first error message below, makes perfect sense. I downloaded a version of xJEA that had a PowerShell 5.0 requirement, without even knowing it. Instead of moving up to WMF 5.0 to “fix” this, I opted to first get an older version of xJEA and determine if I was able to make it run with a version that can be used on WMF 4.0. This seemed like a logical progression in my learning and understanding of both DSC and JEA. Here’s the error:

“Error Message = The script ‘MSFT_xJeaToolkit.psm1’ cannot be run because it contained a “#requires” statement for Windows PowerShell 5.0. The version of Windows PowerShell that is required by the script does not match the currently running version of Windows PowerShell 4.0.”

As to be expected, the error message that was reported in the console host, was much less helpful. In fact, I was at a total loss until I went hunting in the Event Viewer, where I turned up the previous error message. Here’s the error I saw in the console:

“Invoke-CimMethod : Failed to extract the module from zip file C:\Windows\TEMP\\635842583367244849\xJea_0.2.16.6.zip
downloaded by Download Manager WebDownloadManager.”

In order to determine which version of xJEA didn’t require PowerShell 5.0, I needed to download all the xJEA versions and inspect them. You see, there isn’t a way to find out if there’s a PowerShell version requirement… more on that shortly. To begin, I ensured that the PowerShell Gallery was a trusted repository, and therefore, wouldn’t prompt me when I ran the Save-Module command in an upcoming example. To do this, I ran the commands below to (1) verify if the PowerShell Gallery was trusted (which it wasn’t, and isn’t by default), (2) trust it as an installation source, and (3) verify it was trusted.

PS> Get-PSRepository

Name                      PackageManagementPro InstallationPolicy   SourceLocation
                          vider
----                      -------------------- ------------------   --------------
PSGallery                 NuGet                Untrusted            https://www.powershellgallery.com/api/v2/


PS> Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
PS> Get-PSRepository

Name                      PackageManagementPro InstallationPolicy   SourceLocation
                          vider
----                      -------------------- ------------------   --------------
PSGallery                 NuGet                Trusted              https://www.powershellgallery.com/api/v2/

When that was completed, what I needed was to download all the xJEA modules from the PowerShell Gallery, at once, and place them into a folder for each version. Luckily, the Save-Module cmdlet will handle the file structure. This means, each module (a DSC resource, in this case) will be saved inside a version folder, nested in the same xJea folder. I gave the cmdlet a base path (C:\), and it did the rest. Take a look below at the example and image.

PS> Find-Module -Name xJea -AllVersions |
>> ForEach-Object {Save-Module -Path C:\ -Name xJea -RequiredVersion "$($_.Version.ToString())"}

Search PowerShell Gallery Module for requires01

Once I had all the files downloaded, I needed to run through all the contained .psm1 files for the requires statement for PowerShell version 5.0. The command below returned 51 files where it located the string “requires -version 5.”

PS> (Get-ChildItem -Path C:\xJea -Recurse -Filter '*.psm1' |
>> Get-Content) -match 'requires -version 5' |
>> Select-Object PSPath | Convert-Path
C:\xJea\0.2.10\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.10\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.10\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.11\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.11\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.11\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.12\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.12\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.12\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.13\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.13\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.13\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.14\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.14\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.14\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.15\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.15\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.15\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.16\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.16\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.16\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.16.1\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.16.1\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.16.1\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.16.2\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.16.2\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.16.2\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.16.3\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.16.3\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.16.3\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.16.4\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.16.4\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.16.4\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.16.5\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.16.5\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.16.5\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.16.6\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.16.6\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.16.6\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.5\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.5\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.6\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.6\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.7\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.7\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.8\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.8\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.8\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
C:\xJea\0.2.9\DSCResources\Library\JeaAccount.psm1
C:\xJea\0.2.9\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
C:\xJea\0.2.9\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1

In the next two examples, I haven’t included all the results to save some space. The example below, takes the previous one, and removes the portion of the path to the left of the version number. Compare the last line in the example above with the last line in the example below, and you’ll see how the .Split() method cleaned up the beginning of each result.

PS> (Get-ChildItem -Path C:\xJea -Recurse -Filter '*.psm1' |
>> Get-Content) -match 'requires -version 5' |
>> Select-Object PSPath | Convert-Path |
>> ForEach-Object {$_.Split('\',3)[-1]}
0.2.10\DSCResources\Library\JeaAccount.psm1
0.2.10\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
0.2.10\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
0.2.11\DSCResources\Library\JeaAccount.psm1
0.2.11\DSCResources\MSFT_xJeaEndpoint\MSFT_xJeaEndpoint.psm1
0.2.11\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1
...
0.2.9\DSCResources\MSFT_xJeaToolkit\MSFT_xJeaToolkit.psm1

In this example, we added a second .Split() method to clean up the path information to the right of the version number. Now, we only have the versions, although we do have duplicates.

PS> (Get-ChildItem -Path C:\xJea -Recurse -Filter '*.psm1' |
>> Get-Content) -match 'requires -version 5' |
>> Select-Object PSPath | Convert-Path |
>> Foreach-Object {$_.Split('\',3)[-1].Split('\')[0]}
0.2.10
0.2.10
0.2.10
0.2.11
0.2.11
0.2.11
..
0.2.9

This example below, shows the full results again; however, each version is only listed one time. This is because we’ve piped the previous results to Select-Object -Unique. This means, for example, we only see 0.2.11 once, even though it appeared three times in the last example.

PS> (Get-ChildItem -Path C:\xJea -Recurse -Filter '*.psm1' |
>> Get-Content) -match 'requires -version 5' |
>> Select-Object PSPath | Convert-Path |
>> ForEach-Object {$_.Split('\',3)[-1].Split('\')[0]} |
>> Select-Object -Unique
0.2.10
0.2.11
0.2.12
0.2.13
0.2.14
0.2.15
0.2.16
0.2.16.1
0.2.16.2
0.2.16.3
0.2.16.4
0.2.16.5
0.2.16.6
0.2.5
0.2.6
0.2.7
0.2.8
0.2.9

We now have a complete list of all the versions that require PowerShell 5.0. That means we can determine which versions will run with PowerShell 4.0.

In this final example, we set a variable, $VersionsRequireWMF5, to the results produced by our last command. These are the versions that require PowerShell 5.0. In the middle of the example below, we set a second variable, $AllVersions, to all the versions of xJEA that we downloaded. We did this by running a Get-ChildItem (think, dir or ls) against the C:\xJea directory and returned just the directory names. That gives us two variables that we can supply to the Compare-Object cmdlet. That cmdlet will tell us the differences between the values of the variables, indicating which versions I can use with WMF 4.0.

PS> $VersionsRequireWMF5 = (Get-ChildItem -Path C:\xJea -Recurse -Filter '*.psm1' | 
>> Get-Content) -match 'requires -version 5' | 
>> Select-Object PSPath | Convert-Path | 
>> ForEach-Object {$_.Split('\',3)[-1].Split('\')[0]} | 
>> Select-Object -Unique
PS>  
PS> $AllVersions = (Get-ChildItem -Path C:\xJea | Select-Object).Name
PS>  
PS> Compare-Object -ReferenceObject $AllVersions -DifferenceObject $VersionsRequireWMF5

InputObject SideIndicator
----------- -------------
0.2         <=
0.2.1       <=
0.2.2       <=
0.2.4       <=

Based on these results, version 0.2, 0.2.1, 0.2.2, and 0.2.4, do not require WMF 5.0 and can be tested with WMF 4.0, before moving to the newer version of WMF. You can expect that I’ll work with 0.2.4 tomorrow morning.

A final note: Microsoft may not yet think they need it, but they should indicate the version of PowerShell that is required by modules on the PowerShell Gallery. That needs to be a part of their approval process, and the results need to added to the web interface, and made a part of the results returned by the Find-Module cmdlet (see the full Find-Module results below). These results should include a Requires property. This isn’t going to be the last time someone comes up against this problem, especially as we move into versions post WMF 5.0.

PS> Find-Module -Name xJea | Select-Object *

Name : xJea
Version : 0.2.16.6
Description : Module with DSC Resources for Just Enough Admin (JEA). Jea makes it simple to create
custom RBAC solutions using PowerShell.
Author : Microsoft Corporation
CompanyName :
Copyright : (c) 2014 Microsoft Corporation. All rights reserved.
PublishedDate : 5/14/2015 7:51:23 PM
LicenseUri :
ProjectUri :
IconUri :
Tags : {PSModule, PSIncludes_DscResource}
Includes : {Function, DscResource, Cmdlet, Command}
PowerShellGetFormatVersion :
ReleaseNotes :
Dependencies : {}
RepositorySourceLocation : https://www.powershellgallery.com/api/v2/
Repository : PSGallery
PackageManagementProvider : NuGet

If you think this should be included, then up vote the feedback I’ve left on connect.microsoft.com.

Leave a Reply

Your email address will not be published.