Automation
Turn your one-off commands into real, reusable .ps1 scripts that handle errors gracefully and run themselves on a schedule — exactly how working sysadmins automate the boring stuff.
Part of the free Powershell course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
1. From Commands to a Script File
A script is simply the commands you've been typing, saved into a file ending in .ps1 . You run it by name. Adding a param() block at the very top turns it from a one-trick file into a reusable tool: the caller passes values in, so the same script works for many situations. Read this, save it as save-me.ps1 , and run it.
2. The Execution Policy
The first time you try to run a script, Windows often refuses with "running scripts is disabled on this system." That's the execution policy — a safety gate so a downloaded file can't run code behind your back. The fix is to set it to RemoteSigned : your own local scripts run freely, but anything downloaded from the internet must be digitally signed first.
3. Handling Errors with try/catch/finally
A scheduled script runs with no human watching, so it must cope with failure. Wrap risky work in try { … } ; if it throws, the matching catch { … } runs your recovery code; finally { … } always runs (great for cleanup). The catch is the gotcha: most cmdlet errors don't throw by default — they just print red text and the script keeps going. Add -ErrorAction Stop (or set $ErrorActionPreference = 'Stop' ) to make them throw so catch can actually fire.
Your turn. Fill in the two blanks marked ___ so this script catches a missing file instead of crashing, then run it.
4. Modules — Reusing Other People's Code
A module is a packaged set of cmdlets. Install-Module downloads one from the PowerShell Gallery (once per machine); Import-Module loads it so you can use its commands in the current session. This is how you get powerful tools — like PSScriptAnalyzer , which lints your scripts for mistakes — without writing them yourself.
5. Scheduling a Script to Run Itself
The payoff: tell Windows to run your script on a timer. A scheduled task is built from three parts — an Action (what to run), a Trigger (when), and Settings (how) — then registered with Register-ScheduledTask . On Linux or macOS the same job is a one-line cron entry ( 0 2 * * * pwsh -File … ); the script is identical, only the scheduler changes.
Putting It Together: a Log-Cleanup Job
Here's a complete, schedulable script that uses everything from this lesson: param() inputs, $ErrorActionPreference = 'Stop' , a pipeline to find old files, try/catch , and a -WhatIf dry-run switch so you can preview before deleting. Read it line by line — you understand every part now.
Why -WhatIf ? Anything destructive ( Remove-Item , Copy-Item ) supports it. Passing -WhatIf:$WhatIf flows your switch straight through, so one script does both a safe preview and the real run.
Pro Tips
- 💡 Always log scheduled runs to a file. A task has no visible console — Add-Content $logFile $msg is how you find out what happened at 2am.
- 💡 Test with -WhatIf before anything destructive. It shows what would happen without doing it.
- 💡 Put $ErrorActionPreference = 'Stop' at the top of scripts so a stray non-terminating error can't sail past your try/catch .
- 💡 Never hard-code paths. Take them as param() inputs (or use $env:TEMP , Join-Path ) so the script works on any machine.
Common Errors (and the fix)
- "running scripts is disabled on this system" — the execution policy is blocking you. Run Set-ExecutionPolicy RemoteSigned -Scope CurrentUser , then try again.
- Your catch never runs and the script keeps going — the error was non-terminating. Add -ErrorAction Stop to the failing cmdlet, or set $ErrorActionPreference = 'Stop' at the top.
- "cannot find path … because it does not exist" — a hard-coded path is wrong on this machine. Pass paths as param() inputs and build them with Join-Path instead of typing C:\Logs directly.
- Scheduled task shows "Ready" but never seems to do anything — it has no console, so its output vanished. Log to a file from inside the script and check that file.
- "The term 'Invoke-ScriptAnalyzer' is not recognized" — the module isn't loaded. Run Install-Module PSScriptAnalyzer -Scope CurrentUser then Import-Module PSScriptAnalyzer .
📋 Quick Reference
Task
Code
Allow scripts to run
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Make errors catchable
-ErrorAction Stop / $ErrorActionPreference = 'Stop'
Handle a failure
try { … } catch { $_ } finally { … }
Install a module
Install-Module Name -Scope CurrentUser
Load a module
Import-Module Name
Schedule (Windows)
Register-ScheduledTask -TaskName … -Action … -Trigger …
Schedule (Linux/macOS)
crontab -e → 0 2 * * * pwsh -File ./job.ps1
Frequently Asked Questions
Mini-Challenge: Backup Script
No blanks this time — just a brief and an outline. Write backup.ps1 yourself, test it with a real folder, then schedule it. This is the exact kind of script that runs in production every night.
🎉 Course Complete!
That's the whole PowerShell course. You can now:
- ✅ Save commands into reusable .ps1 scripts with param() inputs
- ✅ Unblock scripts safely with Set-ExecutionPolicy RemoteSigned
- ✅ Survive failures with try/catch/finally and -ErrorAction Stop
- ✅ Pull in extra power with Install-Module / Import-Module
- ✅ Run scripts on a timer with Register-ScheduledTask (or cron)
- ✅ Ship a real end-to-end automation, from dry-run to nightly schedule
Where next? Put it to work: automate a chore on your own machine (backups, log cleanup, a report). Then deepen your toolkit with the Git course to version-control your scripts, or the Python course for cross-platform scripting. Keep automating the boring stuff — that's the whole job.
Practice quiz
What file extension does a PowerShell script use?
- .ps1
- .psh
- .pow
- .sh
Answer: .ps1. PowerShell scripts are saved in files ending in .ps1.
Where must the param() block appear in a script?
- At the very end
- As the first code in the file
- Inside a function only
- Anywhere
Answer: As the first code in the file. param() must be the first executable code in a script file.
Which execution policy lets your local scripts run but gates downloaded ones?
- Restricted
- Bypass
- RemoteSigned
- Unrestricted
Answer: RemoteSigned. RemoteSigned runs your local scripts and requires downloaded ones to be signed.
Which block always runs, whether or not an error occurred?
- try
- catch
- trap
- finally
Answer: finally. The finally block always runs, making it ideal for cleanup.
How do you make a cmdlet error catchable by try/catch?
- Add -ErrorAction Stop
- Add -Catchable
- Use return
- Use Write-Error
Answer: Add -ErrorAction Stop. -ErrorAction Stop turns a non-terminating error into a catchable exception.
Which cmdlet downloads a module from the PowerShell Gallery?
- Import-Module
- Install-Module
- Get-Module
- Add-Module
Answer: Install-Module. Install-Module downloads and installs a module (once per machine).
Which cmdlet loads an installed module into the current session?
- Get-Module
- Install-Module
- Import-Module
- Use-Module
Answer: Import-Module. Import-Module loads a module's commands into the current session.
Which cmdlet registers a scheduled task on Windows?
- New-CronJob
- Add-Schedule
- Set-Timer
- Register-ScheduledTask
Answer: Register-ScheduledTask. Register-ScheduledTask creates a task from an action, trigger, and settings.
What is the cross-platform equivalent of a scheduled task on Linux/macOS?
- cron
- systemd-only
- Task Manager
- launchpad
Answer: cron. On Linux and macOS you use cron, e.g. a crontab entry, to schedule scripts.
Inside a catch block, how do you read the error message?
- $error.text
- $_.Exception.Message
- $catch.msg
- $LastError
Answer: $_.Exception.Message. $_ holds the thrown error; $_.Exception.Message is its text.