Part 5 - A Real Script
So far, you’ve learned how to think in scripts, declare variables, handle logic, structure functions, and manage errors and logging. In this final post, we’ll build a complete script that combines everything.
The script will:
- Accept input from a JSON file
- Validate and process that input
- Create Azure or local resources based on parameters
- Handle errors and log results
- Follow PowerShell best practices throughout
Scenario: Provision Virtual Machines from JSON Input
Let’s say your team provides you with a JSON file like this:
[
{
"Name": "VM-001",
"Location": "westeurope",
"Environment": "Azure"
},
{
"Name": "VM-002",
"Location": "LocalHost",
"Environment": "Local"
}
]
You want to:
- Parse it
- Create a resource group and VM in Azure, or log a local provisioning task
- Log each step and outcome
Full Script
# Provision-VMs.ps1
param (
[Parameter(Mandatory)]
[string]$InputFile,
[string]$LogPath = "$PSScriptRoot\provision.log"
)
function Write-Log {
param (
[Parameter(Mandatory)][string]$Message
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
"$timestamp`t$Message" | Add-Content -Path $LogPath
}
function New-VMFromConfig {
param (
[Parameter(Mandatory)][pscustomobject]$Config
)
try {
$name = $Config.Name
$location = $Config.Location
$environment = $Config.Environment
Write-Log -Message "Starting provisioning for $name in $environment"
switch ($environment) {
'Azure' {
if (-not (Get-AzResourceGroup -Name "RG-$name" -ErrorAction SilentlyContinue)) {
New-AzResourceGroup -Name "RG-$name" -Location $location | Out-Null
Write-Log -Message "Created resource group RG-$name"
}
New-AzVM -Name $name -ResourceGroupName "RG-$name" -Location $location -Image 'Win2022AzureEdition' -Size 'Standard_B2s' -Credential (Get-Credential) -Verbose:$false | Out-Null
Write-Log -Message "Created Azure VM $name in $location"
}
'Local' {
Write-Log -Message "Simulating local VM deployment for $name"
# Here you’d insert logic for Hyper-V or VMware provisioning
}
default {
throw "Unknown environment '$environment' for VM $name"
}
}
Write-Output "Provisioned: $name ($environment)"
}
catch {
Write-Output "Error provisioning $($Config.Name): $_"
Write-Log -Message "ERROR: $($Config.Name) - $_"
}
}
# Validate input
if (-not (Test-Path -Path $InputFile)) {
Write-Output "Input file not found: $InputFile"
exit 1
}
# Process input
$jsonContent = Get-Content -Path $InputFile -Raw | ConvertFrom-Json
foreach ($vmConfig in $jsonContent) {
New-VMFromConfig -Config $vmConfig
}
Write-Output "Provisioning complete."
Write-Log -Message "Provisioning run completed."
How to Run It
- Save the JSON config to
vmconfig.json
- Run the script like this:
.\Provision-VMs.ps1 -InputFile ".\vmconfig.json"
If provisioning Azure VMs, you’ll need:
- The Az PowerShell module
- Logged-in context via
Connect-AzAccount
- Permissions to create resource groups and VMs
Key Concepts Reinforced
- Parameterization via
param()
andMandatory
- Validation with
Test-Path
and structured error throwing - Function encapsulation for
Write-Log
andNew-VMFromConfig
- Logging to file with timestamps
- Error handling with
try/catch
, no silent failures - Clear output with
Write-Output
, neverWrite-Host
Wrapping Up the Series
Congratulations! You’ve now built a fully functional, best-practice PowerShell script that’s:
- Clean
- Reusable
- Maintainable
- Production-ready
From here, you can explore:
- Parameter sets
- Advanced pipeline support
- Class-based modules
- CI/CD integrations (e.g. GitHub Actions, Azure DevOps)
Until then:
Structure your scripts. Handle your errors. Leave a trail of clean logs. And never use select
when you mean Select-Object
.
PowerShell isn’t just a shell. It’s your automation engine.