Introduction
If you write Windows PowerShell scripts, it’s important to understand how to use PowerShell comments effectively. This article can help. It explains the key ways you can include comments in your scripts and provides guidance for when to use each method. It also explains popular use cases for comments and offers best practices to follow and common mistakes to avoid.
Understanding Comments in PowerShell
Comments in PowerShell have a variety of valuable uses. They are essential for making scripts easier to understand and maintain. Using comments to detail the purpose, logic or functionality of a script or a specific section of code is very helpful to any who needs to use, modify or troubleshoot the script. Comments also facilitate collaboration with other team members and reduce the need for verbal explanations. They also help those with less PowerShell expertise follow the script and improve their skill level.
Comments also help development and testing teams temporarily disable one or more lines of code temporarily. Comments are ignored by the PowerShell interpreter, so you can disable code simply by making it into a comment. Since the code was commented out rather than deleted, it can easily be restored later. Detailed comments also prevent confusion during updates and troubleshooting by providing context for why certain decisions were made.
Types of Comments in PowerShell
How to Comment in PowerShell
The two basic types of comments in PowerShell are single-line and multi-line (block) comments.
Single-Line Comments
To create a single-line comment in PowerShell, simply use the hash symbol (#) — any text following the hash symbol will be ignored by the PowerShell interpreter. Here is an example of a single line comment:
# The following script narrow down the users who having logged in for 90 days.
$InactiveUsers = Get-ADUser -Filter {LastLogonDate -lt (Get-Date).AddDays(-90)}
# This is another example of a single-line comment.
Get-Process -Name Notepad
When to Use Single Line Comments
Here are some top use cases for single-line comments:
- Describe the purpose of a line or block of code that follows it, such as a function:
# Calculate the factorial of a number using recursion.
function Get-Factorial {
param ([int]$number)
if ($number -le 1) {
return 1
}
return $number * (Get-Factorial -number ($number - 1))
}
- Document the purpose of a variable:
# Store the list of server names to be checked for connectivity
$servers = @("Server1", "Server2", "Server3")
- Explain why code has been temporarily added or commented out:
# Debugging: Output the current value of the counter
# Write-Output "Counter value is $counter"
Multi-Line (Block) Comments
A PowerShell comment block enables you to add explanatory notes that span multiple lines. Block comments begin with the tag <# and end with the #> tag; all text in between these tags is ignored by the PowerShell interpreter.
Here is an example of a multiline comment in PowerShell that provides detailed information about a script:
<#
This script performs the following tasks:
1. Retrieves the list of all running processes on the system.
2. Filters the processes to include only those consuming more than 100 MB of memory.
3. Outputs the filtered list to the console for review.
Author: Admin
Date: 2025-01-07
#>
# Retrieve all running processes
$processes = Get-Process
# Filter processes using more than 100 MB of memory
$highMemoryProcesses = $processes | Where-Object { $_.WS -gt 100MB }
# Output the filtered processes
$highMemoryProcesses | Format-Table -AutoSize
When to Use Block Comments
PowerShell block comments are ideal when you need to provide long and detailed explanations. While you could start each statement with the hash symbol, using a block comment keeps the script cleaner and more readable.
Here are some top uses for multi line comments:
- Provide metadata about a script:
<#
Script Name: Cleanup-TempFiles.ps1
Author: Jon Mill
Description: This script deletes temporary files older than 30 days
from specified directories to free up disk space.
Last Modified: 2025-01-07
#>
- Explain complex logic or provide context about a section of code:
<#
The following block of code checks the connectivity status of multiple servers.
If a server is unreachable, it logs the error and skips to the next one,
ensuring the script doesn't terminate unexpectedly.
#>
foreach ($server in $servers) {
if (-Not (Test-Connection -ComputerName $server -Count 1 -Quiet)) {
Write-Output "$server is unreachable" >> error.log
continue
}
# Proceed with the next task
}
- Temporarily disable a large section of code during debugging or testing using comment tags:
<#
# Commented out the block below for debugging purposes
Write-Output "Starting debugging session"
$debugVar = $true
#>
Advanced Commenting Techniques
Advanced commenting techniques like inline comments, comment based help and section headers make scripts more professional and user-friendly.
Inline Comments
You can include a comment on the same line as a piece of code by beginning it with the hash symbol. Everything after the # symbol is treated as comment.
Inline comments are best suited for brief explanations of logic or other details to help others quickly understand a key point without interrupting the flow of the script. Here is a simple example:
for ($i = 0; $i -lt 5; $i++) { # Loop from 0 to 4
Write-Output "Iteration $i" }
Comment-Based Help for Scripts and Functions
Comment based help provides a structured way to document scripts inside the code. By using special comment tags in multi line comments, you can specify the information to be provided about the script using the Get-Help cmdlet. For example, the description you provide using the .SYNOPSIS tag will be displayed when a user runs Get-Help on your function or script. This approach yields self-documenting code that reduces the need for separate documentation.
The predefined tags include the following:
- .SYNOPSIS — Use this tag to provide a concise summary of what the script or the function does:
.SYNOPSIS
Copies files from one directory to another.
- .DESCRIPTION — Use this tag to provide a detailed explanation of the script’s functionality and any important considerations for its use:
.DESCRIPTION
This function copies files from a source directory to a destination directory.
It allows users to copy files recursively and optionally overwrite existing files.
- .PARAMETER — Use this tag to define each parameter accepted by the script, including what it does, its type and any specific rules about how it should be used:
.PARAMETER Source
The path of the source directory from which the files will be copied. The source must be a valid directory path.
.PARAMETER Destination
The path to the destination directory where the files will be copied to.
The destination must be a valid directory path.
.PARAMETER Overwrite
A switch to determine whether existing files at the destination should be overwritten.
If not specified, existing files will not be overwritten.
- .EXAMPLE — Use this tag to provide detailed examples of the script or function to help users to understand how to call it and the expected outputs:
.EXAMPLE
Copy-Files -Source "C:\Data" -Destination "D:\Backup" -Overwrite
Copies files from C:\Data to D:\Backup, overwriting existing files at the destination.
Real-World Example of Get-Help Integration
The following script is designed to back up files. The PowerShell script comments at the beginning use the tags detailed above.
<#
.SYNOPSIS
Backs up files from the source to the destination folder.
.DESCRIPTION
This script copies all files from the specified source folder to the destination folder.
.PARAMETER Source
Specifies the path to the source folder.
.PARAMETER Destination
Specifies the path to the destination folder.
.EXAMPLE
.\Backup-Script.ps1 -Source "C:\Data" -Destination "D:\Backup"
Copies all files from C:\Data to D:\Backup.
#>
param (
[string]$Source,
[string]$Destination
)
if (-Not (Test-Path -Path $Source)) {
Write-Error "Source path does not exist."
exit
}
if (-Not (Test-Path -Path $Destination)) {
Write-Host "Destination path does not exist. Creating it..."
New-Item -ItemType Directory -Path $Destination
}
Copy-Item -Path "$Source\*" -Destination $Destination -Recurse
Write-Host "Backup completed successfully."
The screenshot below shows how the tags enable a user to quickly retrieve usage information with the Get-Help
command:
PowerShell Commenting Best Practices
To make code more readable and easier to understand for ourselves and others, it’s vital to use comments effectively. Here are the top best practices to follow.
Add Comments Where Necessary without Cluttering Code
Comments should be used often throughout the code to explain the purpose of functions, the logic behind complex algorithms, the meaning of variables, and any assumptions or limitations of the code.
However, it is important to strike a balance between providing helpful context and avoiding unnecessary clutter. Consider these guidelines:
- Use inline or single line comments for brief notes.
- Use PowerShell multiline comments for longer explanations.
- Avoid providing comments for simple or obvious operations.
- Comment on purpose, not syntax.
- Use comments to clarify logic or intent, especially for complex code.
- Use comments to highlight script assumptions, requirements and known issues.
- Follow formatting and style to keep comments consistent and tidy.
- Add detailed descriptions for functions and scripts using built-in comment-based help.
Here is an example of clear, concise comments that add value without overwhelming the reader:
# Get-Service returns all running services.
Get-Service
# Filter the results to include only services
# related to the World Wide Web.
Get-Service | Where-Object {$_.DisplayName -like "*WWW*"}
# Stop the selected WWW services.
Get-Service | Where-Object {$_.DisplayName -like "*WWW*"} | Stop-Service
Avoid Overuse and Underuse of Comments
Commenting every single line of code can make it difficult to read and understand, especially if the code itself is already self-explanatory. Below is an example of overuse of comments:
# Assign the value 10 to the variable $number
$number = 10
# Add 5 to the variable $number
$number = $number + 5
At the same time, not providing enough comments can make it difficult to understand the logic and purpose of code, especially complex code. For example, the following code is designed to enable all disabled users in Active Directory, but the lack of comments makes it difficult to know that:
$users = Get-ADUser -Filter * | Where-Object {$_.Enabled -eq $false}
$users | ForEach-Object {
Set-ADUser -Identity $_.SamAccountName -Enabled $true
}
Use Temporary Comments for Debugging
Commenting out code is a valuable technique for debugging and troubleshooting issues in code. It can help you isolate issues and understand execution behavior. Be sure to use a comment to explain why the code has been disabled, as illustrated in the following examples:
# Log the value of the Source variable for debugging
Write-Host "Debug: Source path is $Source"
# Temporarily disable file copying to test directory creation
# Copy-Item -Path "$Source\*" -Destination $Destination -Recurse
Use Multiple Comments for Complex Code
To explain complex logic, break down the code into smaller chunks and provide a comment to explain each chunk. Here’s an example:
# Calculate the factorial of a number
function Calculate-Factorial {
param(
[int]$Number
)
# Base case: Factorial of 0 is 1
if ($Number -eq 0) {
return 1
}
# Recursive case: Factorial of N is N * Factorial(N-1)
else {
return $Number * (Calculate-Factorial ($Number - 1))
}
}
Explain Why, Not Just What
Using comments to explain the reasoning underlying a script will enhance code maintainability by providing context for future modifications and debugging efforts.
# Retry the operation up to 3 times to handle intermittent network failures
# This prevents the script from failing on occasional, recoverable issues
for ($i = 0; $i -lt 3; $i++) {
try {
# Attempt the operation
Copy-Item -Path $Source -Destination $Destination
break # Exit the loop if the operation is successful
}
catch {
if ($i -eq 2) { throw "Failed after 3 attempts" }
Start-Sleep -Seconds 2 # Wait before retrying
}
}
Format Comments to Enhance Readability
Effective formatting of comments enhances code readability. Pay close attention to indentation and alignment, as well as the positioning of comments, such as consistently placing them before the code they describe. This code illustrated how good formatting makes comments more effective:
# This function retrieves a list of all running processes.
function Get-RunningProcesses {
# Get all running processes
Get-Process |
# Select only the process name and ID
Select-Object ProcessName, ID
}
Special Use Cases for PowerShell Comments
Using Comments for Version Control
To improve version control, use PowerShell comments to document changes made to the script over time. Use a consistent format that includes details like the date, author and reason for the change. This historical record helps developers understand the context behind each update.
# Version 2.0 - 2025-01-10
# Added a new parameter for logging options and enhanced error handling.
# Updated the file backup logic to support incremental backups.
param (
[string]$Source,
[string]$Destination,
[switch]$EnableLogging # New parameter to enable logging
)
# Check if source exists
if (-Not (Test-Path -Path $Source)) {
Write-Error "Source path does not exist."
exit
}
# Log file creation if logging is enabled
if ($EnableLogging) {
$logPath = "$Destination\backup_log.txt"
"Backup started at $(Get-Date)" | Out-File -Append $logPath
}
# Backup files (incremental logic added in Version 2.0)
if (-Not (Test-Path -Path $Destination)) {
New-Item -ItemType Directory -Path $Destination
}
Copy-Item -Path "$Source\*" -Destination $Destination -Recurse
# Log completion if logging is enabled
if ($EnableLogging) {
"Backup completed at $(Get-Date)" | Out-File -Append $logPath
}
Using Regions to Organize Code
The <#region
and <#endregion
tags can be used to identify logical sections of code. For example, you can tag parts of a script that perform tasks such as data processing, configuration or logging. This approach makes it easier to navigate and understand complex scripts. For instance, developers can collapse sections they are not currently working on to reduce visual clutter and improve focus.
The following script is divided into three regions: Data Import Functions,Data Processing Functions, andOutput Functions. Region comment blocks are used to describe their purpose.
<#region Introduction
This script retrieves a list of all running processes.
It then filters the list to include only processes
that match a specific criterion.
#>
# Get all running processes
$Processes = Get-Process
<#region Filtering
Filter processes based on criteria
(e.g., process name, CPU usage).
#>
$FilteredProcesses = $Processes | Where-Object {$_.ProcessName -eq "notepad"}
<#endregion Filtering
<#region Output
Display the filtered processes.
#>
$FilteredProcesses | Format-List
<#endregion Output
<#endregion Introduction
Troubleshooting and Debugging with PowerShell Comments
Commenting to Isolate Bugs
Comments can be used to insert information for debugging, such as variable values, potential problem areas or troubleshooting steps.
Disabling Code with Comments
We can narrow down the source of errors by temporary commenting out sections of code instead of deleting it.
Documenting Errors with Comments
Comments are a great way to document known issues or troubleshooting tips, especially for code that is susceptible to errors in specific conditions. By adding comments with potential solutions or clarifications, you help both yourself and others quickly resolve problems when they arise.
# Get-Service returns an error on some server versions due to a known bug.
# Workaround: Use WMI to retrieve service status.
try {
Get-Service -Name "SensorService"
} catch {
Get-WmiObject Win32_Service -Filter "Name=''SensorService"
}
PowerShell Commenting for Collaboration
Comments enhance collaboration by making scripts easier to read, understand and maintain. It can also speed onboarding of new contributors.
Documenting Scripts for Team Use
Using comments to explain the purpose of a script, the logic behind each region of code and potential pitfalls improves knowledge sharing among team members and reduces debugging and time.
For example, the block comment at the top of the following scripts outlines its purpose, prerequisites, and parameters:
<#
Script Name: Create-ADUserAccounts.ps1
Description: Automates the creation of Active Directory user accounts from a CSV file.
Prerequisites:
- Active Directory module installed.
- A valid CSV file with columns: FirstName, LastName, UserName, and Email.
Parameters:
-CSVPath: Path to the input CSV file.
-OU: Organizational Unit where accounts will be created.
Usage:
.\Create-ADUserAccounts.ps1 -CSVPath "C:\Users.csv" -OU "OU=Users,DC=Domain,DC=Com"
#>
Sharing Knowledge Through Comments
Comments help all team members understand the logic behind a script, the purpose of each region, why specific approaches were chosen and potential challenges. This shared understanding facilitates troubleshooting, debugging and future enhancements. For instance, if a script includes a workaround for a known software limitation, comments can document the issue and justify the solution so others do not have to duplicate the research.
Here is an example of how to use comments effectively to share information about a script for restarting a service if it is not running:
<#
Script Name: Ensure-ServiceRunning.ps1
Description: Checks if a specified service is running and restarts it if necessary.
Purpose:
- Ensures critical services stay operational without manual intervention.
Decisions:
- Used “Get-Service” for simplicity and compatibility.
- Restart logic avoids redundancy by checking the current status first.
Usage:
.\Ensure-ServiceRunning.ps1 -ServiceName "Spooler"
#>
param (
[Parameter(Mandatory)]
[string]$ServiceName # Name of the service to check
)
# Check the current status of the service
$service = Get-Service -Name $ServiceName -ErrorAction Stop
if ($service.Status -ne "Running") {
# Log and attempt to restart the service if not running
Write-Host "Service '$ServiceName' is not running. Attempting to restart..."
try {
Restart-Service -Name $ServiceName -Force
Write-Host "Service '$ServiceName' has been restarted successfully."
} catch {
Write-Error "Failed to restart the service '$ServiceName': $_"
}
} else {
Write-Host "Service '$ServiceName' is already running."
}
Examples and Scenarios of PowerShell Commenting
The following script monitors disk usage and send an email alert if free space is low:
<#
Script Name: Monitor-DiskUsage.ps1
Description: Checks each logical drive and sends an alert if free space is below the defined threshold.
Purpose:
- Prevents system issues caused by insufficient storage.
Usage:
.\Monitor-DiskUsage.ps1 -Threshold 10 -Email "admin@Netwrix.com"
#>
param (
[Parameter(Mandatory)]
[int]$Threshold, # Minimum free space in GB to trigger an alert
[Parameter(Mandatory)]
[string]$Email # Email address for the alert
)
# Retrieve disk information
$drives = Get-PSDrive -PSProvider FileSystem
foreach ($drive in $drives) {
if ($drive.Free -gt 0) {
$freeSpaceGB = [math]::Round($drive.Free / 1GB, 2)
if ($freeSpaceGB -lt $Threshold) {
Write-Warning "Drive $($drive.Name) has low space: $freeSpaceGB GB remaining."
# Send an alert email (replace with real SMTP details)
try {
Send-MailMessage -From "alerts@netwrix.com" -To $Email -Subject "Low Disk Space Alert" `
-Body "Drive $($drive.Name) has only $freeSpaceGB GB remaining." `
-SmtpServer "smtp.domain.com"
} catch {
Write-Error "Failed to send alert email: $_"
}
}
}
}
The script below cleans up user accounts in Active Directory by disabling inactive accounts and moving them to a particular OU:
<#
Script Name: Cleanup-ADUsers.ps1
Description: Disables and moves inactive user accounts to a specified OU.
Purpose:
- Helps maintain a clean and secure Active Directory environment.
Usage:
.\Cleanup-ADUsers.ps1 -OU "OU=Inactive,DC=Netwrix,DC=Com" -DaysInactive 90
#>
param (
[Parameter(Mandatory)]
[string]$OU, # Target OU for inactive users
[Parameter(Mandatory)]
[int]$DaysInactive # Number of days since last logon
)
# Get the current date and calculate the inactivity threshold
$thresholdDate = (Get-Date).AddDays(-$DaysInactive)
# Find inactive user accounts
$inactiveUsers = Get-ADUser -Filter {LastLogonDate -lt $thresholdDate -and Enabled -eq $true}
foreach ($user in $inactiveUsers) {
Write-Host "Disabling and moving user: $($user.SamAccountName)"
# Disable the account
Disable-ADAccount -Identity $user.SamAccountName
# Move the account to the specified OU
Move-ADObject -Identity $user.DistinguishedName -TargetPath $OU
}
Common Mistakes in Commenting
Some comments in scripts add more confusion than clarity. Here are some of the most frequent mistakes when using PowerShell comments and how to avoid them.
Vague or Obvious Comments
One of the most common mistakes is writing comments that are too vague or obvious, such as this one:
# Set the variable to 5
$number = 5
Instead, focus on explaining why a particular decision was made or why a complex operation is being performed, especially if the code could be confusing or there are multiple possible approaches.
# Assign the default retry count to handle intermittent failures
$retryCount = 5
Inaccurate or Outdated Comments
Sometimes, comments are not updated when the code changes, leading to inaccurate or misleading information. For example, here the backup path was changed but the comment wasn’t updated, which can mislead the reader:
# This script backs up data to a network drive
Backup-Data -Path "C:\Data"
Make sure that comments are updated whenever the code changes. Regularly review and refactor your comments as part of your code maintenance to make sure they stay relevant.
Overuse of Comments
Some code is filled with unnecessary comments for every line or simple operation.
# Initialize the variable
$number = 10
# Increment the variable by 1
$number++
Add comments only where they add value, typically for complex logic, unusual solutions or decisions that might not be immediately obvious to another developer.
Not Using Comments to Explain Complex Code
If your script contains a section of code that is complex or non-intuitive, be sure to explain it. For instance, without a comment, it is unclear why these specific calculations are being made or what 1.15 represents:
$finalPrice = $basePrice * (1 - $discountPercentage) * 1.15
For complex or cryptic code, provide context on why certain formulas, calculations or algorithms are used.
# Calculate the final price including a 15% tax and applying the discount
$finalPrice = $basePrice * (1 - $discountPercentage) * 1.15
Commenting Out Code Instead of Removing It
Developers sometimes leave commented-out code that is left over from testing or is otherwise no longer needed. This clutters up the script and cause confusion about whether the code should ever be reactivated.
# $oldValue = Get-Item "C:\OldFile.txt"
# $oldValue.Delete()
Remove dead code from the script instead of commenting it out. If you need to keep code for future reference, clearly document that purpose.
Conclusion
Comments are critical for professional and maintainable PowerShell scripts. Used correctly, they make code easier to understand, debug, troubleshoot, maintain and enhance. Effective commenting includes documenting the script’s purpose, decisions, assumptions and potential issues. Developers should make sure to neither overuse nor underuse comments, and to adopt formatting standards to ensure readability.
Effective commenting is not a one-time task but an ongoing process. Reviewing and updating comments whenever the code is modified helps ensure the comments accurately reflect the current state of the script, preventing confusion and errors.
FAQ
Can comments impact the performance of PowerShell scripts?
Comments are not processed by the PowerShell interpreter during execution, so they do not impact the performance of scripts at runtime. Nevertheless, it is a good practice to keep comments concise and relevant so they provide the most value.
How often should I update comments in my scripts?
Comments should be reviewed and updated whenever significant changes are made to the code, such as when new features are added, existing functionality is modified or bugs are fixed. Comments should always reflect the current state of the script.
How can I comment out multiple lines quickly in PowerShell?
You can quickly comment out multiple lines in PowerShell using the block comment syntax. Simply enclose the lines of code between <#
at the beginning and#>
at the end.
What’s the difference between inline comments and block comments?
Inline comments start with the # symbol and appear on the same line as the code they refer to. They are best suited for brief explanations.
Block comments can span multiple lines, enclosed by the delimiters <# and #>. They are particularly useful for providing detailed explanations, documenting complex logic or temporarily disabling a section of code.