mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-11 02:35:55 +00:00
Update powershell.instructions.md (#1125)
* 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>
This commit is contained in:
@@ -30,11 +30,11 @@ safe, and maintainable scripts. It aligns with Microsoft’s PowerShell cmdlet d
|
||||
|
||||
- **Alias Avoidance:**
|
||||
- Use full cmdlet names
|
||||
- Avoid using aliases in scripts (e.g., use Get-ChildItem instead of gci)
|
||||
- Avoid using aliases in scripts (e.g., use `Get-ChildItem` instead of `gci`)
|
||||
- Document any custom aliases
|
||||
- Use full parameter names
|
||||
|
||||
### Example
|
||||
### Example - Naming Conventions
|
||||
|
||||
```powershell
|
||||
function Get-UserProfile {
|
||||
@@ -49,6 +49,9 @@ function Get-UserProfile {
|
||||
)
|
||||
|
||||
process {
|
||||
$outputString = "Searching for: '$($Username)'"
|
||||
Write-Verbose -Message $outputString
|
||||
Write-Verbose -Message "Profile type: $ProfileType"
|
||||
# Logic here
|
||||
}
|
||||
}
|
||||
@@ -75,12 +78,14 @@ function Get-UserProfile {
|
||||
- Enable tab completion where possible
|
||||
|
||||
- **Switch Parameters:**
|
||||
- Use [switch] for boolean flags
|
||||
- Avoid $true/$false parameters
|
||||
- Default to $false when omitted
|
||||
- Use clear action names
|
||||
- **ALWAYS** use `[switch]` for boolean flags, never `[bool]`
|
||||
- **NEVER** use `[bool]$Parameter` or assign default values
|
||||
- Switch parameters default to `$false` when omitted
|
||||
- Use clear, action-oriented names
|
||||
- Test presence with `.IsPresent`
|
||||
- Using `$true`/`$false` in parameter attributes (e.g., `Mandatory = $true`) is acceptable
|
||||
|
||||
### Example
|
||||
### Example - Parameter Design
|
||||
|
||||
```powershell
|
||||
function Set-ResourceConfiguration {
|
||||
@@ -93,16 +98,24 @@ function Set-ResourceConfiguration {
|
||||
[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 {
|
||||
# Logic here
|
||||
# Use .IsPresent to check switch state
|
||||
if ($Quiet.IsPresent) {
|
||||
Write-Verbose "Quiet mode enabled"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -133,7 +146,7 @@ function Set-ResourceConfiguration {
|
||||
- Return modified/created object with `-PassThru`
|
||||
- Use verbose/warning for status updates
|
||||
|
||||
### Example
|
||||
### Example - Pipeline and Output
|
||||
|
||||
```powershell
|
||||
function Update-ResourceStatus {
|
||||
@@ -163,7 +176,7 @@ function Update-ResourceStatus {
|
||||
Name = $Name
|
||||
Status = $Status
|
||||
LastUpdated = $timestamp
|
||||
UpdatedBy = $env:USERNAME
|
||||
UpdatedBy = "$($env:USERNAME)"
|
||||
}
|
||||
|
||||
# Only output if PassThru is specified
|
||||
@@ -183,8 +196,8 @@ function Update-ResourceStatus {
|
||||
- **ShouldProcess Implementation:**
|
||||
- Use `[CmdletBinding(SupportsShouldProcess = $true)]`
|
||||
- Set appropriate `ConfirmImpact` level
|
||||
- Call `$PSCmdlet.ShouldProcess()` for system changes
|
||||
- Use `ShouldContinue()` for additional confirmations
|
||||
- Call `$PSCmdlet.ShouldProcess()` as close the the changes action
|
||||
- Use `$PSCmdlet.ShouldContinue()` for additional confirmations
|
||||
|
||||
- **Message Streams:**
|
||||
- `Write-Verbose` for operational details with `-Verbose`
|
||||
@@ -209,69 +222,32 @@ function Update-ResourceStatus {
|
||||
- Support automation scenarios
|
||||
- Document all required inputs
|
||||
|
||||
### Example
|
||||
### Example - Error Handling and Safety
|
||||
|
||||
```powershell
|
||||
function Remove-UserAccount {
|
||||
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
|
||||
function Remove-CacheFiles {
|
||||
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
|
||||
param(
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$Username,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$Force
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
begin {
|
||||
Write-Verbose 'Starting user account removal process'
|
||||
$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
|
||||
}
|
||||
|
||||
# Confirmation
|
||||
$shouldProcessMessage = "Remove user account '$Username'"
|
||||
if ($Force -or $PSCmdlet.ShouldProcess($Username, $shouldProcessMessage)) {
|
||||
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)
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
Write-Verbose 'User account removal process completed'
|
||||
} catch {
|
||||
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
|
||||
$_.Exception,
|
||||
'RemovalFailed',
|
||||
[System.Management.Automation.ErrorCategory]::NotSpecified,
|
||||
$Path
|
||||
)
|
||||
$PSCmdlet.WriteError($errorRecord)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -307,50 +283,77 @@ function Remove-UserAccount {
|
||||
- Use `ForEach-Object` instead of `%`
|
||||
- Use `Get-ChildItem` instead of `ls` or `dir`
|
||||
|
||||
---
|
||||
|
||||
## Full Example: End-to-End Cmdlet Pattern
|
||||
|
||||
```powershell
|
||||
function New-Resource {
|
||||
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
|
||||
function Remove-UserAccount {
|
||||
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $true)]
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$Name,
|
||||
[string]$Username,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateSet('Development', 'Production')]
|
||||
[string]$Environment = 'Development'
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
begin {
|
||||
Write-Verbose 'Starting resource creation process'
|
||||
Write-Verbose 'Starting user account removal process'
|
||||
$currentErrorActionValue = $ErrorActionPreference
|
||||
$ErrorActionPreference = 'Stop'
|
||||
}
|
||||
|
||||
process {
|
||||
try {
|
||||
if ($PSCmdlet.ShouldProcess($Name, 'Create new resource')) {
|
||||
# Resource creation logic here
|
||||
Write-Output ([PSCustomObject]@{
|
||||
Name = $Name
|
||||
Environment = $Environment
|
||||
Created = Get-Date
|
||||
})
|
||||
# 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,
|
||||
'ResourceCreationFailed',
|
||||
'UnexpectedError',
|
||||
[System.Management.Automation.ErrorCategory]::NotSpecified,
|
||||
$Name
|
||||
$Username
|
||||
)
|
||||
$PSCmdlet.ThrowTerminatingError($errorRecord)
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
Write-Verbose 'Completed resource creation process'
|
||||
Write-Verbose 'User account removal process completed'
|
||||
# Set ErrorActionPreference back to the value it had
|
||||
$ErrorActionPreference = $currentErrorActionValue
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user