mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-11 02:35:55 +00:00
* Update powershell.instructions.md ## Description ### Error Handling - Update to the `powershell.instructions.md` file. Now includes less error handling in the examples. This means when using the instructions file the output script contains less of the structured error handling, however the output scripts are easier for beginners and powershell novices to read and understand. ### Switch parameter - Updates to using the switch parameters should now prevent default values data type being a bool - Using no default value is the way in PowerShell. Defaults to a false value and shouldn't be set to a true value. Although a note has been added to show the correct syntax that requires type casting ### Examples updates - Now includes a better demonstration of using the `WhatIf` parameter via `$PSCmdlet.ShouldProcesss` - Full Example: End-to-End Cmdlet Pattern updated with the `$Force` & `$PSCmdlet.ShouldContinue` pattern * Update instructions/powershell.instructions.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update instructions/powershell.instructions.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update instructions/powershell.instructions.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ShouldProcess and ShouldContinue guidance Clarified ShouldProcess and ShouldContinue usage in PowerShell instructions. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
11 KiB
11 KiB
applyTo, description
| applyTo | description |
|---|---|
| **/*.ps1,**/*.psm1 | PowerShell cmdlet and scripting best practices based on Microsoft guidelines |
PowerShell Cmdlet Development Guidelines
This guide provides PowerShell-specific instructions to help GitHub Copilot generate idiomatic, safe, and maintainable scripts. It aligns with Microsoft’s PowerShell cmdlet development guidelines.
Naming Conventions
-
Verb-Noun Format:
- Use approved PowerShell verbs (Get-Verb)
- Use singular nouns
- PascalCase for both verb and noun
- Avoid special characters and spaces
-
Parameter Names:
- Use PascalCase
- Choose clear, descriptive names
- Use singular form unless always multiple
- Follow PowerShell standard names
-
Variable Names:
- Use PascalCase for public variables
- Use camelCase for private variables
- Avoid abbreviations
- Use meaningful names
-
Alias Avoidance:
- Use full cmdlet names
- Avoid using aliases in scripts (e.g., use
Get-ChildIteminstead ofgci) - Document any custom aliases
- Use full parameter names
Example - Naming Conventions
function Get-UserProfile {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Username,
[Parameter()]
[ValidateSet('Basic', 'Detailed')]
[string]$ProfileType = 'Basic'
)
process {
$outputString = "Searching for: '$($Username)'"
Write-Verbose -Message $outputString
Write-Verbose -Message "Profile type: $ProfileType"
# Logic here
}
}
Parameter Design
-
Standard Parameters:
- Use common parameter names (
Path,Name,Force) - Follow built-in cmdlet conventions
- Use aliases for specialized terms
- Document parameter purpose
- Use common parameter names (
-
Parameter Names:
- Use singular form unless always multiple
- Choose clear, descriptive names
- Follow PowerShell conventions
- Use PascalCase formatting
-
Type Selection:
- Use common .NET types
- Implement proper validation
- Consider ValidateSet for limited options
- Enable tab completion where possible
-
Switch Parameters:
- ALWAYS use
[switch]for boolean flags, never[bool] - NEVER use
[bool]$Parameteror assign default values - Switch parameters default to
$falsewhen omitted - Use clear, action-oriented names
- Test presence with
.IsPresent - Using
$true/$falsein parameter attributes (e.g.,Mandatory = $true) is acceptable
- ALWAYS use
Example - Parameter Design
function Set-ResourceConfiguration {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Name,
[Parameter()]
[ValidateSet('Dev', 'Test', 'Prod')]
[string]$Environment = 'Dev',
# ✔️ CORRECT: Use `[switch]` with no default value
[Parameter()]
[switch]$Force,
# ❌ WRONG: Shows incorrect default assignment, however this is correct syntax (requires `[switch]` cast).
[Parameter()]
[switch]$Quiet = [switch]$true,
[Parameter()]
[ValidateNotNullOrEmpty()]
[string[]]$Tags
)
process {
# Use .IsPresent to check switch state
if ($Quiet.IsPresent) {
Write-Verbose "Quiet mode enabled"
}
}
}
Pipeline and Output
-
Pipeline Input:
- Use
ValueFromPipelinefor direct object input - Use
ValueFromPipelineByPropertyNamefor property mapping - Implement Begin/Process/End blocks for pipeline handling
- Document pipeline input requirements
- Use
-
Output Objects:
- Return rich objects, not formatted text
- Use PSCustomObject for structured data
- Avoid Write-Host for data output
- Enable downstream cmdlet processing
-
Pipeline Streaming:
- Output one object at a time
- Use process block for streaming
- Avoid collecting large arrays
- Enable immediate processing
-
PassThru Pattern:
- Default to no output for action cmdlets
- Implement
-PassThruswitch for object return - Return modified/created object with
-PassThru - Use verbose/warning for status updates
Example - Pipeline and Output
function Update-ResourceStatus {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string]$Name,
[Parameter(Mandatory)]
[ValidateSet('Active', 'Inactive', 'Maintenance')]
[string]$Status,
[Parameter()]
[switch]$PassThru
)
begin {
Write-Verbose 'Starting resource status update process'
$timestamp = Get-Date
}
process {
# Process each resource individually
Write-Verbose "Processing resource: $Name"
$resource = [PSCustomObject]@{
Name = $Name
Status = $Status
LastUpdated = $timestamp
UpdatedBy = "$($env:USERNAME)"
}
# Only output if PassThru is specified
if ($PassThru.IsPresent) {
Write-Output $resource
}
}
end {
Write-Verbose 'Resource status update process completed'
}
}
Error Handling and Safety
-
ShouldProcess Implementation:
- Use
[CmdletBinding(SupportsShouldProcess = $true)] - Set appropriate
ConfirmImpactlevel - Call
$PSCmdlet.ShouldProcess()as close the the changes action - Use
$PSCmdlet.ShouldContinue()for additional confirmations
- Use
-
Message Streams:
Write-Verbosefor operational details with-VerboseWrite-Warningfor warning conditionsWrite-Errorfor non-terminating errorsthrowfor terminating errors- Avoid
Write-Hostexcept for user interface text
-
Error Handling Pattern:
- Use try/catch blocks for error management
- Set appropriate ErrorAction preferences
- Return meaningful error messages
- Use ErrorVariable when needed
- Include proper terminating vs non-terminating error handling
- In advanced functions with
[CmdletBinding()], prefer$PSCmdlet.WriteError()overWrite-Error - In advanced functions with
[CmdletBinding()], prefer$PSCmdlet.ThrowTerminatingError()overthrow - Construct proper ErrorRecord objects with category, target, and exception details
-
Non-Interactive Design:
- Accept input via parameters
- Avoid
Read-Hostin scripts - Support automation scenarios
- Document all required inputs
Example - Error Handling and Safety
function Remove-CacheFiles {
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory)]
[string]$Path
)
try {
$files = Get-ChildItem -Path $Path -Filter "*.cache" -ErrorAction Stop
# Demonstrates WhatIf support
if ($PSCmdlet.ShouldProcess($Path, 'Remove cache files')) {
$files | Remove-Item -Force -ErrorAction Stop
Write-Verbose "Removed $($files.Count) cache files from $Path"
}
} catch {
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
$_.Exception,
'RemovalFailed',
[System.Management.Automation.ErrorCategory]::NotSpecified,
$Path
)
$PSCmdlet.WriteError($errorRecord)
}
}
Documentation and Style
-
Comment-Based Help: Include comment-based help for any public-facing function or cmdlet. Inside the function, add a
<# ... #>help comment with at least:.SYNOPSISBrief description.DESCRIPTIONDetailed explanation.EXAMPLEsections with practical usage.PARAMETERdescriptions.OUTPUTSType of output returned.NOTESAdditional information
-
Consistent Formatting:
- Follow consistent PowerShell style
- Use proper indentation (4 spaces recommended)
- Opening braces on same line as statement
- Closing braces on new line
- Use line breaks after pipeline operators
- PascalCase for function and parameter names
- Avoid unnecessary whitespace
-
Pipeline Support:
- Implement Begin/Process/End blocks for pipeline functions
- Use ValueFromPipeline where appropriate
- Support pipeline input by property name
- Return proper objects, not formatted text
-
Avoid Aliases: Use full cmdlet names and parameters
- Avoid using aliases in scripts (e.g., use Get-ChildItem instead of gci); aliases are acceptable for interactive shell use.
- Use
Where-Objectinstead of?orwhere - Use
ForEach-Objectinstead of% - Use
Get-ChildIteminstead oflsordir
Full Example: End-to-End Cmdlet Pattern
function Remove-UserAccount {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string]$Username,
[Parameter()]
[switch]$Force
)
begin {
Write-Verbose 'Starting user account removal process'
$currentErrorActionValue = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
}
process {
try {
# Validation
if (-not (Test-UserExists -Username $Username)) {
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
[System.Exception]::new("User account '$Username' not found"),
'UserNotFound',
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
$Username
)
$PSCmdlet.WriteError($errorRecord)
return
}
# ShouldProcess enables -WhatIf and -Confirm support
if ($PSCmdlet.ShouldProcess($Username, "Remove user account")) {
# ShouldContinue provides an additional confirmation prompt for high-impact operations
# This prompt is bypassed when -Force is specified
if ($Force -or $PSCmdlet.ShouldContinue("Are you sure you want to remove '$Username'?", "Confirm Removal")) {
Write-Verbose "Removing user account: $Username"
# Main operation
Remove-ADUser -Identity $Username -ErrorAction Stop
Write-Warning "User account '$Username' has been removed"
}
}
} catch [Microsoft.ActiveDirectory.Management.ADException] {
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
$_.Exception,
'ActiveDirectoryError',
[System.Management.Automation.ErrorCategory]::NotSpecified,
$Username
)
$PSCmdlet.ThrowTerminatingError($errorRecord)
} catch {
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
$_.Exception,
'UnexpectedError',
[System.Management.Automation.ErrorCategory]::NotSpecified,
$Username
)
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
}
end {
Write-Verbose 'User account removal process completed'
# Set ErrorActionPreference back to the value it had
$ErrorActionPreference = $currentErrorActionValue
}
}