Book Image

Mastering PowerShell Scripting - Fourth Edition

By : Chris Dent
5 (1)
Book Image

Mastering PowerShell Scripting - Fourth Edition

5 (1)
By: Chris Dent

Overview of this book

PowerShell scripts offer a convenient way to automate various tasks, but working with them can be daunting. Mastering PowerShell Scripting takes away the fear and helps you navigate through PowerShell's capabilities.This extensively revised edition includes new chapters on debugging and troubleshooting and creating GUIs (online chapter). Learn the new features of PowerShell 7.1 by working with parameters, objects, and .NET classes from within PowerShell 7.1. This comprehensive guide starts with the basics before moving on to advanced topics, including asynchronous processing, desired state configuration, using more complex scripts and filters, debugging issues, and error-handling techniques. Explore how to efficiently manage substantial amounts of data and interact with other services using PowerShell 7.1. This book will help you to make the most of PowerShell's automation features, using different methods to parse data, manipulate regular expressions, and work with Windows Management Instrumentation (WMI).
Table of Contents (26 chapters)
24
Other Books You May Enjoy
25
Index

Introducing modules

Modules were introduced with the release of PowerShell version 2.0. A module is a packaged set of commands that includes any required supporting content; modules often include help content.

Modules tend to target a specific system or focus on a small set of related operations. For example, the Microsoft.PowerShell.Archive module contains a small number of commands for interacting with ZIP files.

The modules available on a system can be discovered using the Get-Module command.

The Get-Module command

Get-Module is used to find the modules either in the current PowerShell session, or available on the current system.

PowerShell itself comes with several built-in modules, including PowerShellGet, ThreadJob, PSReadLine, and the commands in the Microsoft.PowerShell.* modules.

The Windows platform, especially the most recent versions, comes with a wide variety of modules installed. These, as well as any other available modules, can be viewed using the Get-Module -ListAvailable command.

By default, Get-Module returns information about each module that has been imported (either automatically or by using Import-Module). For example, if the command is run from PowerShell 7, it shows that the ISE module has been loaded:

PS> Get-Module
ModuleType    Version    Name                            ExportedCommands 
----------    -------    ----                            ---------------- 
Script        1.0.0.0    ISE                             {Get-IseSnippe...}
Manifest      3.1.0.0    Microsoft.PowerShell.Management {Add-Computer...}

The ListAvailable parameter shows the list of modules that are available on the system instead of just those that have been imported:

Get-Module -ListAvailable

Modules are discovered using the paths in the PSModulePath environment variable, which contains a delimited list of paths for PowerShell to search.

Get-Module will show all instances of a module regardless of the path and version when using the All parameter:

Get-Module <ModuleName> -All -ListAvailable 

Modules that are available on a system can be imported either by running Import-Module or by running a command from the module.

The Import-Module command

PowerShell 3 and later attempts to automatically load modules if a command from that module is used and the module is under one of the paths in the $env:PSModulePath environment variable. Explicit use of the Import-Module command is less important than it was before Windows PowerShell 3.

For example, if PowerShell is started and the CimCmdlets module is not imported, running the Get-CimInstance command will cause the module to be automatically imported. This is shown in the following example:

PS> Get-Module CimCmdlets
PS> Get-CimInstance Win32_OperatingSystem | Out-Null
PS> Get-Module CimCmdlets
ModuleType Version    PreRelease Name         ExportedCommands
---------- -------    ---------- ----         ----------------
Binary     7.0.0.0               CimCmdlets   {Get-CimAssociatedInstance,...

The autoloader may be disabled using the $PSModuleAutoLoadingPreference variable as shown here:

$PSModuleAutoLoadingPreference = 'None'

You can explicitly import modules in PowerShell using the Import-Module command. Modules may be imported using a name or with a full path, as shown in the following example:

Import-Module -Name ThreadJob
Import-Module -Name $PSHome\Modules\ThreadJob\ThreadJob.psd1

Importing a module using a path is only required if the module is not in a discoverable path.

Once a module has been imported, the commands within the module may be listed using Get-Command as follows:

Get-Command -Module ThreadJob 

Modules, Get-Command, and auto-loading

As the commands exported by a module are only identified by PowerShell importing the module, the previous command will also trigger an automatic import.

Modules installed in Windows PowerShell 5 and later are placed in a folder named after the module version, for example, Modules\ModuleName\1.0.0\<ModuleContent>. This allows multiple versions of the same module to coexist, as shown in the following example:

Figure 2.1: Side-by-side versioning

Version 1.8.1 of PSScriptAnalyzer will be imported by default, as it is the highest version number. It is possible to import a specific version of a module using the MinimumVersion and MaximumVersion parameters:

Import-Module PSScriptAnalyzer -MaxmimumVersion 1.7.0

Modules that have been imported can be removed from a PowerShell session using the Remove-Module command.

The Remove-Module command

The Remove-Module command removes a previously imported module from the current session.

For binary modules or manifest modules that incorporate a Dynamic Link Library (DLL), commands are removed from PowerShell but DLLs are not unloaded. DLL files used in a PowerShell session cannot be unloaded without restarting the PowerShell process.

Remove-Module does not remove or delete the files that make up a module from a computer.

Each of the preceding commands, by default, interacts with modules saved in the PSModulePath environment variable.

PSModulePath in PowerShell

PSModulePath is a delimited list of paths that can be used to store modules. You can import modules in these paths by name and they will be automatically loaded when a command from the module is used.

PowerShell allows the value of $env:PSModulePath to be set using user- and machine-scoped environment variables. By default, the machine-scoped variable should include the following paths, which are used by Windows PowerShell and by PowerShell 7 for compatibility:

C:\Program Files\WindowsPowerShell\Modules
C:\Windows\System32\WindowsPowerShell\v1.0\Modules

If the environment variables do not exist, PowerShell 7 uses the default values:

PS> $Env:PSModulePath -split ';'
C:\Users\whoami\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7\Modules

The default values in the preceding list are included regardless of the value of the environment variable.

When using module paths, it is important to note that PowerShell does not search all paths for the latest version of a module. PowerShell searches the list of paths in the order they appear in the PSModulePath environment variable. If a module is listed in more than one path, the most recent version from the first discovered path is used.

For example, if the current user path contains a module with version 1.0.0, and the program files path contains the same module but with version 2.0.0, PowerShell will prefer to load version 1.0.0 because the current user path is searched first. The Version or MinimumVersion parameter must be used with Import-Module to avoid this.

If both Windows PowerShell and PowerShell 7 are in use in an environment, care must be taken when updating the PSModulePath environment variable. The behavior described previously differs from Windows PowerShell. In Windows PowerShell:

  • If the user environment variable is set, it completely replaces the user value, which defaults to C:\Users\whoami\Documents\WindowsPowerShell\Modules
  • If the machine environment variable is set, it replaces the system32 path: C:\windows\system32\windowspowershell\v1.0\Modules
  • In all cases, the C:\Program Files\WindowsPowerShell\Modules path remains

The C:\windows\system32\windowspowershell\v1.0\Modules path should be included in the machine environment variable to allow PowerShell 7 to load modules, either directly or using a Windows PowerShell compatibility session.

The value of $env:PSModulePath may be safely modified within on all PowerShell versions and all platforms, for example, by using a profile script. Changes made to $env:PSModulePath are scoped to the process and only affect the current PowerShell session and any child processes; the changes do not persist.

PowerShell 7 can use modules intended for Windows PowerShell either directly or by using a Windows PowerShell compatibility session.

Using Windows PowerShell modules in PowerShell 7

Many modules available to Windows PowerShell are compatible with PowerShell 7 without requiring any changes.

If a module is not compatible with PowerShell 7, an attempt can be made to load the module in a Windows compatibility session.

In PowerShell 6, the following functionality discussed is part of the WindowsCompatibility module available in the PowerShell Gallery. This module is not required in PowerShell 7. In PowerShell 7, the ability to load a module in a compatibility session is built into the Import-Module command.

The TLS module, for example, will not load PowerShell 7 by default because it does not state that it supports the Core edition of PowerShell, as shown by Get-Module:

PS> Get-Module TLS -ListAvailable -SkipEditionCheck
    Directory: C:\Windows\System32\WindowsPowerShell\v1.0\Modules
ModuleType Version    PreRelease Name       PSEdition ExportedCommands
---------- -------    ---------- ----       --------- ----------------
Manifest   2.0.0.0               TLS        Desk      {New-TlsSessionTic...

The module can be loaded in two ways:

The edition check can be skipped (the module may work, it may just lack testing, and therefore careful testing may be required before using the module in a production scenario):

Import-Module TLS -SkipEditionCheck

Alternatively, if the previous command fails, the module may load in a compatibility session:

PS> Import-Module TLS -UseWindowsPowerShell
WARNING: Module TLS is loaded in Windows PowerShell using WinPSCompatSession remoting session; please note that all input and output of commands from this module will be deserialized objects. If you want to load this module into PowerShell Core please use 'Import-Module -SkipEditionCheck' syntax.

The compatibility session can be seen using the Get-PSSession command after the module has been imported:

Get-PSSession -Name WinPSCompatSession

When importing the preceding TLS module, a warning message is displayed that notes the input and output is deserialized. The impact of this depends on the complexity of the objects returned by the command; typically, it will mean that methods of specialized types will not work from PowerShell 7.

The effect of this can be demonstrated by invoking Get-WmiObject in the compatibility session. Get-WmiObject is not available in PowerShell 7 and cannot be directly used:

$session = Get-PSSession -Name WinPSCompatSession
$process = Invoke-Command -Session $session -ScriptBlock {
    Get-WmiObject Win32_Process -Filter "ProcessID=$PID"
}

If the Get-WmiObject command is run in Windows PowerShell without using Invoke-Command, it will have several methods available. One of these methods is GetRelated, which is typically used as follows when used in Windows PowerShell:

$process = Get-WmiObject Win32_Process -Filter "ProcessID=$PID"
$process.GetRelated('Win32_Session')

Because PowerShell 7 has a copy of the properties only, the method does not exist and an error will be displayed:

PS> $session = Get-PSSession -Name WinPSCompatSession
PS> $process = Invoke-Command -Session $session -ScriptBlock {
>>     Get-WmiObject Win32_Process -Filter "ProcessID=$PID"
>> }
PS> $process.GetRelated('Win32_Session')
InvalidOperation: Method invocation failed because [Deserialized.System.Management.ManagementObject#root\cimv2\Win32_Process] does not contain a method named 'GetRelated'.

The compatibility feature is incredibly useful but does not replace native compatibility with modern versions of PowerShell.

PowerShell on the Windows platform has a wide variety of modules available, or available through installable applications and features to interact with other systems. New modules can also be installed from resources such as the PowerShell Gallery.