A param block declares the inputs a PowerShell function or script accepts. Each parameter can state whether a value is mandatory and what type of data it expects, making required inputs visible to callers and allowing PowerShell to help reject unsuitable values before automation proceeds. section 2.1.1
Place the param block near the start of a function, after CmdletBinding and OutputType when those attributes are used. Scripts likewise begin with comment-based help, CmdletBinding, OutputType, and a parameter block; required module imports follow the parameters. section 2.1.1 section 2.1.7
Use a param block to make an automation interface explicit:
- Mark inputs as mandatory when the automation cannot safely proceed without them, such as an archive path or filename prefix. section 2.1.1
- Declare input types such as string, datetime, and double so callers know the expected value shape and PowerShell can help prevent wrong values. section 2.1.1 section 2.1.7
- Provide a default for an optional setting when a useful operational choice exists, such as retaining files for 30 days. section 2.1.7
- Keep the block separate from the automation logic so reusable functions can be added or replaced without changing unrelated steps. section 2.1.7
Log archive inputs
A cleanup script exposes LogPath, ZipPath, and ZipPrefix as mandatory string parameters. NumberOfDays is an optional double with a default of 30. The script can then calculate a date cutoff, collect older log files, build an archive path, compress the files, and remove them only after testing the operation. section 2.1.7
Listing 2.3 Putting it all together
[CmdletBinding()]
[OutputType()]
param(
[Parameter(Mandatory = $true)]
[string]$LogPath,
[Parameter(Mandatory = $true)]
[string]$ZipPath,
[Parameter(Mandatory = $true)]
[string]$ZipPrefix,
[Parameter(Mandatory = $false)]
[double]$NumberOfDays = 30
)
Function Set-ArchiveFilePath{ #1
[CmdletBinding()]
[OutputType([string])]
param(
[Parameter(Mandatory = $true)]
[string]$ZipPath,
[Parameter(Mandatory = $true)]
[string]$ZipPrefix,
[Parameter(Mandatory = $false)]
[datetime]$Date = (Get-Date)
)
if(-not (Test-Path -Path $ZipPath)){
New-Item -Path $ZipPath -ItemType Directory | Out-Null
Write-Verbose "Created folder '$ZipPath'"
}
$ZipName = "$($ZipPrefix)$($Date.ToString('yyyyMMdd')).zip"
$ZipFile = Join-Path $ZipPath $ZipName
if(Test-Path -Path $ZipFile){
throw "The file '$ZipFile' already exists"
}
$ZipFile
}
Function Remove-ArchivedFiles {
[CmdletBinding()]
[OutputType()]
param(
[Parameter(Mandatory = $true)]
[string]$ZipFile,
[Parameter(Mandatory = $true)]
[object]$FilesToDelete,
[Parameter(Mandatory = $false)]
[switch]$WhatIf = $false
)
$AssemblyName = 'System.IO.Compression.FileSystem'
Add-Type -AssemblyName $AssemblyName | Out-Null
$OpenZip = [System.IO.Compression.ZipFile]::OpenRead($ZipFile)
$ZipFileEntries = $OpenZip.Entries
foreach($file in $FilesToDelete){
$check = $ZipFileEntries | Where-Object{ $_.Name -eq $file.Name -and
$_.Length -eq $file.Length }
if($null -ne $check){
$file | Remove-Item -Force -WhatIf:$WhatIf
}
else {
Write-Error "'$($file.Name)' was not find in '$($ZipFile)'"
}
}
}
$Date = (Get-Date).AddDays(-$NumberOfDays) #2
$files = Get-ChildItem -Path $LogPath -File | #3
Where-Object{ $_.LastWriteTime -lt $Date}
$ZipParameters = @{
ZipPath = $ZipPath
ZipPrefix = $ZipPrefix
Date = $Date
}
$ZipFile = Set-ArchiveFilePath @ZipParameters #4
$files | Compress-Archive -DestinationPath $ZipFile #5
$RemoveFiles = @{
ZipFile = $ZipFile
FilesToDelete = $files
}
Remove-ArchivedFiles @RemoveFiles #6
The complete cleanup script shows a top-level param block with required paths and prefix, plus an optional NumberOfDays default, before its reusable functions and archive logic. section 2.1.7
Use the same pattern inside a reusable function. A function that creates an archive filename can require a zip path, prefix, and date; its typed parameter declarations document the contract before the function checks folders and constructs the filename. section 2.1.1

