Terminology & Syntax

PowerShell Tips & Tricks

Embedded PowerShell Scripting is a great way to get data from Windows systems that provide management data via cmdlets. The following a few tips & tricks to get the most out of PowerShell.

Using Custom Device Properties in PowerShell

In addition to ingesting standard device properties in PowerShell scripts along the lines of ##system.hostname##, you can also use any custom-defined device property such as ##my.custom.property##. Prior to running through the PowerShell interpreter, each property specified in this format is substituted with the value stored for that device.

Note that where a specified property has NOT been defined for a device, the substitution is handled differently beween Active Discovery scripts and Data Collection scripts. In an Active Discovery script an unspecified device property undergoes no substitution, so code written as:

$foo = '##my.custom.property##';

will execute exactly as shown here with no substitution. If, in your Active Discovery script you wanted to test if a device property isn't set you would test for a lack of substitution:

# was my.custom.property set for this device?
if ($foo eq '##my.custom.property##')
{
    # no. handle this case 
}

In a Data Collection script this is handled differently, in that an unspecified device property is substituted as an empty string. This means that if you wanted to this same test in a Data Collection script you'd write your conditional as:

# was my.custom.property set for this device?
if ($foo eq '')
{
    # no. handle this case
}

PowerShell Authentication

A handful of PowerShell cmdlets such as Get-WMIObject can query remote systems directly. Many others – including the PowerShell Remoting features discussed below – require that remote credentials are provided directly.

To leverage remote credentials you'll need to bundle them into a credential object and then pass this object as a parameter into any cmdlets that require remote authentication. You can create a credential object from stored device properties in LM as follows:

$hostname = '##SYSTEM.HOSTNAME##';
$userid   = '##THIS.USER##';
$passwd   = '##THIS.PASS##';

# build a credential object
$secure_passwd  = ConvertTo-SecureString `
  -String $passwd -AsPlainText -Force;
$user_credential = New-Object `
  -typename System.Management.Automation.PSCredential`
  ($userid, $secure_passwd);

Restart-Computer -ComputerName $hostname -Credential $user_credential;

PowerShell Remoting – Initial Setup

Although a handful of the standard PowerShell cmdlets like Get-WMIObject allow for querying metrics on remote systems, typically you'll need to setup PowerShell Remoting to run cmdlets on remote systems monitored by the Collector. To do so you'll of course need administrative rights on both systems.

  1. On each target Windows system monitored by a given Collector, you'll need to start the WinRM service, configure it for automatic startup, and add firewall exceptions for WS-Management communications. You can do all of this with a single command:
    Enable-PSRemoting
  2. Also on the target system, you'll need to specify the Collector as a "trusted host" from which it will allow remote connections.
    Set-Item WSMan:\localhost\Client\TrustedHosts CollectorHostName
  3. Then on the Collector system, you'll need to add each target host to TrustedHosts as comma-separated list. Or alternately specify that it is authorized to connect to any host:
    Set-Item WSMan:\localhost\Client\TrustedHosts *
  4. Also on the Collector, you'll likely want to increase the remote shell quota to allow for a sufficient number of shells:
    winrm set winrm/config/winrs '@{MaxShellsPerUser="50"}'
  5. Finally, you'll want to restart the WinRM service on both hosts to ensure these settings take effect.
    Restart-Service WinRM

Once you've got this configured, you can verify operation by running the following from the Collector:

Invoke-Command -ComputerName <target hostname> -ScriptBlock { Write-Host "Running Remotely on ${env:computername}" } -Credential <domain>\<user>

This will prompt you for the password of the specified user and, upon successful authentication, will report the name of the remote system on which it's running.

 

PowerShell Remoting - Example Using Invoke-Command

Once you've successfully configured remoting between your Collector and remote systems, there are a couple different ways to actuate remote commands. In this first example we use the Invoke-Command cmdlet, which you'd use when you need to actuate only a single remote command. Here's how we'd use this technique to generate a list of Veeam backup jobs:

# ingest the hostname and credentials from LM device properties
$hostname = '##SYSTEM.HOSTNAME##';
$userid   = '##THIS.USER##';
$passwd   = '##THIS.PASS##';

# build a credential object
$secure_passwd  = ConvertTo-SecureString `
  -String $passwd -AsPlainText -Force;
$user_credential = New-Object `
  -typename System.Management.Automation.PSCredential`
  ($userid, $secure_passwd);

# initiate a session to the remote host
$session = New-PSSession `
  -ComputerName $hostname `
  -Credential $user_credential;

# invoke a command on the remote host and store the output
$veeam_jobs = Invoke-Command `
  -Session $session `
  -ScriptBlock {  Add-PSSnapin VeeamPSSnapin; Get-VBRJob -WarningAction silentlyContinue; };

# iterate over the list of backup jobs
foreach ($job in $veeam_jobs) 
{
    # get the instance info for this job
    $instance_id   = $job.Name;
    $instance_desc = $job.Description;

    # write out an instance for each job
    Write-Host "$instance_id##$instance_id##$instance_desc";
}

# disconnect the session
Remove-PSSession $Session; 

# exit with a return code indicating success
Return 0;

PowerShell Remoting - Example Using Import-PSSession

An alternate approach to executing remote commands is to use Import-PSSession to import cmdlets into your session. This might be useful when you have a number of commands you'd like to pull into a single session. In this case we use the technique to report statistics around Exchange Server Mailbox Database Copies.

# ingest the hostname, instance, and required credentials
$hostname    = '##SYSTEM.HOSTNAME##';
$userid      = '##THIS.USER##';
$passwd      = '##THIS.PASS##';
$instance_id = '##WILDVALUE##';

# build a credential object
$secure_passwd  = ConvertTo-SecureString `
  -String $passwd -AsPlainText -Force;
$user_credential = New-Object `
  -typename System.Management.Automation.PSCredential`
  ($userid, $secure_passwd);

# initiate a session to the remote host
$session = New-PSSession `
  -ComputerName $hostname `
  -Credential $user_credential;

# import the specified cmdlet into the session
$import_session = Import-PSSession $session `
  -CommandName Get-MailboxDatabaseCopyStatus `
  -AllowClobber;

# run the imported cmdlet and filter for the specified instance
$mailbox_db_copy = Get-MailboxDatabaseCopyStatus | `
  Where-Object {$_.Name -eq $instance_id} | `
  Select-Object Status,CopyQueueLength,ReplayQueueLength,ContentIndexState;

Write-Host "Status:" $mailbox_db_copy.Status;
Write-Host "CopyQueueLength:" $mailbox_db_copy.CopyQueueLength;
Write-Host "ReplayQueueLength:" $mailbox_db_copy.ReplayQueueLength;
Write-Host "ContentIndexState:" $mailbox_db_copy. ContentIndexState;

# disconnect the session
Remove-PSSession $Session;

# exit with a return code indicating success
Return 0;