PowerShell

Introduction

This page contains useful information about Windows PowerShell automation. We will cover topics, such as:

  • PowerShell Language
  • PowerShell Core commands
  • PowerShell scripts
  • PowerShell Modules
    • Installing / removing
    • Discovering and inspecting modules
  • PowerShell Remoting
    • Enabling / disabling PowerShell Remoting
    • Securing PowerShell Remoting with TLS certificates
    • Configuring authentication for Remoting
    • Just Enough Administration (JEA)
  • PowerShell Workflow
  • PowerShell Desired State Configuration (DSC)
  • PowerShell Classes
  • PowerShell Event Registrations
  • Using PowerShell with .NET Objects

Language

The PowerShell language itself is only one component of the entire PowerShell framework. The PowerShell language is very rich as a programming language. Although it PowerShell isn’t generally geared towards service-oriented application architectures, it has most of the essential constructs of a procedural programming language.

Conditional Statements

If Statements

The if statement is one of the most fundamental components of a procedural programming language. PowerShell provides a variety of comparison operators that can be used in conjunction with the if statement. For comprehensive documentation on these operators, use Get-Help -Name about_Comparison_Operators -Category HelpFile.

Switch Statements

The switch statement is a powerful conditional statement in PowerShell that enables you to define conditions using regular expressions, static values, and wildcards. Let’s take a look at how to use the switch statement with static values, for starters.

$FirstName = 'Trevor'

switch ($FirstName) {
  'Trevor' {
    $LastName = 'Sullivan'
  }
  'Trevor' {
    $LastName = 'Smith'
  }
}

If you look at the code above, what do you think the outcome would be? I’m comparing the $FirstName variable to two static values, which are both the same. Do you think PowerShell will simply evaluate the first condition, and then move on? If so, you’d actually be wrong. The result of this switch statement is that the $LastName variable would be set to Smith. This is a very important learning point for switch statements: your input to the switch statement can be matched to more than one condition, and multiple conditions can be processed within the switch statement.

Unless you are explicitly looking for the above behavior, you’ll want to make sure that you put break statements after each of your conditional code blocks. Check out the updated code below.

$FirstName = 'Trevor'

switch ($FirstName) {
  'Trevor' {
    $LastName = 'Sullivan'
    break
  }
  'Trevor' {
    $LastName = 'Smith'
    break
  }
}

Now that we’ve put the break statements in place, the first matching condition will be executed, and then the switch statement will exit. This gives us the desired results of $LastName = ‘Sullivan’.

You can also use regular expressions with the switch statement, to perform more advanced conditional evaluations. Consider the following example, which uses a regular expression for comparison.

$FirstName = 'Trevor'

switch -regex ($FirstName) {
  '^S' {
    Write-Host -Object 'Your name starts with S!'
    break
  }
  '^T.*r$' {
    Write-Host -Object 'Your name starts with T and ends with r!'
    break
  }
}

The code above performs an evaluation against each of the regular expressions. The second regular expression matches our input to the switch statement, so the output we receive is: “Your name starts with T and ends with r!

While Statements

The while statement in PowerShell enables you to invoke PowerShell code in a loop, as long as the condition that you specify is true. While you are executing code inside of a while loop, you can define a special condition that can cause your code to “break” out of the while loop by using the break statement. Let’s take a look at an example of a while statement.

while (1 -eq 1) {
  Write-Host -Object 'I love writing code'
}

The above example will simply print “I love writing code” until the cows come home, unless you use CTRL + C to break out of the loop. Let’s take a look at another example, where we can manually break out of the loop, based on a special condition.

$MyNumber = 0
while (1 -eq 1) {
  Write-Host -Object $MyNumber
  if ($MyNumber -eq 42) { break }
  $MyNumber++
}

This time, we’re specifying a condition, such that if the $MyNumber variable becomes equal to 42, then we want to exit the while loop, by invoking the built-in PowerShell break statement. Try it out yourself by copy-pasting this code into your PowerShell host.

Background Jobs

Long-running PowerShell code can be executed inside of a Background Job. Typically, the Start-Job PowerShell command is used to create and execute a new Background Job. The Start-Job command offers a -ScriptBlock parameter, which enables you to specify the commands to execute using a PowerShell ScriptBlock. A PowerShell ScriptBlock is an object that is somewhat similar to an anonymous method in other languages. ScriptBlocks are easily defined using curly braces, for example: { Get-Process; }.

Remoting

PowerShell Remoting is a feature that enables remote management of the Windows operating system. Remoting is based on the Windows Remote Management (WinRM) service that works across both client and server Microsoft Windows operating systems, starting with Windows XP and Server 2003 (requires installation of PowerShell 2.0).

To enable PowerShell Remoting, you typically run the Enable-PSRemoting command, which is available in PowerShell core. If you need to disable Remoting, you can use the Disable-PSRemoting command.

Securing PowerShell Remoting

Given the power that PowerShell Remoting offers to engineers and software developers, the need to secure it is obvious. In fact, I presented a webcast on this topic back in 2013 for the International Legal Technology Association (ILTA). Security for WinRM is typically handled by securing the transport layer by implementing TLS certificate on the WinRM listeners, and disabling the HTTP

Double-Hop Authentication with CredSSP

 

Workflow

PowerShell Workflow is a feature that enables the persistence of execution state, connectivity to remote systems, and execution in PowerShell Background Jobs. This feature is built on top of the Microsoft .NET Windows Workflow Foundation (WWF).

Modules

PowerShell modules are essentially containers around a related set of PowerShell commands, aliases, variables, and DSC resources. Modules can be developed as binary modules (authored in C# or VB.NET, generally) or script modules (PowerShell code).

Finding Modules

You can use PowerShell’s built-in commands to discover the PowerShell modules that are installed on a system. PowerShell is able to discover installed modules by searching the paths in the environment variable named PSModulePath. By default, modules are stored in %UserProfile%\Documents\WindowsPowerShell\Modules and as of PowerShell version 4.0, also under %ProgramFiles%\WindowsPowerShell\Modules. In PowerShell 5.0, Microsoft added support for installing multiple versions of a module side-by-side. Use the Get-Module command, with the -ListAvailable parameter, to search for all modules in the PSModulePath.

Module Manifest

A PowerShell module manifest is a file with a .psd1 file extension, that describes a PowerShell module. The contents of a manifest is a PowerShell HashTable, with predetermined keys, such as Name, Description, Author, and other metadata elements. When you import a PowerShell module, you can call the Import-Module command and specify the path to a module manifest. The module manifest acts as a “gatekeeper” for cmdlets, functions, aliases, and DSC resources that are “exported” from a PowerShell module. For example, if you declare a function in your module’s .psm1 file, but don’t add the function in the FunctionsToExport key in your module manifest, then the command will not be visible from your PowerShell module.

Module File

In a PowerShell script module, the .psm1 file is the module file itself. This file is responsible for importing external PowerShell script files that define aliases, functions, variables, workflows, and other related elements. While many new module authors tend to stick many functions inside of their .psm1 file, a better approach is to separate your functions into individual .ps1 script files, and then dot-source those script files from your .psm1 module file. I generally recommend creating a ./Functions subdirectory in the root of your PowerShell module folder, and adding the individual function files into that directory. Then, inside your module file, use the following code to import all of your functions at once.

$FunctionList = Get-ChildItem -Path $PSScriptRoot/Functions
foreach ($Function in $FunctionList) {
  . $Function.FullName
}

PowerShell Modules

TabExpansion++

Jason Shirk, a Microsoft employee, has created a PowerShell module called TabExpansion++. This module enables PowerShell authors to extend PowerShell’s Intellisense capabilities, leveraging a special function called TabExpansion2. The TabExpansion2 function was originally introduced in PowerShell 3.0.

Debugging and Testing Argument Completers

Typically, argument completers are intended to be used for interactive script authoring. However, sometimes, you might need to perform debugging inside a custom argument completer function, or simply perform some validation to ensure the desired result.

Testing custom argument completers can be achieved by manually invoking the built-in TabExpansion2 function. The value passed into the -InputScript parameter is the script that will have auto-completion performed against it. For example, if you wanted to return a list of all commands that matched the word “test” you would simply pass in the string value “test” into the -InputScript parameter.

The mandatory -CursorColumn parameter enables you to specify the cursor’s position, within the command specified in the -InputScript parameter, where auto-completion should occur. For example, if you have a command similar to the following: “Disable-NetAdapter -Name  -PassThru;” and you wanted the auto-completion to occur after the -Name parameter, then you would specify a -CursorColumn value of 25.

The screenshot below shows the results of the invocation to TabExpansion2, and the contents of the CompletionMatches property.

Testing Argument Completers

If you need to debug your custom argument completer function, you can set a breakpoint inside the function, and then invoke TabExpansion2. Once TabExpansion2 invokes your argument completer function, the breakpoint will be hit, and you can debug your code from the debugging context.

NOTE: Argument completer functions do not emit exception messages. If your argument completer throws a non-terminating or terminating error, then the function will return immediately. You should use the Write-Host command, try..catch statements, or logging to the filesystem, to help debug problems inside your argument completer.

PSReadline

The PSReadline PowerShell module was developed by Jason Shirk, a Microsoft employee, and is included out-of-box with Windows 10. This module enables the PowerShell Console Host to have much richer text editing capabilities, searchable history, syntax highlighting, and more.

PowerShell Gallery

The PowerShell Gallery is a web service that enables PowerShell module authors to publish their modules to a centralized repository, and enables module consumers to easily search for and install modules. The PowerShellGet module enables simplified end-user access to this repository.

A user can search for modules using the Find-Module command, and installation is quite easy using the Install-Module command. The Install-Module command offers a -Scope parameter that enables the user to specify whether the module should be installed under the current user’s scope, or for all user’s of the operating system.

Finding Installed Items

You can search for PowerShell modules, that were installed using the PowerShellGet module, by using the Get-InstalledModule command. This command does not enumerate PowerShell modules that were installed using another method, such as traditional “copy-installs.” You can also search for “installed” PowerShell scripts using a similar command, Get-InstalledScript. This command will enumerate one-off PowerShell scripts that were installed using the PowerShellGet module.

Repositories

The PowerShellGet module enables users to register custom module repositories. To achieve this, the user must first set up a NuGet repository, and then call the Register-PSRepository command. If you don’t want to set up a custom NuGet repository internally, there’s a service called MyGet, which enables you to easily set up a NuGet repository as a service. There is also a paid solution called ProGet that you can deploy on-premises!

Desired State Configuration

PowerShell Desired State Configuration (DSC) enables you to perform systems configuration operations in a declarative and idempotent fashion. Rather than writing procedurally-focused PowerShell scripts, you have the option of authoring the desired “end state” (sometimes known as the “goal state”) of a system

The PowerShell DSC feature was initially released in PowerShell version 4.0, which came out in 2013 with Windows 8.1. In its initial iteration, PowerShell DSC was poorly documented and challenging to use. On July 29th, 2015, Microsoft released PowerShell 5.0 along with the Windows 10 operating system. DSC was significantly improved in PowerShell 5.0, and made it much easier to use. Additionally, the Microsoft Azure Automation DSC service was released, making the process of distributing DSC configurations much simpler.

It wasn’t until later in 2015 that PowerShell 5.0 was officially released for down-level operating systems, including Windows 7, 8.1, and Server 2012 R2. Windows Server 2016 includes PowerShell 5.0, similar to Windows 10.

Configuration Document

A PowerShell DSC configuration document is an entity that’s very similar to a PowerShell function. DSC configuration documents are declared using the configuration keyword in PowerShell 4.0 and later. A configuration document can have an input parameter block, just like a PowerShell function or workflow. By properly that enables a consistent configuration structure to be reused across multiple environments.

Local Configuration Manager (LCM)

The Local Configuration Manager (LCM) is the agent software that is responsible for interpreting and applying a DSC configuration document. The LCM is implemented as a WMI provider, and there are several built-in PowerShell commands that enable communication with the LCM.

The LCM supports a variety of settings, including:

  • ActionAfterReboot – The action the LCM will take, after the system is rebooted during the processing of a DSC configuration document. This setting can be configured to values StopConfiguration or ContinueConfiguration.
  • CertificateID – The thumbprint of the certificate in the LocalMachine certificate store, that will be used to decrypt credentials that have been encrypted in the DSC configuration document.
  • RebootIfNeeded – A boolean value that determines if a reboot is pending for the managed node. This setting can be configure to $true or $false.
  • DebugMode – This setting configures the LCM to be in a debugging mode for DSC resource authors. The valid values for this are All, ForceModuleImport, or None.
  • ConfigurationMode – This setting configures the behavior of the LCM, when it applies its DSC configuration document. The valid values for this setting are: ApplyAndAutocorrect, ApplyAndMonitor, and ApplyOnly.
  • RefreshFrequencyMins – The number of minutes that will exist as a gap in between DSC configuration document updates. This setting only applies when the LCM is in the Pull refresh mode.
  • ConfigurationModeFrequencyMins – The number of minutes that will exist as a gap in between consistency checks. The LCM will attempt to perform a consistency check, and reconfigure the system, after this period, dependent on the value configured for ConfigurationMode.

In order to retrieve the current configuration of the DSC LCM, the Get-DscLocalConfigurationManager command is used.

DSC Resource Authoring

PowerShell DSC was designed from the ground up to be extensible. Nearly any virtual managed entity can be managed through the abstraction layer that is the DSC Resource. A DSC Resource can be authored using a couple of different models.

DSC Modules

PowerShell modules are the packaging mechanism that enable distribution of DSC resources. A PowerShell module can define one or more DSC resources, and “export” them using the module manifest file.

DSC PowerShell Classes

PowerShell DSC Resources can be authored using PowerShell Classes in version 5.0 and later. In order to designate a PowerShell Class as being a DSC Resource, you use the [DscResource()] attribute just before declaring the class, using the class PowerShell keyword.

[DscResource()]
class File {
}

After you’ve defined the class, you need to define one or more DSC properties on the class. At least one of the DSC properties must be marked as a key property. A DSC key property means that it uniquely identifies the DSC resource in a DSC configuration document. For example, if you were building a DSC resource that represents a file on the Windows filesystem, then you might uniquely identify the file, based on its FullPath.

NOTE: PowerShell DSC includes a File DSC resource “out of the box,” but we will use this as an example, for the sake of simplicity.

[DscResource()]
class File {
  [DscProperty(Key = $true)]
  [string] $FullPath
}

You can also expose other properties to the user of your custom DSC resource, by simply using the [DscProperty()] attribute without the key parameter. For example, let’s expose a [string] property called Contents that enables the user to define what the contents of the file should be.

[DscResource()]
class File {
  [DscProperty(Key = $true)]
  [string] $FullPath

  [DscProperty()]
  [string] $Contents
}

After you’ve implemented the DSC resource properties, you will need to implement a few methods in order to ensure that you have a valid DSC resource.

Pull Server

The DSC Pull Server is a web service that enables DSC managed endpoints to obtain their configuration from a centralized point, instead of deploying the DSC configuration via PowerShell Remoting, via Push mode. In order to utilize a DSC Pull Server, the LCM must be configured in the Pull refresh mode.

Debugging

PowerShell DSC offers debugging capabilities for authors of custom DSC Resources. The Enable-DscDebug command switches on debugging. If you add on the -BreakAll switch parameter, you can turn on debugging when an exception occurs. To disable debugging in the Local Configuration Manager (LCM), use the Disable-DscDebug command.

Classes

Starting with PowerShell version 5.0, the class keyword is supported. Classes can have constructors, properties, and methods defined on them.

PowerShell & .NET

One of the most powerful capabilities of Microsoft Windows PowerShell, is the ability for you to load and utilize .NET assemblies inside your PowerShell scripts and modules. Under this section, we’ll explore this powerful capability, and see how to load and consume .NET assemblies in PowerShell. For the purposes of this documentation, we will use the terms assembly and library as synonyms.

Finding Assemblies

Before loading a .NET assembly into your PowerShell environment, the first thing you’ll want to do is find a .NET assembly that you want to consume. There are lots of free and open source .NET libraries available, so search around on sites like GitHub for them. You can use Google to search around for .NET assemblies that perform specific tasks. For example, there’s a .NET library called TagLibSharp that enables you to modify media tags (eg. ID3v2 tags) using .NET.

Loading Assemblies

As with anything in PowerShell, there are multiple ways of accomplishing the task of loading .NET DLLs.

  • Use the Add-Type command with the -Path parameter
  • Use the [System.Reflection.Assembly]::LoadFile() static method

Inspecting Assemblies

Once you’ve loaded more assemblies into your AppDomain, you can begin inspecting them, for the purposes of learning how to use them, or how they were built. You can inspect .NET assemblies using some of these methods:

  • Reflect over them using PowerShell
  • Use Redgate Software’s .NET Reflector
  • Use the open source ILSpy utility (similar to .NET Reflector)
  • Use Visual Studio’s Object Browser

You can find out which assemblies have been loaded into PowerShell using the following command:

[System.AppDomain]::CurrentDomain.GetAssemblies()

Note that this only works on the “full” version of PowerShell that’s integrated into the Windows operating system, and will not work on PowerShell Core, which runs cross-platform on Windows, Mac, and Linux.