paulgorman.org

Windows PowerShell

See the MS TechNet PowerShell documentation.

Windows PowerShell is a shell scripting environment suitable for system administration tasks. Unlike most admin scripting environments that process text, PowerShell processes .NET objects. PowerShell uses its own new language, rather than repurposing an exiting language like VBScript or Javascript.

Built-in PowerShell commands are commandlets named like verb-noun (e.g.—Get-Help or Start-Service). Every built-in commandlet provides help with:

get-help commandlet-name -detailed | more

Another useful exploration commandlet is get-member. The get-service commandlet (run without any arguments) returns a list of services. Piping that to get-member shows the members and methods of service objects.

get-service | get-member

PowerShell has a number of built-in aliases, as seen from get-alias. ls (an alias for Get-ChildItem) does what one expects, as does cat (an alias for get-content). There's no exact analog for 'grep', because everything is object-based rather than text-based. However:

get-service | where-object {$_.status -eq 'running'}

Note that PowerShell is case insensitive. You can often turn on case sensitivity for comparison operators by prepending "c" to flags.

PS> 'test','TEST','Test' | ? {$_ -like 'Test'}
test
TEST
Test
PS> 'test','TEST','Test' | ? {$_ -clike 'Test'}
Test

? is an alias for the where-object commanlet. Where-Object is a filter that evaluates the bracketed expression, and returns it if it evaluates as true. $_ is a built-in variable that holds the current argument of the current block/iteration (the current pipeline iteration in this case). See get-help about_automatic_variables. There are a variety of filters, including sort (Sort-Object):

get-eventlog -newest 25 -logname System | sort -descending -property Source,Message

Comments are prepended by a hash:

# This is a comment.

The get-command commandlet shows a list of all available commandlets.

As of late 2012, the current version is PowerShell 3.0.

Scripts

PowerShell scripts are normal text files with a .ps1 extension. However, execution of scripts is restricted by default (Get-ExecutionPolicy and Set-ExecutionPolicy and Get-Help about_Execution_Policies), and scripts can't be run by double-clicking in any event.

Set-ExecutionPolicy Unrestricted -scope CurrentUser

Run a script:

$home\myScripts\script.ps1

In Windows 8 and Server 2012, scripts can be run by right-clicking on them and choosing "Run with PowerShell" (though obviously this is only appropriate for scripts that don't require arguments and don't send important output to the console).

Microsoft also includes a lightweight IDE for PowerShell: powershell_ise.exe.

Powershell Scripting Basic Syntax

# Hash starts a comment

<# A multi-
   line
   comment.
   These seem to only work in scripts,
   not in the shell.
#>

$_    # The current object, soft of like in Perl
$Args    # Parameters passed to a script, function, or block
$False    # Evaluates to false
$True    # Evaluates to true
$NULL    # Evaluates to null
$Home    # Full path to user's home directory
$Profile    # Path to user's profile
$Pwd    # Current working directory

# Comparison operators
-eq    # equal
-ne    # not equal
-gt    # greater than
-ge    # greater or equal
-lt    # less than
-le    # less or equal
-like  # wildcard pattern matching
-match # regex
-contains    # Test if an item is present in a collection

# Conditionals

if ($foo -eq $True) {
    Write-Host "Yes"
} elseif ($foo -eq "Blue") {
    Write-Host "Blue"
} else {
    Write-Host "Other"
}

switch ($x) {
    "a" { Write-Host "A" }
    "b" { Write-Host "B" }
    default { Write-Host "C-Z" }
}

# Looping

for ($i = 0; $i -le 10; $i++) {
    Write-Host $i
}

foreach ($i in Get-Alias) {
    Write-Host $i.name
}

$i = 0
do {
    Write-Host $i++
} while ($i -lt 10)

Objects

Object creation varies between COM and .NET objects.

# COM:
$myComObj = New-Object -comobject "InternetExplorer.Application"
$myComObj.visible = $True
$myComObj.Quit

# .NET:
$myNetObj = [System.Net.Dns]::GetHostAddresses("mail.example.com")
$myNetObj | Select-Object IpAddressToString

Show the members and properties of an object by piping the object to get-member (which is also available as the alias gm).

PS H:\> get-wmiobject win32_logicaldisk | gm


   TypeName: System.Management.ManagementObject#root\cimv2\Win32_LogicalDisk

Name                         MemberType    Definition                                        
----                         ----------    ----------                                        
PSComputerName               AliasProperty PSComputerName = __SERVER                         
Chkdsk                       Method        System.Management.ManagementBaseObject Chkdsk(S...
Reset                        Method        System.Management.ManagementBaseObject Reset()    
SetPowerState                Method        System.Management.ManagementBaseObject SetPower...
Access                       Property      uint16 Access {get;set;}                          
Availability                 Property      uint16 Availability {get;set;}                    
BlockSize                    Property      uint64 BlockSize {get;set;}                       
Caption                      Property      string Caption {get;set;}
[...snip...]

Functions

function My-Function ($bar) {
    return $bar + 1
}
My-Function 1

When calling a function don't wrap the arguments in parens.

Example Script

# This Powershell script monitors free disk space, and alerts if it
# falls below a certain threshold. -- Paul Gorman 12 March 2015

$percentFreeThreshold = 20
$alertTriggered = $False
$mailServer = "mail.example.com"
$mailFrom = "alert@example.com"
$mailTo = "alert@example.com"
$mailSubject = "Check disk space on server!"
$mailBody = "The script check_drive_space.ps1 on server sees that drive free space has fallen below the acceptable threshold."

function Percent-Free ($disk) {
    return [Math]::Round(($_.FreeSpace / $_.Size) * 100)
}

$drives = Get-WmiObject Win32_LogicalDisk |
    Where-Object {$_.Size -gt 1} # Ignore optical drive

$drives | ForEach-Object {
    if ((Percent-Free $_) -lt $percentFreeThreshold) {
        $alertTriggered = $True
    }   
}

if ($alertTriggered) {
    Send-MailMessage -SmtpServer $mailServer -to $mailTo -from $mailFrom -subject $mailSubject -body $mailBody
}