fix: improve error handling in PowerShell guidelines (#280)
Update error handling examples to use $PSCmdlet.WriteError() and $PSCmdlet.ThrowTerminatingError() instead of Write-Error and throw for better PowerShell cmdlet integration.
This commit is contained in:
parent
c95af033c9
commit
897d61b03f
@ -1,11 +1,12 @@
|
|||||||
---
|
---
|
||||||
applyTo: '**/*.ps1,**/*.psm1'
|
applyTo: '**/*.ps1,**/*.psm1'
|
||||||
description: 'PowerShell cmdlet and scripting best practices based on Microsoft guidelines'
|
description: 'PowerShell cmdlet and scripting best practices based on Microsoft guidelines'
|
||||||
---
|
---
|
||||||
|
|
||||||
# PowerShell Cmdlet Development 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.
|
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
|
## Naming Conventions
|
||||||
|
|
||||||
@ -87,19 +88,19 @@ function Set-ResourceConfiguration {
|
|||||||
param(
|
param(
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[string]$Name,
|
[string]$Name,
|
||||||
|
|
||||||
[Parameter()]
|
[Parameter()]
|
||||||
[ValidateSet('Dev', 'Test', 'Prod')]
|
[ValidateSet('Dev', 'Test', 'Prod')]
|
||||||
[string]$Environment = 'Dev',
|
[string]$Environment = 'Dev',
|
||||||
|
|
||||||
[Parameter()]
|
[Parameter()]
|
||||||
[switch]$Force,
|
[switch]$Force,
|
||||||
|
|
||||||
[Parameter()]
|
[Parameter()]
|
||||||
[ValidateNotNullOrEmpty()]
|
[ValidateNotNullOrEmpty()]
|
||||||
[string[]]$Tags
|
[string[]]$Tags
|
||||||
)
|
)
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Logic here
|
# Logic here
|
||||||
}
|
}
|
||||||
@ -150,32 +151,32 @@ function Update-ResourceStatus {
|
|||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
Write-Verbose "Starting resource status update process"
|
Write-Verbose 'Starting resource status update process'
|
||||||
$timestamp = Get-Date
|
$timestamp = Get-Date
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Process each resource individually
|
# Process each resource individually
|
||||||
Write-Verbose "Processing resource: $Name"
|
Write-Verbose "Processing resource: $Name"
|
||||||
|
|
||||||
$resource = [PSCustomObject]@{
|
$resource = [PSCustomObject]@{
|
||||||
Name = $Name
|
Name = $Name
|
||||||
Status = $Status
|
Status = $Status
|
||||||
LastUpdated = $timestamp
|
LastUpdated = $timestamp
|
||||||
UpdatedBy = $env:USERNAME
|
UpdatedBy = $env:USERNAME
|
||||||
}
|
}
|
||||||
|
|
||||||
# Only output if PassThru is specified
|
# Only output if PassThru is specified
|
||||||
if ($PassThru) {
|
if ($PassThru.IsPresent) {
|
||||||
Write-Output $resource
|
Write-Output $resource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
Write-Verbose "Resource status update process completed"
|
Write-Verbose 'Resource status update process completed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Error Handling and Safety
|
## Error Handling and Safety
|
||||||
|
|
||||||
@ -198,6 +199,9 @@ function Update-ResourceStatus {
|
|||||||
- Return meaningful error messages
|
- Return meaningful error messages
|
||||||
- Use ErrorVariable when needed
|
- Use ErrorVariable when needed
|
||||||
- Include proper terminating vs non-terminating error handling
|
- Include proper terminating vs non-terminating error handling
|
||||||
|
- In advanced functions with `[CmdletBinding()]`, prefer `$PSCmdlet.WriteError()` over `Write-Error`
|
||||||
|
- In advanced functions with `[CmdletBinding()]`, prefer `$PSCmdlet.ThrowTerminatingError()` over `throw`
|
||||||
|
- Construct proper ErrorRecord objects with category, target, and exception details
|
||||||
|
|
||||||
- **Non-Interactive Design:**
|
- **Non-Interactive Design:**
|
||||||
- Accept input via parameters
|
- Accept input via parameters
|
||||||
@ -220,7 +224,7 @@ function Remove-UserAccount {
|
|||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
Write-Verbose "Starting user account removal process"
|
Write-Verbose 'Starting user account removal process'
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +232,13 @@ function Remove-UserAccount {
|
|||||||
try {
|
try {
|
||||||
# Validation
|
# Validation
|
||||||
if (-not (Test-UserExists -Username $Username)) {
|
if (-not (Test-UserExists -Username $Username)) {
|
||||||
Write-Error "User account '$Username' not found"
|
$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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,24 +246,32 @@ function Remove-UserAccount {
|
|||||||
$shouldProcessMessage = "Remove user account '$Username'"
|
$shouldProcessMessage = "Remove user account '$Username'"
|
||||||
if ($Force -or $PSCmdlet.ShouldProcess($Username, $shouldProcessMessage)) {
|
if ($Force -or $PSCmdlet.ShouldProcess($Username, $shouldProcessMessage)) {
|
||||||
Write-Verbose "Removing user account: $Username"
|
Write-Verbose "Removing user account: $Username"
|
||||||
|
|
||||||
# Main operation
|
# Main operation
|
||||||
Remove-ADUser -Identity $Username -ErrorAction Stop
|
Remove-ADUser -Identity $Username -ErrorAction Stop
|
||||||
Write-Warning "User account '$Username' has been removed"
|
Write-Warning "User account '$Username' has been removed"
|
||||||
}
|
}
|
||||||
}
|
} catch [Microsoft.ActiveDirectory.Management.ADException] {
|
||||||
catch [Microsoft.ActiveDirectory.Management.ADException] {
|
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
|
||||||
Write-Error "Active Directory error: $_"
|
$_.Exception,
|
||||||
throw
|
'ActiveDirectoryError',
|
||||||
}
|
[System.Management.Automation.ErrorCategory]::NotSpecified,
|
||||||
catch {
|
$Username
|
||||||
Write-Error "Unexpected error removing user account: $_"
|
)
|
||||||
throw
|
$PSCmdlet.ThrowTerminatingError($errorRecord)
|
||||||
|
} catch {
|
||||||
|
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
|
||||||
|
$_.Exception,
|
||||||
|
'UnexpectedError',
|
||||||
|
[System.Management.Automation.ErrorCategory]::NotSpecified,
|
||||||
|
$Username
|
||||||
|
)
|
||||||
|
$PSCmdlet.ThrowTerminatingError($errorRecord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
Write-Verbose "User account removal process completed"
|
Write-Verbose 'User account removal process completed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -296,8 +314,8 @@ function New-Resource {
|
|||||||
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
|
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true,
|
[Parameter(Mandatory = $true,
|
||||||
ValueFromPipeline = $true,
|
ValueFromPipeline = $true,
|
||||||
ValueFromPipelineByPropertyName = $true)]
|
ValueFromPipelineByPropertyName = $true)]
|
||||||
[ValidateNotNullOrEmpty()]
|
[ValidateNotNullOrEmpty()]
|
||||||
[string]$Name,
|
[string]$Name,
|
||||||
|
|
||||||
@ -305,29 +323,34 @@ function New-Resource {
|
|||||||
[ValidateSet('Development', 'Production')]
|
[ValidateSet('Development', 'Production')]
|
||||||
[string]$Environment = 'Development'
|
[string]$Environment = 'Development'
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
Write-Verbose "Starting resource creation process"
|
Write-Verbose 'Starting resource creation process'
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
try {
|
try {
|
||||||
if ($PSCmdlet.ShouldProcess($Name, "Create new resource")) {
|
if ($PSCmdlet.ShouldProcess($Name, 'Create new resource')) {
|
||||||
# Resource creation logic here
|
# Resource creation logic here
|
||||||
Write-Output ([PSCustomObject]@{
|
Write-Output ([PSCustomObject]@{
|
||||||
Name = $Name
|
Name = $Name
|
||||||
Environment = $Environment
|
Environment = $Environment
|
||||||
Created = Get-Date
|
Created = Get-Date
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch {
|
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
|
||||||
Write-Error "Failed to create resource: $_"
|
$_.Exception,
|
||||||
|
'ResourceCreationFailed',
|
||||||
|
[System.Management.Automation.ErrorCategory]::NotSpecified,
|
||||||
|
$Name
|
||||||
|
)
|
||||||
|
$PSCmdlet.ThrowTerminatingError($errorRecord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
Write-Verbose "Completed resource creation process"
|
Write-Verbose 'Completed resource creation process'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user