| description | applyTo |
|---|---|
Guidelines for writing PowerShell scripts and modules. |
**/*.ps?(m|d)1 |
- Use descriptive names (3+ characters, no abbreviations)
- Functions: PascalCase with Verb-Noun format using approved verbs
- Parameters: PascalCase
- Variables: camelCase
- Keywords: lower-case
- Classes: PascalCase
- Include scope for script/global/environment variables:
$script:,$global:,$env:
- Class files:
###.ClassName.ps1format (e.g.001.SqlReason.ps1,004.StartupParameters.ps1)
- Use 4 spaces (no tabs)
- One space around operators:
$a = 1 + 2 - One space between type and variable:
[String] $name - One space between keyword and parenthesis:
if ($condition) - No spaces on empty lines
- Try to limit lines to 120 characters
- Newline before opening brace (except variable assignments)
- One newline after opening brace
- Two newlines after closing brace (one if followed by another brace or continuation)
- Use single quotes unless variable expansion is needed:
'text'vs"text $variable"
- Single line:
@('one', 'two', 'three') - Multi-line: each element on separate line with proper indentation
- Do not use the unary comma operator (
,) in return statements to force an array
- Empty:
@{} - Multi-line: each property on separate line with proper indentation
- Properties: Use PascalCase
- Single line:
# Comment(capitalized, on own line) - Multi-line:
<# Comment #>format (opening and closing brackets on own line), and indent text - No commented-out code
- Always add comment-based help to all functions and scripts
- Comment-based help: SYNOPSIS, DESCRIPTION (40+ chars), PARAMETER, EXAMPLE sections before function/class
- Comment-based help indentation: keywords 4 spaces, text 8 spaces
- Include examples for all parameter sets and combinations
- INPUTS: List each pipeline‑accepted type (one per line) with a 1‑line description.
- OUTPUTS: List each return type (one per line) with a 1‑line description. Must match both [OutputType()] and actual returns.
- .NOTES: Include only if it conveys critical info (constraints, side effects, security, version compatibility, breaking behavior). Keep to ≤2 short sentences.
- Avoid aliases (use full command names)
- Avoid
Write-Host(useWrite-Verbose,Write-Information, etc.) - Avoid
Write-Output(usereturninstead) - Avoid
ConvertTo-SecureString -AsPlainTextin production code - Don't redefine reserved parameters (Verbose, Debug, etc.)
- Include a
Forceparameter for functions that uses$PSCmdlet.ShouldContinueor$PSCmdlet.ShouldProcess - For state-changing functions, use
SupportsShouldProcess- Place ShouldProcess check immediately before each state-change
$PSCmdlet.ShouldProcessmust use required pattern
- Use
$PSCmdlet.ThrowTerminatingError()for terminating errors (except for classes), use relevant error category - Use
Write-Errorfor non-terminating errors, use relevant error category - Use
Write-Warningfor warnings - Use
Write-Debugfor debugging information - Use
Write-Verbosefor actionable information - Use
Write-Informationfor informational messages. - Never use backtick as line continuation in production code.
- Ensure
$descriptionMessageexplains what will happen - Ensure
$confirmationMessagesuccinctly asks for confirmation - Keep
$captionMessageshort and descriptive (no trailing.)
$descriptionMessage = $script:localizedData.FunctionName_Action_ShouldProcessDescription -f $param1, $param2
$confirmationMessage = $script:localizedData.FunctionName_Action_ShouldProcessConfirmation -f $param1
$captionMessage = $script:localizedData.FunctionName_Action_ShouldProcessCaption
if ($PSCmdlet.ShouldProcess($descriptionMessage, $confirmationMessage, $captionMessage))
{
# state changing code
}if ($Force.IsPresent -and -not $Confirm)
{
$ConfirmPreference = 'None'
}<#
.SYNOPSIS
Brief description
.DESCRIPTION
Detailed description
.PARAMETER Name
Parameter description
.INPUTS
TypeName
Description
.OUTPUTS
TypeName
Description
#>
function Get-Something
{
[CmdletBinding()]
[OutputType([System.String])]
param
(
[Parameter(Mandatory = $true)]
[System.String]
$Name,
[Parameter()]
[System.String]
$OptionalParam
)
# Implementation
}- Include
[CmdletBinding()]on every function - Parameter block at top
- Parameter block:
param ()if empty, else opening/closing parentheses on own lines [OutputType({return type})]for functions with output, no output use[OutputType()]- All parameters use
[Parameter()]attribute, mandatory parameters use[Parameter(Mandatory = $true)] - Parameter attributes on separate lines
- Parameter type on line above parameter name
- Parameters separated by blank line
- Parameters should use full type name.
- Pipeline parameters (
ValueFromPipeline = $true) must be declared in ALL parameter sets
- Use named parameters in function calls
- Use splatting for long parameter lists
- Limit piping to one pipe per line
- Assign function results to variables rather than inline calls
- Return a single, consistent object type per function
- Use
PSCredentialfor credentials - Avoid hardcoded computer names, use cross-platform
Get-ComputerNameinstead of$env:COMPUTERNAME - Place
$nullon left side of comparisons - Avoid empty catch blocks (instead use
-ErrorAction SilentlyContinue) - Don't use
Invoke-Expression(use&operator) - Use CIM commands instead of WMI commands
- Avoid global variables (exception:
$global:DSCMachineStatus) - Use declared variables more than once
- Avoid unnecessary type declarations when type is clear
- Use full type name when type casting
- No default values for mandatory or switch parameters
- End files with only one blank line
- Use CR+LF line endings
- Maximum two consecutive newlines
- Use UTF-8 encoding (no BOM) for all files
- Don't use
NestedModulesfor shared commands withoutRootModule