WSA M18: PowerShell Automation

Slide 1 of 10 ← Course Home

Module 18: PowerShell Automation

Scripts replace clicks, DSC replaces drift — from fundamentals to declarative config.

What you'll learn

  • Script fundamentals — variables, functions, try/catch
  • Task Scheduler — how PS scripts get scheduled at scale
  • DSC ★ — declarative configuration — "this is enforced"
  • Remoting — PSRemoting basics, push vs pull
  • Modules + repos — PSGallery + internal repos
Where this fits: Capstone #18 — config-as-code. CTS1328C Objective #1. AZ-800: Manage Windows Servers (automation). DSC (★) flips you from "I configured this" to "this is enforced."
Module 18 — your journey 1Script Fundamentals 2Error Handling 3Task Scheduler 4DSC ★ 5DSC Implementation 6Remoting 7Workflow Patterns 8Modules 9Repositories → next: M19 — Troubleshooting

PowerShell Script Fundamentals

The difference between a one-off command and a production script is structure. A robust script declares its prerequisites, validates its inputs, fails loudly, and behaves predictably, all before it does any real work.

The skeleton. #Requires statements assert the PowerShell version and rights the script needs (and refuse to run otherwise). A typed, validated param() block defines and checks inputs up front. Set-StrictMode turns sloppy mistakes (using an undefined variable) into errors. Setting $ErrorActionPreference = 'Stop' makes errors actually stop the script so your try/catch can handle them. Using approved verbs (Get/Set/New) and supporting -WhatIf keeps scripts predictable and safe to dry-run.

# Production script header PS> #Requires -Version 5.1 #Requires -RunAsAdministrator param([Parameter(Mandatory)][string]$ServerName, [switch]$Force) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop'
A production script declares prerequisites with #Requires, validates typed parameters, enables StrictMode, and uses approved verbs with -WhatIf.
Script SkeletonCTS1328C · AZ-800
A production script declares prerequisites with #Requires, validates typed parameters, enables StrictMode, and uses approved verbs with -WhatIf.

Error Handling & Logging

An automation script that fails silently is worse than no script, because you trust it and it lets you down without telling you. Production scripts capture every outcome and record what happened.

Logging. A small reusable Write-Log function that stamps each line with a timestamp and severity turns a script into an auditable record. Every meaningful action writes a line, so when something breaks at 3 AM the log tells you exactly where.

Structured error handling. With $ErrorActionPreference = 'Stop', a try/catch/finally block catches failures (log the error and re-throw so callers know it failed), while finally runs cleanup regardless of outcome. The pattern is: try the work, catch and log+throw on failure, finally log completion.

# Logging + try/catch/finally pattern PS> function Write-Log { param($Msg,$Level='INFO') "[$(Get-Date -f 'yyyy-MM-dd HH:mm:ss')] [$Level] $Msg" | Add-Content $LogPath } try { Write-Log "Start"; Invoke-Command -ComputerName $S -Sb {Get-Service} } catch { Write-Log "ERROR: $_" 'ERROR'; throw } finally { Write-Log "Done" }
Wrap work in try/catch/finally with ErrorAction Stop and a timestamped Write-Log so failures are caught and recorded instead of lost silently.
Errors + LoggingCTS1328C · AZ-800
Wrap work in try/catch/finally with ErrorAction Stop and a timestamped Write-Log so failures are caught and recorded instead of lost silently.

Task Scheduler Integration

A script only delivers value when it runs on its own. Task Scheduler is how recurring PowerShell automation runs unattended, on a schedule or in response to events, without anyone logged in.

The three pieces. A trigger says when (a time like daily at 2 AM, or an event like a log entry, startup, or idle). An action says what (run powershell.exe against your script). A principal says who (a user, or SYSTEM with highest privileges so the task works even when no one is logged on). Conditions can further gate it (only on AC power, only if the network is available).

# Daily 2 AM maintenance task running as SYSTEM PS> $a = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-File C:\Scripts\DailyMaint.ps1' $t = New-ScheduledTaskTrigger -Daily -At '2:00AM' $p = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest Register-ScheduledTask -TaskName 'DailyMaint' -Action $a -Trigger $t -Principal $p
Run recurring automation unattended by registering a scheduled task with a trigger (when), an action (what), and a principal like SYSTEM (who).
Scheduled TasksCTS1328C · AZ-800
Run recurring automation unattended by registering a scheduled task with a trigger (when), an action (what), and a principal like SYSTEM (who).

Desired State Configuration (DSC)

Desired State Configuration flips automation from imperative ("run these steps") to declarative ("the server should look like this"). You describe the end state, and a local agent makes the machine match it and keeps it matching over time.

The pieces. A Configuration block contains resources (WindowsFeature, File, Service, Registry, and many more), each declaring a desired state with Ensure = 'Present' or similar. The configuration compiles to a MOF file, the machine-readable contract. The Local Configuration Manager (LCM) is the agent on each node that applies the MOF and, crucially, re-applies it on a schedule so configuration drift is automatically corrected.

# Declare: IIS installed, default page present PS> Configuration WebServerConfig { Node 'WebServer01' { WindowsFeature IIS { Name='Web-Server'; Ensure='Present' } File Index { DestinationPath='C:\inetpub\wwwroot\index.html'; Ensure='Present' } } }
DSC is declarative: you describe the desired state in a Configuration, compile it to a MOF, and the LCM agent applies it and corrects drift over time.
DSCCTS1328C · AZ-800
DSC is declarative: you describe the desired state in a Configuration, compile it to a MOF, and the LCM agent applies it and corrects drift over time.

Implementing DSC

Putting DSC to work is a four-step loop, and choosing a delivery mode determines how the configuration reaches your nodes.

The four steps. Define the configuration (often a security baseline as code), compile it to MOF with an output path, apply it with Start-DscConfiguration, and verify with Test-DscConfiguration (which reports whether the node still matches, the drift check).

Push vs Pull. In push mode you send the MOF to nodes on demand, simple, good for a handful of servers. In pull mode nodes periodically poll a pull server for their assigned configuration, which scales to large fleets and re-applies automatically. Pull is the enterprise pattern.

# Compile, push, then verify (drift check) PS> SecureServer -OutputPath 'C:\DSC\SecureServer' Start-DscConfiguration -Path 'C:\DSC\SecureServer' -Wait -Verbose Test-DscConfiguration -Detailed
Apply DSC in four steps, define, compile, apply, verify, using push for a few nodes or pull, where nodes poll a server, to scale to a fleet.
Applying DSCCTS1328C · AZ-800
Apply DSC in four steps, define, compile, apply, verify, using push for a few nodes or pull, where nodes poll a server, to scale to a fleet.

PowerShell Remoting

Remoting is what lets one administrator manage hundreds of servers. Instead of logging into each box, you run a script block against many machines in parallel and collect structured results.

The four patterns. Invoke-Command runs a script block on many computers at once (fire-and-forget fan-out, the workhorse). Enter-PSSession drops you into an interactive shell on one remote machine. New-PSSession creates a persistent connection you can reuse across commands. CIM/WMI queries pull management data from many hosts without full remoting. Throttling (-ThrottleLimit) controls how many run concurrently.

# Uptime + free space across N servers in parallel PS> Invoke-Command -ComputerName DC01,WEB01,SQL01 -ThrottleLimit 10 -ScriptBlock { [PSCustomObject]@{ Name=$env:COMPUTERNAME FreeGB=[math]::Round((Get-PSDrive C).Free/1GB,2) } }
Remoting runs work across many servers at once: Invoke-Command for fan-out, and Enter or New-PSSession for an interactive or persistent session.
RemotingCTS1328C · AZ-800
Remoting runs work across many servers at once: Invoke-Command for fan-out, and Enter or New-PSSession for an interactive or persistent session.

Automation Workflow Patterns

Most enterprise automation falls into a few repeating shapes. Recognizing them means you reuse the same idiom (a parameterized function returning structured objects) instead of reinventing a script each time.

The four patterns. Provisioning stands up new resources (a new user, server, or share). Deployment rolls software or config out across machines. Compliance checks and enforces a standard (often via DSC). Backup captures and verifies recoverable state. Wrapping a multi-step process in a single well-named function with parameters turns a fragile sequence into a reusable, testable building block.

# Onboarding wrapped in one reusable call PS> function New-HexworthUser { param($First,$Last,$Dept) $u = ($First.Substring(0,1)+$Last).ToLower() New-ADUser -Name "$First $Last" -SamAccountName $u -Department $Dept -Enabled $true Add-ADGroupMember -Identity "GRP-$Dept" -Members $u }
Most enterprise automation is provisioning, deployment, compliance, or backup, each built from one idiom: a parameterized function returning structured output.
Workflow PatternsCTS1328C · AZ-800
Most enterprise automation is provisioning, deployment, compliance, or backup, each built from one idiom: a parameterized function returning structured output.

PowerShell Modules & Repositories

Once you have useful functions, modules are how you package them for reuse and repositories are how you distribute them. This is the step from "scripts on my machine" to "tooling the whole team installs."

Module layout. A module is a .psm1 file (the code) plus a .psd1 manifest declaring its version, exported functions, and dependencies. A well-structured module separates Public/ (exported) from Private/ (internal helpers) and includes Tests/ (Pester tests).

Repositories. You install community modules from the PSGallery, register an internal repository (a NuGet feed or a file share) for your own tools, and publish to it so every admin runs Install-Module instead of copying scripts around.

# Install, register an internal repo, publish PS> Install-Module -Name 'Az.Accounts' -Scope AllUsers Register-PSRepository -Name 'HexworthRepo' -SourceLocation '\\fileserver\PSModules' -InstallationPolicy Trusted Publish-Module -Name 'HexworthTools' -Repository 'HexworthRepo'
Package functions as a module (.psm1 plus a .psd1 manifest) and distribute them through the PSGallery or an internal repository so everyone installs the same tooling.
Modules + ReposCTS1328C · AZ-800
Package functions as a module (.psm1 plus a .psd1 manifest) and distribute them through the PSGallery or an internal repository so everyone installs the same tooling.

Module Summary

This module built the ladder from a single command to a managed automation platform. Each rung makes your scripts more reliable, more reusable, and more scalable.

  • Scripts: a hardened skeleton, typed params, strict mode, approved verbs, -WhatIf.
  • Errors: try/catch/finally with structured, timestamped logging so nothing fails silently.
  • Scheduling: Task Scheduler runs recurring automation unattended as SYSTEM.
  • DSC: declarative configuration that the LCM applies and re-applies, correcting drift.
  • Remoting: Invoke-Command and PSSessions run work across many hosts in parallel.
  • Modules: package functions and distribute them through repositories.
Next: the labs put these together, you will write, schedule, and distribute real automation.
This module climbed from a hardened script to a managed platform: error handling and logging, scheduling, DSC, remoting, and packaged modules.
Module SummaryCTS1328C · AZ-800
This module climbed from a hardened script to a managed platform: error handling and logging, scheduling, DSC, remoting, and packaged modules.