Hey, Scripting Guy! I really dig that I can have a module that contains functions and things. This seems like it would be groovy to use to customize my Windows PowerShell environment. The thing is that I do not want to be hopping around from desk to desk from user to user to copy module files into esoteric and obscure locations. There needs to be an Install-Module cmdlet that can be used to perform this service for me. Can you help me?
— EH
Hello EH,
Microsoft Scripting Guy Ed Wilson here. It is snack time in Charlotte, North Carolina. This morning I am sipping a cup of English Breakfast tea, with a little lemon grass, and a cinnamon stick while munching on raw walnuts and artisan cheese. I have been hanging out on Twitter, answering questions, making new friends, gathering ideas for research, and deleting spam in the scripter@microsoft.com e-mail inbox. Like most IT pros, I can multitask. However, I will admit the snacking is getting a few extra MIPS right now.
EH, you do not want to be hopping around from desk to desk? Actually hopping around is fun. When I was in Australia doing a train-the-trainer session for the Windows PowerShell class I wrote, I used to sit outside in the twilight and mellow out. One day while I was relaxing, I saw a giant rabbit hopping through the yard. I quickly grabbed my camera and snapped the following photo.
Okay, so he is not really a rabbit, but my friend Chris assured me that kangaroos in Australia are like rabbits from the southern United States. Awesome! It is an inspiring site to be sitting on your back porch and watch these dudes go hopping through the back yard.
Note: Portions of today’s Hey, Scripting Guy! Blog post are excerpted from the Microsoft Press book, Windows PowerShell 2.0 Best Practices by Ed Wilson. The book is now available.
EH, one of the features of modules is that they can be installed without elevated rights. Because each user has a modules folder in their %userprofile% directory that they automatically have rights to use, the installation of a module does not require Administrator rights. An additional feature of modules is that they do not require a specialized installer. The files associated with a module can be copied by using the Xcopy utility, or, as I prefer to do, they can be copied by using the Copy-Item Windows PowerShell cmdlet.
The users’ modules folder does not exist by default. To avoid confusion, you may decide to create the modules directory in the users’ profile before deploying modules, or you may simply create a module installer script that checks for the existence of the users’ modules folder, creates the folder if it does not exist, and then copies the modules. One problem in directly accessing the users’ module directory is that it is in different locations, depending on the version of the operating system. In Windows XP and Windows Server 2003, the users’ module folder is in the My Documents folder. In Windows Vista and later, the users’ module folder is in the Documents folder. In the Copy-Modules.ps1 script, we solve the problem of different module folder locations by using a function, Get-OperatingSystemVersion, which retrieves the major version number of the operating system. The Get-OperatingSystemversion function is seen here:
Function Get-OperatingSystemVersion
{
(Get-WmiObject -Class Win32_OperatingSystem).Version
} #end Get-OperatingSystemVersion
The major version number of the operating system is used in the Test-ModulePath function. If the major version number of the operating system is greater than 6, it means the operating system is at least Windows Vista and will therefore use the Documents folder in the path to the modules. If the major version number of the operating system is not greater than 6, the script will use the My Documents folder for the module location. After the version of the operating system is determined and the path to the module location is ascertained, it is time to determine if the module folders exist. To do this, use the Test-Path cmdlet, which returns a Boolean value. Because you are interested only in the absence of the folder, you can use the –not operator as shown here in the completed Test-ModulePath function:
Function Test-ModulePath
{
$VistaPath = “$env:userProfiledocumentsWindowsPowerShellModules”
$XPPath = “$env:Userprofilemy documentsWindowsPowerShellModules”
if ([int](Get-OperatingSystemVersion).substring(0,1) -ge 6)
{
if(-not(Test-Path -path $VistaPath))
{
New-Item -Path $VistaPath -itemtype directory | Out-Null
} #end if
} #end if
Else
{
if(-not(Test-Path -path $XPPath))
{
New-Item -path $XPPath -itemtype directory | Out-Null
} #end if
} #end else
} #end Test-ModulePath
After the users’ modules folder has been created, it is time to create a child folder to hold the new module. A module is always installed in a folder that has the same name as the module itself. The name of the module is the file name that contains the module minus the .psm1 extension. This location is shown in the following image.
I wrote the Copy-Modules.ps1 script to ease some of the concerns about installing Windows PowerShell modules on a computer. The complete Copy-Modules.ps1 script is seen here.
Copy-Modules.ps1
<#
.Synopsis
Looks for the module path folder, copies module files to folder
.Description
This script looks for the module path folderon either xp family
or vista family computer. if the module folder is missing, it
will create it. It then copies the module files into the folder.
When copying new module files, it creates a parent folder for each
new module as required by the module architecture.
.Example
Copy-Modules.ps1 -Path c:fso
This command checks for existence of module folder and copies
all module files from the c:fso directory into their own folder
in the users modules directory.
.Inputs
[String]
.OutPuts
[System.Io.DirectoryInfo]
.Notes
NAME: Copy-Modules.ps1
AUTHOR: ed wilson
LASTEDIT: 4/12/2009
KEYWORDS: modules, test-path, new-item, environment
PowerShell Best Practices
.Link
Test-Path
New-Item
Get-WmiObject
Http://www.ScriptingGuys.com
#Requires -Version 2.0
#>
[CmdletBinding()]
Param(
[Parameter(Position=0,
Mandatory=$True,
ValueFromPipeline=$True)]
[string]$path
)
Function Get-OperatingSystemVersion
{
(Get-WmiObject -Class Win32_OperatingSystem).Version
} #end Get-OperatingSystemVersion
Function Test-ModulePath
{
$VistaPath = “$env:userProfiledocumentsWindowsPowerShellModules”
$XPPath = “$env:Userprofilemy documentsWindowsPowerShellModules”
if ([int](Get-OperatingSystemVersion).substring(0,1) -ge 6)
{
if(-not(Test-Path -path $VistaPath))
{
New-Item -Path $VistaPath -itemtype directory | Out-Null
} #end if
} #end if
Else
{
if(-not(Test-Path -path $XPPath))
{
New-Item -path $XPPath -itemtype directory | Out-Null
} #end if
} #end else
} #end Test-ModulePath
Function Copy-Module([string]$name)
{
$UserPath = $env:PSModulePath.split(“;”
)[0]
$ModulePath = Join-Path -path $userPath `
-childpath (Get-Item -path $name).basename
If(-not(Test-Path -path $modulePath))
{
New-Item -path $modulePath -itemtype directory | Out-Null
Copy-item -path $name -destination $ModulePath | Out-Null
}
Else
{
Copy-item -path $name -destination $ModulePath | Out-Null
}
}
# *** Entry Point to Script ***
Test-ModulePath
Get-ChildItem -Path $path -Include *.psm1,*.psd1 -Recurse |
Foreach-Object { Copy-Module -name $_.fullName }
In the Copy-Module function from the Copy-Modules.ps1 script, the first action that is taken is to retrieve the value of PSModulePath environmental variable. Because there are two locations in which modules can be stored, the PSModulePath environmental variable contains the path to both locations. The PSModulePath is not stored as an array; it is stored as a string. The value contained in PSModulePath is seen here:
PS C:> $env:psmodulePath
C:Usersadministrator.NWTRADERS.000DocumentsWindowsPowerShellModules;C:Win
dowsSystem32WindowsPowerShellV1.0Modules
If you attempt to index into the data stored in the PSModulePath environmental variable, you will retrieve one letter at a time. This is seen here:
PS C:> $env:psmodulePath[0]
C
PS C:> $env:psmodulePath[1]
:
PS C:> $env:psmodulePath[2]
PS C:> $env:psmodulePath[3]
U
Attempting to retrieve the path to the users’ module location one letter at a time would be problematic at best and error prone at worst. Because the data is a string, you can use string methods to manipulate the two paths. To break a string into an array that can be easily utilized, you use the split method from the System.String class. You only need to pass a single value to the split method—the character to split upon. Because the value stored in the PSModulePath variable is a string, you can access the split method directly. This is shown here:
PS C:> $env:psmodulePath.split(“;”)
C:Usersadministrator.NWTRADERS.000DocumentsWindowsPowerShellModules
C:WindowsSystem32WindowsPowerShellV1.0Modules
You can see from the output above that the first string displayed is the path to the users’ modules folder, and the second path is the path to the system modules folder. Because the split method turns a string into an array, it means you can now index into the array and retrieve the path to the users’ modules folder by using the [0] syntax. You do not need to use an intermediate variable to store the returned array of paths if you do not wish to do so. You can index into the returned array directly. If you were to use the intermediate variable to hold the returned array and then index into the array, the code would resemble the following:
PS C:> $aryPaths = $env:psmodulePath.split(“;”)
PS C:> $aryPaths[0]
C:Usersadministrator.NWTRADERS.000DocumentsWindowsPowerShellModules
Because the array is immediately available after the split method has been called, you directly retrieve the users’ modules path. This is seen here:
PS C:> $env:psmodulePath.split(“;”)[0]
C:Usersadministrator.NWTRADERS.000DocumentsWindowsPowerShellModules
EH, that is all there is to installing Windows PowerShell modules. Windows PowerShell Module Week will continue tomorrow.
If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys
0 comments