While working on some PowerShell scripts which are using AzureRM PowerShell modules for an Azure DevOps Extension I had some trouble using the correct AzureRM PowerShell module version on the Hosted Agent.
By default version 2.1.0 of the PowerShell AzureRM version is being used on the Visual Studio 2017 on Windows Server 2016 (Hosted VS2017) Agent. But more AzureRM PowerShell versions are available, but not by default accessible via the PSModulePath environment variable.
It turned out that the PSModulePath determines which versions of a PowerShell Module are being used. The location of the PowerShell module must be in the PSModulePath environment variable, and the order of the paths in the PSModulePath determines which version is being used.
The PSModulePath environment variable stores the paths to the locations of the modules that are installed on disk. PowerShell uses this variable to locate modules when the user does not specify the full path to a module. The paths in this variable are searched in the order in which they appear.
If I look at my current PSModulePath environment variable on my Windows PowerShell host I see the following:
When I retrieve the PSModulePath environment variable on PowerShell Core running on a Docker container with Ubuntu 16.04.5 I have the following paths in my PSModulePath:
There are multiple ways to modify the PSModulePath variable.
$env:PSModulePath = $env:PSModulePath + ";c:\ModulePath"
For PowerShell Core on Linux run the following:
$env:PSModulePath = $env:PSModulePath + ":/ModulePath"
Screenshot of updated PSModulePath environment variable for PowerShell Core running on Ubuntu Docker container image.
$env:PSModulePath = $env:PSModulePath + ";c:\ModulePath"
For more information about profiles, see about_Profiles in the Microsoft TechNet library.
$CurrentValue = [Environment]::GetEnvironmentVariable("PSModulePath", "Machine")
[Environment]::SetEnvironmentVariable("PSModulePath", $CurrentValue + ";C:\Program Files\Fabrikam\Modules", "Machine")
On my Windows 10 development system I’ve the following PSModulePath configured:
Let’s first see which version of the AzureRM.Resources Module will be used when I try to use the Get-AzureRMResourceGroup Cmdlet.
Get-Command -Name Get-AzureRMResourceGroup
This shows that Version 6.5.0 of the AzureRM.Resources PowerShell module is being used.
This module is being located via the PSModulePath environment variable in folder C:\Users\[username]\Documents\WindowsPowerShell\Modules\AzureRM.Resources.
This is also the first path where the AzureRM.Resources PowerShell Module is located.
What would happen if we changed the PSModulePath to have a different path as first location to contain the AzureRM.Resources Module?
Remark:
Use a ‘clean’ PowerShell host to avoid already having loaded a PowerShell module in the session.
# Adding new path to the beginning of the PSModulePath!
$env:PSModulePath = "C:\Modules\azurerm_5.1.1;" + $env:PSModulePath
Check now which PowerShell AzureRM.Resources module version will be loaded when retrieving the Get-AzureRMResourceGroup cmdlet.
Get-Command -Name Get-AzureRMResourceGroup
Now the first PowerShell AzureRM.Resources module being found in the PSModulePath variable is version 5.1.1 which is located in the path C:\Modules\azurerm_5.1.1\5.1.1\AzureRM.Resources.
We have now seen that the PSModulePath looks for the highest/latest PowerShell Module version in the order of the configured paths in the PSModulePath environment variable.
With the following method you can reset the PSModulePath to the default setting again:
$CurrentValue = [Environment]::GetEnvironmentVariable("PSModulePath", "Machine")
$UserPSModuleLocation = "$HOME\Documents\WindowsPowerShell\Modules"
$Env:PSModulePath = $UserModuleLocation + ";" + $CurrentValue
Conclusion:
If you want to use the PSModulePath for autoloading PowerShell modules make sure your desired PowerShell Module version path is in the first order of the PSModulePath variable.
Only modules that are stored in the location specified by the PSModulePath environment variable are automatically imported. Modules in other locations must be imported by running the Import-Module cmdlet.
The $PSModuleAutoloadingPreference enables and disables automatic importing of modules in the session. “All” is the default. Regardless of the value of this variable, you can use the Import-Module cmdlet to import a module. Valid values are:
You can disable the automatic loading of module by using the following command:
$PSModuleAutoloadingPreference = 'none'
Note
I do not recommend you make this change except in very specific situations and for very specific reasons. The number of Windows PowerShell cmdlets and functions in Windows Server 2018 and Windows 10 would make knowing which module a particular command resided in extremely difficult; with this change in place, you have to specifically load the module prior to using any commands.
Let’s see what happens if we disable auto module loading when retrieving the Get-AzureRMResourceGroup cmdlet.
Remark:
Use a ‘clean’ PowerShell host to avoid already having loaded a PowerShell module in the session.
The Command is not found. We need to explictly use Import-Module to be able to find and use the Get-AzureRmResourceGroup cmdlet.
Import-Module C:\Modules\azurerm_5.1.1\5.1.1\AzureRM.Resources -RequiredVersion 5.1.1
After explictly importing a specific PowerShell Module and version we are able to use the cmdlet we want.
Conclusions
References: