Automate repetitive tasks, manage Active Directory, and build reusable scripts that save hours of manual work.
Learning Objectives
Module Breakdown
Module 1: PowerShell Foundations
🕐 ~2.5 hoursCore Skills
Getting started, syntax, variables, data types
Working with objects, properties, and methods
Conditional logic and loops
Module 2: Active Directory & File Operations
🕐 ~2 hoursIT Support Focus
AD user/computer management cmdlets
File system navigation and manipulation
Error handling with try/catch
Module 3: Functions, Automation & Scheduling
🕐 ~2 hoursAdvanced
Writing and parameterizing functions
Scheduling scripts with Windows Task Scheduler
Security considerations and credential management
Module 1: PowerShell Foundations
What is PowerShell?
PowerShell is a cross-platform task automation framework from Microsoft, consisting of a command-line shell and scripting language. Unlike traditional shells that work with text, PowerShell works with .NET objects, enabling powerful piping and object manipulation.
PowerShell Versions
Windows PowerShell 5.1: The legacy version built into Windows 10/11. Still widely used in enterprise environments. PowerShell 7+ (pwsh): The modern cross-platform version built on .NET Core. Supports Linux, macOS, and Windows. All examples below work in both versions unless noted.
Everything in PowerShell is an object. Cmdlets return objects with properties you can access and methods you can invoke.
# Get processes as objects and filter by property
Get-Process | Where-Object { $_.CPU -gt 100 } | Select-Object Name, CPU, Id
# View available properties and methods
Get-Process | Get-Member
# Call a method directly
(Get-Date).AddDays(7).ToString("yyyy-MM-dd")
# Format output as a table
Get-Service | Where-Object { $_.Status -eq 'Running' } |
Sort-Object DisplayName |
Select-Object -First 10 DisplayName, Status, StartType
Key Syntax Notes
The $_.Property syntax refers to the current object in a pipeline. Get-Member (alias gm) shows all properties and methods available on any object. Use Select-Object (alias select) to choose which properties appear in your output.
Conditional Logic
# If / Else if / Else
$serviceStatus = (Get-Service -Name wuauserv).Status
if ($serviceStatus -eq 'Running') {
Write-Host "Windows Update service is running." -ForegroundColor Green
} elseif ($serviceStatus -eq 'Stopped') {
Write-Host "Windows Update service is stopped. Attempting to start..." -ForegroundColor Yellow
Start-Service wuauserv
} else {
Write-Host "Service status is: $serviceStatus" -ForegroundColor Red
}
# Switch statement (cleaner for multiple conditions)
$exitCode = $LASTEXITCODE
switch ($exitCode) {
0 { Write-Host "Success!" -ForegroundColor Green }
1 { Write-Host "Warning -- non-fatal error" -ForegroundColor Yellow }
2 { Write-Host "Critical failure" -ForegroundColor Red }
default { Write-Host "Unknown exit code: $exitCode" }
}
# Comparison operators reference
# -eq (equal), -ne (not equal), -gt (greater than), -lt (less than)
# -ge (>=), -le (<=), -like (wildcard match), -match (regex)
# -contains, -in, -not, -and, -or
Loops for Batch Operations
# For loop -- iterate through a list of computers
$servers = @("SRV-DC01", "SRV-APP01", "SRV-FILE01")
foreach ($server in $servers) {
if (Test-Connection -ComputerName $server -Quiet -Count 2) {
Write-Host "$server is ONLINE" -ForegroundColor Green
} else {
Write-Host "$server is OFFLINE" -ForegroundColor Red
}
}
# While loop -- process until condition changes
$maxAttempts = 5
$attempt = 0
$connected = $false
while ($attempt -lt $maxAttempts -and -not $connected) {
$attempt++
Write-Host "Attempt $attempt of $maxAttempts..."
$connected = Test-Connection -ComputerName "SRV-DC01" -Quiet -Count 1
Start-Sleep -Seconds 2
}
if ($connected) { Write-Host "Connection established!" -ForegroundColor Green }
# ForEach-Object (pipeline version) -- essential for production scripts
Get-Process | Where-Object { $_.WorkingSet64 -gt 100MB } |
ForEach-Object {
Write-Host "$($_.Name) is using $([math]::Round($_.WorkingSet64/1MB)) MB RAM"
}
Exercise 1: User Account Age Report
Write a script that queries Active Directory for user accounts, finds those older than 90 days, and outputs their SamAccountName, LastLogonDate, and Created date. If the AD module is not available, simulate with sample data.
# Simulation without AD module (use Replace-AdventureWorks for real AD)
$simulatedUsers = @(
@{SamAccountName = "jsmith"; LastLogon = (Get-Date).AddDays(-150); Created = (Get-Date).AddDays(-200)},
@{SamAccountName = "ajones"; LastLogon = (Get-Date).AddDays(-30); Created = (Get-Date).AddDays(-60)},
@{SamAccountName = "bwilliams"; LastLogon = (Get-Date).AddDays(-365); Created = (Get-Date).AddDays(-400)},
@{SamAccountName = "mdavis"; LastLogon = (Get-Date).AddDays(-95); Created = (Get-Date).AddDays(-120)}
)
$cutoffDate = (Get-Date).AddDays(-90)
$inactiveUsers = $simulatedUsers | Where-Object { $_.LastLogon -lt $cutoffDate }
Write-Host "Inactive users (no login in 90+ days):" -ForegroundColor Yellow
$inactiveUsers | ForEach-Object {
Write-Host " $($_.SamAccountName) -- Last logon: $($_.LastLogon.ToString('yyyy-MM-dd')) | Created: $($_.Created.ToString('yyyy-MM-dd'))"
}
# Real AD equivalent (uncomment when AD module is available):
# Import-Module ActiveDirectory
# Get-ADUser -Filter {Enabled -eq $true} -Properties LastLogonDate, Created |
# Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } |
# Select-Object SamAccountName, LastLogonDate, Created |
# Export-Csv -Path "C:\Reports\StaleAccounts.csv" -NoTypeInformation
Module 2: Active Directory & File Operations
Active Directory Cmdlets
The ActiveDirectory module provides over 200 cmdlets for managing AD. These are available on Domain Controllers and machines with RSAT (Remote Server Administration Tools) installed.
Create a PowerShell script that takes FirstName, LastName, Department, and Manager as parameters. It should create an AD user, add them to the appropriate security group, assign an O365 license placeholder, and send a confirmation email body as a string.
Running scripts with unrestricted execution policy
Storing credentials in config files
Sharing admin scripts without access controls
Do This Instead
Use Get-Credential prompts or Azure Key Vault
Store encrypted credentials: Read-Host -AsSecureString | ConvertFrom-SecureString
Set ExecutionPolicy to RemoteSigned at minimum
Place scripts in protected network shares with ACLs
Audit script execution via Windows Event Logs
# Secure credential storage pattern
$credentialPath = "$env:APPDATA\scripts\credentials.enc"
# One-time setup: save encrypted credential
# Read-Host "Enter password" -AsSecureString | ConvertFrom-SecureString | Set-Content $credentialPath
# Daily use: retrieve and use
#$cred = New-Object PSCredential("admin", (Get-Content $credentialPath | ConvertTo-SecureString))
# Use $cred in your script...
Knowledge Check
1. What does the $_.Property syntax represent in a PowerShell pipeline?
A) The first object in the pipeline
B) The current object being processed in the pipeline
C) A variable reference to the pipeline itself
D) An error object from the previous command
Answer: B. $_ is the automatic variable representing the current object in the pipeline during each iteration of Where-Object, ForEach-Object, etc.
2. Which command creates a new Active Directory user account?
A) Add-ADUser
B) New-ADUser
C) Create-ADAccount
D) New-ADSObject
Answer: B) New-ADUser. Part of the ActiveDirectory module. Requires proper permissions and an OU path.
3. How do you prevent a PowerShell script from stopping entirely when a non-terminating error occurs?
A) Use try/catch with -ErrorAction SilentlyContinue
B) Set $ErrorActionPreference to Ignore
C) Use -ErrorAction Stop to convert to terminating errors only
D) Both A and C are correct approaches depending on intent
Answer: D. -ErrorAction SilentlyContinue suppresses non-terminating errors without stopping. Using -ErrorAction Stop converts them to terminating errors so try/catch can handle them gracefully.
4. What is the recommended way to store credentials securely in a scheduled PowerShell script?
A) Hardcode username/password in the script
B) Store in a .txt file next to the script
C) Encrypt with DPAPI using ConvertFrom-SecureString and store the encrypted string
D) Use plain text in an environment variable
Answer: C. DPAPI (Data Protection API) encryption via ConvertFrom-SecureString binds the encrypted credential to the user account and machine, making it readable only by that specific user on that specific machine.
5. Which execution policy allows local scripts to run but requires downloaded scripts to be signed?
A) Restricted
B) AllSigned
C) RemoteSigned
D) Unrestricted
Answer: C) RemoteSigned. This is the default on most systems and is the recommended minimum for production. Restricted blocks all scripts. AllSigned requires every script to be signed. Unrestricted runs everything including unsigned downloaded scripts (dangerous).
I understand PowerShell vs Windows PowerShell vs PowerShell 7
I can declare variables, arrays, and hashtables correctly
I can pipe objects and filter with Where-Object and Select-Object
I can write if/elseif/else and switch statements
I can write for, foreach, and while loops with practical examples
I know the key AD cmdlets for user management
I can perform file system operations and parse event logs
I understand try/catch/finally error handling patterns
I can write functions with parameters and validation
I have created a scheduled task to run a PowerShell script
I know how to safely handle credentials in scripts
I have scored at least 4/5 on the knowledge check quiz
Next Steps
Where to Go From Here
You now have strong PowerShell skills for Windows automation. Next, complete Bash Scripting & IT Service Management to gain the complementary Linux scripting skills every modern IT support professional needs. After finishing Phase 5, you'll be ready for Phase 6: Land Your First Role.