logo

Comprehensive PowerShell Commenting Guide

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.
  • .PARAMETERUse 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.

Since 2012, Jonathan Blackwell, an engineer and innovator, has provided engineering leadership that has put Netwrix GroupID at the forefront of group and user management for Active Directory and Azure AD environments. His experience in development, marketing, and sales allows Jonathan to fully understand the Identity market and how buyers think.