Skip to content

Scripting — PowerShell Cheatsheet

PowerShell automation and project structure — 2024–2025


Table of Contents


PowerShell Model

Get-Command *Service*
Get-Help Get-Service -Full
Get-Help Get-Service -Examples
Get-Member

Cmdlets follow Verb-Noun; use approved verbs (Get-Verb). PowerShell passes objects, not formatted text. Aliases are handy interactively but unclear in scripts.

Operator Meaning
-eq, -ne equal, not equal
-lt, -le, -gt, -ge comparisons
-like, -match wildcard, regex
-contains, -in collection contains value
-and, -or, -not logic
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

These settings surface hidden errors faster, but test existing scripts since non-terminating errors can become terminating.


Objects and Pipeline

Get-Service |
    Where-Object Status -eq 'Running' |
    Sort-Object DisplayName |
    Select-Object Name, DisplayName, Status
Get-Process |
    Select-Object Name, Id, @{Name='MemoryMB'; Expression={ [math]::Round($_.WorkingSet64 / 1MB, 2) }}
Cmdlet Task
Where-Object filter
Select-Object pick/compute properties
Sort-Object sort
Group-Object group
Measure-Object count/sum/average
ForEach-Object process each pipeline object

Format at the end

Format-Table and Format-List create formatting objects. Use them only as the final step for screen output, never before export or further processing.

$services = Get-Service             # data
$services | Export-Csv services.csv -NoTypeInformation
$services | Format-Table            # presentation

Variables and Collections

[string]$Name = 'Ada'
[int]$Count = 3
[datetime]$Today = Get-Date
[bool]$Enabled = $true
$null
$array = @('a', 'b', 'c')
$array[0]
$array[-1]

$list = [System.Collections.Generic.List[string]]::new()
$list.Add('item')

$settings = @{
    Domain = 'lab.local'
    Prefix = 'LAB'
}
$settings.Domain

Use [pscustomobject] for records:

$result = [pscustomobject]@{
    Computer = $env:COMPUTERNAME
    Success  = $true
    Time     = Get-Date
}

Variables inside double quotes are expanded; single quotes are literal. Use $($object.Property) for expressions in strings.


Selection and Loops

if ($FreePercent -lt 10) {
    'Critical'
} elseif ($FreePercent -lt 20) {
    'Warning'
} else {
    'OK'
}
switch -Regex ($line) {
    '^ERROR' { 'Error line' }
    '^WARN'  { 'Warning' }
    default  { 'Other line' }
}
foreach ($user in $users) { $user.Name }
for ($i = 0; $i -lt $items.Count; $i++) { $items[$i] }
while ($service.Status -ne 'Running') { $service.Refresh() }
do { $answer = Read-Host 'yes/no' } until ($answer -in 'yes','no')

Avoid infinite loops: provide a timeout, a maximum number of attempts, or an explicit stop condition.


Functions and Scripts

function New-ProjectFolder {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [switch]$Force
    )

    process {
        if ($PSCmdlet.ShouldProcess($Path, 'Create directory')) {
            New-Item -ItemType Directory -Path $Path -Force:$Force
        }
    }
}

Advanced functions support common parameters such as -Verbose, -ErrorAction, and with SupportsShouldProcess also -WhatIf/-Confirm.

Script parameters

[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [ValidateScript({ Test-Path $_ -PathType Leaf })]
    [string]$ConfigPath
)

Use return sparingly: any uncaptured output goes onto the pipeline. Status messages belong on Write-Verbose; warnings on Write-Warning; object results on the success stream.

Scope and modules

  • Avoid global variables.
  • Pass data via parameters and return objects.
  • Put reusable functions in a .psm1 and export deliberately.
  • Use a .psd1 manifest for metadata and exports.

Error Handling and Logging

try {
    $content = Get-Content -LiteralPath $Path -ErrorAction Stop
} catch [System.IO.FileNotFoundException] {
    Write-Error "File not found: $Path"
} catch {
    Write-Error "Failed: $($_.Exception.Message)"
    throw
} finally {
    # cleanup that must always run
}

try/catch catches terminating errors. Add -ErrorAction Stop when a cmdlet would otherwise only write a non-terminating error.

function Write-Log {
    param([string]$Message, [ValidateSet('INFO','WARN','ERROR')]$Level = 'INFO')
    $entry = '{0:o} [{1}] {2}' -f (Get-Date), $Level, $Message
    Add-Content -LiteralPath $script:LogPath -Value $entry
}

Log the action, target object, result and useful error context; never log passwords or secrets.


Files and Data Formats

Get-Content -LiteralPath file.txt
Set-Content -LiteralPath file.txt -Value $data -Encoding utf8
Add-Content -LiteralPath file.txt -Value $line
Get-ChildItem -LiteralPath $root -Recurse -File
Test-Path -LiteralPath $path

Use -LiteralPath for paths that may contain [ ] or wildcards.

CSV

$users = Import-Csv users.csv
$users | Export-Csv output.csv -NoTypeInformation -Encoding utf8

CSV yields strings: cast numeric, date and boolean fields explicitly.

JSON

$config = Get-Content config.json -Raw | ConvertFrom-Json
$object | ConvertTo-Json -Depth 10 | Set-Content result.json -Encoding utf8

XML

[xml]$xml = Get-Content config.xml -Raw
$xml.Settings.Domain.Name

Combining hashtable/config data

Validate required fields before applying changes. Normalize data into one internal object model, even when input comes from CSV, JSON and XML.


Windows and AD Automation

Get-LocalUser
New-LocalUser -Name 'operator' -Password $securePassword
Add-LocalGroupMember -Group 'Remote Desktop Users' -Member 'operator'

Get-SmbShare
New-SmbShare -Name 'Data' -Path 'D:\Data' -ChangeAccess 'LAB\Data_RW'
Import-Module ActiveDirectory
Get-ADUser -Filter * -SearchBase 'OU=Users,DC=lab,DC=local'
New-ADOrganizationalUnit -Name 'Sales' -Path 'DC=lab,DC=local'
New-ADGroup -Name 'GG_Sales' -GroupScope Global -GroupCategory Security -Path $ou
Add-ADGroupMember -Identity 'GG_Sales' -Members $user

Idempotency

A script must be safely re-runnable:

if (-not (Get-ADOrganizationalUnit -LDAPFilter '(ou=Sales)' -SearchBase $base -ErrorAction SilentlyContinue)) {
    New-ADOrganizationalUnit -Name 'Sales' -Path $base
}

Check the existing state, change only the difference, and report what happened. Use -WhatIf for risky administrative actions.


Robust Project Structure

project/
├── Invoke-Deployment.ps1
├── Config/
│   ├── Domain.Settings.xml
│   └── users.json
├── Data/
│   ├── ous.csv
│   ├── securitygroups.csv
│   └── shares.csv
├── Modules/
│   └── ProjectTools.psm1
└── Logs/

Recommended phases:

  1. determine parameters and paths;
  2. read configuration/data;
  3. validate schema, required values and references;
  4. show the plan (-WhatIf);
  5. execute dependencies in order: OUs → groups → users → memberships → folders → ACLs → shares;
  6. handle and log errors per step;
  7. verify and summarize the final state.

No plaintext passwords

Use SecureString, a secret store/vault or safe interactive input. Don't put credentials in source code, CSV, JSON or logs.