Using Pester Tests to Test Anything

Pester is an open source module shipping with Windows 10 and Windows Server 2016, and can be downloaded from the PowerShell Gallery (www.powershellgallery.com) for free (provided you have installed at least PowerShellGet):

 
PS C:> Install-Module -Name Pester -Force -SkipPublisherCheck 
 

Pester is a testing framework primarily used to test PowerShell code. You are not limited to code tests, though, and so you can test anything with Pester. Here is a little example that tests your PowerShell version and a couple of its settings:

Describe 'PowerShell Basic Check' {

  Context 'PS Versioning'   {
    It 'is current version' {
      $host.Version.Major -ge 5 -and $host.Version.Minor -ge 1 | Should Be $true
    }
  }
  Context 'PS Settings'   {
    It 'can execute scripts' {
      (Get-ExecutionPolicy) | Should Not Be 'Restricted'
    }
    It 'does not use AllSigned' {
      (Get-ExecutionPolicy) | Should Not Be 'AllSigned'
    }
    It 'does not have GPO restrictions' {
      (Get-ExecutionPolicy -Scope MachinePolicy) | Should Be 'Undefined'
      (Get-ExecutionPolicy -Scope UserPolicy) | Should Be 'Undefined'
    }
  }
}

When you run it (provided the Pester module is available, of course), this is the output you get:

 
Describing PowerShell Basic  Check 
 
  Context PS Versioning 
    [+] is current version 76ms 
 
  Context PS Settings 
    [+] can execute scripts 47ms 
    [+] does not use AllSigned 18ms 
    [+] does not have GPO restrictions 21ms 
 
PS>
 

Of course, this is just an example. You could expand on this and extend the test to many other settings or prerequisites.

Twitter This Tip! ReTweet this Tip!

Reading Environment Variables Freshly

When you read environment variables in PowerShell, you probably make use of the “env:” drive. This line retrieves the environment variable %USERNAME%, for example, telling you the name of the user executing the script:

 
PS C:> $env:USERNAME
tobwe

PS C:>
 

The “env:” drive always accesses the process set of environment variables. This makes sense in most cases as many of the environment variables (like “UserName”) are defined in this set. Basically, the process set of environment variables is a “snapshot” of all environment variables at the time of when an application starts, plus a number of additional pieces of information (like “UserName”).

To read environment variables freshly and explicitly from the system or user set, use code like this:

$name = 'temp'
$scope = [EnvironmentVariableTarget]::Machine

$content = [Environment]::GetEnvironmentVariable($name, $scope)
"Content: $content"

You could use this technique, for example, to communicate between two processes. To play with this, open two PowerShell consoles. Now, in the first console, enter this:

[Environment]::SetEnvironmentVariable("PS_Info", "Hello", "user")

In the second PowerShell console, enter this line to receive the information:

[Environment]::GetEnvironmentVariable("PS_Info", "user") 

To clean up the environment variable, enter this line in either console:

[Environment]::SetEnvironmentVariable("PS_Info", "", "user")

Twitter This Tip! ReTweet this Tip!

Setting Environment Variables

When setting environment variables through the PowerShell “env:” drive, you are always just manipulating the process set. It applies to your current PowerShell instance, and all applications that you start from there. Changes will not persist, though.

To permanently set an environment variable, use this code instead:

$name = 'Test'
$value = 'hello'
$scope = [EnvironmentVariableTarget]::User

[Environment]::SetEnvironmentVariable($name, $value, $scope)

This example sets a new environment variable named “test” with content “hello” on user level. Note that this change will affect only programs that you launch after you set this variable. All new applications receive a “snapshot” of all environment variables in their process set.

To permanently delete an environment variable, set $value to an empty string.

Twitter This Tip! ReTweet this Tip!

Checking Host

In the past, Microsoft shipped two PowerShell hosts: the basic PowerShell console, and the more sophisticated PowerShell ISE. Some users used code like below to find out whether a script runs in the console or the PowerShell ISE:

$inISE = $psISE -ne $null

"Running in ISE: $inISE"

However, there are many more hosts around these days. Visual Studio can host PowerShell, and so does Visual Studio Code. And there are additional commercial editors. So if you must know whether a script runs in a given environment, use the host identifier instead:

$name = $host.Name
$inISE = $name -eq 'Windows PowerShell ISE Host'

"Running in ISE: $inISE"

Each host emits its own host name, so this approach can be adjusted to any host. When you run a script inside Visual Studio Code, for example, the host name is “Visual Studio Code Host”.

Twitter This Tip! ReTweet this Tip!

Playing with PowerShell 6.0

PowerShell is open source now, and the next big release of PowerShell is being developed in the open. If you’d like to take a peek preview, simply navigate to the open source project release page, and download the appropriate release:

https://github.com/PowerShell/PowerShell/releases

PowerShell 6.0 is also a cross-platform. You find versions for Windows operating systems right next to versions for Linux or OS X.

Once you downloaded a release on Windows systems in ZIP format, make sure you unblock the file first (right-click the file, choose “Properties”, then unblock). Next, extract the archive. Inside of it, you find powershell.exe, and you can double-click this to launch a PowerShell 6.0 console. It’s completely independent of shipping PowerShell versions so you can use it side-by-side with your existing PowerShell version.

 
PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.

PS C:UserstobweDownloadsPowerShell_6.0.0-alpha.15-win10-win2k16-x64> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      6.0.0-alpha
CLRVersion
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
GitCommitId                    v6.0.0-alpha.15
BuildVersion                   3.0.0.0
PSEdition                      Core


PS C:UserstobweDownloadsPowerShell_6.0.0-alpha.15-win10-win2k16-x64>
 

If you are a developer, check out the GitHub project: you can view all source codes, and even join the community of people working on this version.

Twitter This Tip! ReTweet this Tip!

Caching Credentials Using JSON

When you need to cache logon credentials to a file, this is typically done by piping the credential to Export-Clixml which produces a rather lengthy XML file. With Import-Clixml, the cached credential can then be imported back into a script whenever you need it. PowerShell automatically uses the user and machine identity to encrypt the password (and it can only be read back by the same person on the same machine).

The same can be done in JSON format as well which produces less cluttered files. Just the password encryption part needs to be done manually.

This example prompts for logon credentials, then saves them to a “mycred.json” file on your desktop, and opens the file in the Notepad so you can view the content and make sure the password was encrypted:

$path = "$homeDesktopmycred.json"

$cred = Get-Credential
$cred | 
  Select Username,@{n="Password"; e={$_.password | ConvertFrom-SecureString}} |
  ConvertTo-Json |
  Set-Content -Path $path -Encoding UTF8


notepad.exe $path 

To later reuse the file and import the credential, use this:
$path = "$homeDesktopmycred.json"

$o = Get-Content -Path $path -Encoding UTF8 -Raw | ConvertFrom-Json 
$cred = New-Object -TypeName PSCredential $o.UserName, 
  ($o.Password | ConvertTo-SecureString)

# if you entered a valid user credentials, this line
# will start Notepad using the credentials retrieved from 
# the JSON file to prove that the credentials are
# working. 
Start-Process notepad -Credential $cred

To later reuse the file and import the credential, use this:

$path = "$homeDesktopmycred.json"

$o = Get-Content -Path $path -Encoding UTF8 -Raw | ConvertFrom-Json 
$cred = New-Object -TypeName PSCredential $o.UserName, 
  ($o.Password | ConvertTo-SecureString)

# if you entered a valid user credentials, this line
# will start Notepad using the credentials retrieved from 
# the JSON file to prove that the credentials are
# working. 
Start-Process notepad -Credential $cred

Note that this example will use the credential stored in the JSON file to launch an instance of Notepad under these user credentials. This obviously fails if you entered invalid logon information when you created the JSON file in the first example script.

Note also that the password will be saved encrypted, using your user account and your machine as secrets. So the saved password is securely encrypted, but the technique shown here is only suitable for tasks where the same person (on the same machine) wants to later reuse the saved credential. One use case would be to save credentials for scripts you run frequently on your own machine.

Twitter This Tip! ReTweet this Tip!