Skip to content

Commit 897d61b

Browse files
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.
1 parent c95af03 commit 897d61b

1 file changed

Lines changed: 64 additions & 41 deletions

File tree

instructions/powershell.instructions.md

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
---
22
applyTo: '**/*.ps1,**/*.psm1'
33
description: 'PowerShell cmdlet and scripting best practices based on Microsoft guidelines'
4-
---
4+
---
55

66
# PowerShell Cmdlet Development Guidelines
77

8-
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.
8+
This guide provides PowerShell-specific instructions to help GitHub Copilot generate idiomatic,
9+
safe, and maintainable scripts. It aligns with Microsoft’s PowerShell cmdlet development guidelines.
910

1011
## Naming Conventions
1112

@@ -87,19 +88,19 @@ function Set-ResourceConfiguration {
8788
param(
8889
[Parameter(Mandatory)]
8990
[string]$Name,
90-
91+
9192
[Parameter()]
9293
[ValidateSet('Dev', 'Test', 'Prod')]
9394
[string]$Environment = 'Dev',
94-
95+
9596
[Parameter()]
9697
[switch]$Force,
97-
98+
9899
[Parameter()]
99100
[ValidateNotNullOrEmpty()]
100101
[string[]]$Tags
101102
)
102-
103+
103104
process {
104105
# Logic here
105106
}
@@ -150,32 +151,32 @@ function Update-ResourceStatus {
150151
)
151152
152153
begin {
153-
Write-Verbose "Starting resource status update process"
154+
Write-Verbose 'Starting resource status update process'
154155
$timestamp = Get-Date
155156
}
156157
157158
process {
158159
# Process each resource individually
159160
Write-Verbose "Processing resource: $Name"
160-
161+
161162
$resource = [PSCustomObject]@{
162-
Name = $Name
163-
Status = $Status
163+
Name = $Name
164+
Status = $Status
164165
LastUpdated = $timestamp
165-
UpdatedBy = $env:USERNAME
166+
UpdatedBy = $env:USERNAME
166167
}
167168
168169
# Only output if PassThru is specified
169-
if ($PassThru) {
170+
if ($PassThru.IsPresent) {
170171
Write-Output $resource
171172
}
172173
}
173174
174175
end {
175-
Write-Verbose "Resource status update process completed"
176+
Write-Verbose 'Resource status update process completed'
176177
}
177178
}
178-
```
179+
```
179180

180181
## Error Handling and Safety
181182

@@ -198,6 +199,9 @@ function Update-ResourceStatus {
198199
- Return meaningful error messages
199200
- Use ErrorVariable when needed
200201
- Include proper terminating vs non-terminating error handling
202+
- In advanced functions with `[CmdletBinding()]`, prefer `$PSCmdlet.WriteError()` over `Write-Error`
203+
- In advanced functions with `[CmdletBinding()]`, prefer `$PSCmdlet.ThrowTerminatingError()` over `throw`
204+
- Construct proper ErrorRecord objects with category, target, and exception details
201205

202206
- **Non-Interactive Design:**
203207
- Accept input via parameters
@@ -220,40 +224,54 @@ function Remove-UserAccount {
220224
)
221225
222226
begin {
223-
Write-Verbose "Starting user account removal process"
227+
Write-Verbose 'Starting user account removal process'
224228
$ErrorActionPreference = 'Stop'
225229
}
226230
227231
process {
228232
try {
229233
# Validation
230234
if (-not (Test-UserExists -Username $Username)) {
231-
Write-Error "User account '$Username' not found"
235+
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
236+
[System.Exception]::new("User account '$Username' not found"),
237+
'UserNotFound',
238+
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
239+
$Username
240+
)
241+
$PSCmdlet.WriteError($errorRecord)
232242
return
233243
}
234244
235245
# Confirmation
236246
$shouldProcessMessage = "Remove user account '$Username'"
237247
if ($Force -or $PSCmdlet.ShouldProcess($Username, $shouldProcessMessage)) {
238248
Write-Verbose "Removing user account: $Username"
239-
249+
240250
# Main operation
241251
Remove-ADUser -Identity $Username -ErrorAction Stop
242252
Write-Warning "User account '$Username' has been removed"
243253
}
244-
}
245-
catch [Microsoft.ActiveDirectory.Management.ADException] {
246-
Write-Error "Active Directory error: $_"
247-
throw
248-
}
249-
catch {
250-
Write-Error "Unexpected error removing user account: $_"
251-
throw
254+
} catch [Microsoft.ActiveDirectory.Management.ADException] {
255+
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
256+
$_.Exception,
257+
'ActiveDirectoryError',
258+
[System.Management.Automation.ErrorCategory]::NotSpecified,
259+
$Username
260+
)
261+
$PSCmdlet.ThrowTerminatingError($errorRecord)
262+
} catch {
263+
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
264+
$_.Exception,
265+
'UnexpectedError',
266+
[System.Management.Automation.ErrorCategory]::NotSpecified,
267+
$Username
268+
)
269+
$PSCmdlet.ThrowTerminatingError($errorRecord)
252270
}
253271
}
254272
255273
end {
256-
Write-Verbose "User account removal process completed"
274+
Write-Verbose 'User account removal process completed'
257275
}
258276
}
259277
```
@@ -296,38 +314,43 @@ function New-Resource {
296314
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
297315
param(
298316
[Parameter(Mandatory = $true,
299-
ValueFromPipeline = $true,
300-
ValueFromPipelineByPropertyName = $true)]
317+
ValueFromPipeline = $true,
318+
ValueFromPipelineByPropertyName = $true)]
301319
[ValidateNotNullOrEmpty()]
302320
[string]$Name,
303321
304322
[Parameter()]
305323
[ValidateSet('Development', 'Production')]
306324
[string]$Environment = 'Development'
307325
)
308-
326+
309327
begin {
310-
Write-Verbose "Starting resource creation process"
328+
Write-Verbose 'Starting resource creation process'
311329
}
312-
330+
313331
process {
314332
try {
315-
if ($PSCmdlet.ShouldProcess($Name, "Create new resource")) {
333+
if ($PSCmdlet.ShouldProcess($Name, 'Create new resource')) {
316334
# Resource creation logic here
317335
Write-Output ([PSCustomObject]@{
318-
Name = $Name
319-
Environment = $Environment
320-
Created = Get-Date
321-
})
336+
Name = $Name
337+
Environment = $Environment
338+
Created = Get-Date
339+
})
322340
}
323-
}
324-
catch {
325-
Write-Error "Failed to create resource: $_"
341+
} catch {
342+
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
343+
$_.Exception,
344+
'ResourceCreationFailed',
345+
[System.Management.Automation.ErrorCategory]::NotSpecified,
346+
$Name
347+
)
348+
$PSCmdlet.ThrowTerminatingError($errorRecord)
326349
}
327350
}
328-
351+
329352
end {
330-
Write-Verbose "Completed resource creation process"
353+
Write-Verbose 'Completed resource creation process'
331354
}
332355
}
333356
```

0 commit comments

Comments
 (0)