Skip to content

Commit df81ca0

Browse files
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>
1 parent 52e14ab commit df81ca0

File tree

1 file changed

+91
-88
lines changed

1 file changed

+91
-88
lines changed

instructions/powershell.instructions.md

Lines changed: 91 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ safe, and maintainable scripts. It aligns with Microsoft’s PowerShell cmdlet d
3030

3131
- **Alias Avoidance:**
3232
- Use full cmdlet names
33-
- Avoid using aliases in scripts (e.g., use Get-ChildItem instead of gci)
33+
- Avoid using aliases in scripts (e.g., use `Get-ChildItem` instead of `gci`)
3434
- Document any custom aliases
3535
- Use full parameter names
3636

37-
### Example
37+
### Example - Naming Conventions
3838

3939
```powershell
4040
function Get-UserProfile {
@@ -49,6 +49,9 @@ function Get-UserProfile {
4949
)
5050
5151
process {
52+
$outputString = "Searching for: '$($Username)'"
53+
Write-Verbose -Message $outputString
54+
Write-Verbose -Message "Profile type: $ProfileType"
5255
# Logic here
5356
}
5457
}
@@ -75,12 +78,14 @@ function Get-UserProfile {
7578
- Enable tab completion where possible
7679

7780
- **Switch Parameters:**
78-
- Use [switch] for boolean flags
79-
- Avoid $true/$false parameters
80-
- Default to $false when omitted
81-
- Use clear action names
81+
- **ALWAYS** use `[switch]` for boolean flags, never `[bool]`
82+
- **NEVER** use `[bool]$Parameter` or assign default values
83+
- Switch parameters default to `$false` when omitted
84+
- Use clear, action-oriented names
85+
- Test presence with `.IsPresent`
86+
- Using `$true`/`$false` in parameter attributes (e.g., `Mandatory = $true`) is acceptable
8287

83-
### Example
88+
### Example - Parameter Design
8489

8590
```powershell
8691
function Set-ResourceConfiguration {
@@ -93,16 +98,24 @@ function Set-ResourceConfiguration {
9398
[ValidateSet('Dev', 'Test', 'Prod')]
9499
[string]$Environment = 'Dev',
95100
101+
# ✔️ CORRECT: Use `[switch]` with no default value
96102
[Parameter()]
97103
[switch]$Force,
98104
105+
# ❌ WRONG: Shows incorrect default assignment, however this is correct syntax (requires `[switch]` cast).
106+
[Parameter()]
107+
[switch]$Quiet = [switch]$true,
108+
99109
[Parameter()]
100110
[ValidateNotNullOrEmpty()]
101111
[string[]]$Tags
102112
)
103113
104114
process {
105-
# Logic here
115+
# Use .IsPresent to check switch state
116+
if ($Quiet.IsPresent) {
117+
Write-Verbose "Quiet mode enabled"
118+
}
106119
}
107120
}
108121
```
@@ -133,7 +146,7 @@ function Set-ResourceConfiguration {
133146
- Return modified/created object with `-PassThru`
134147
- Use verbose/warning for status updates
135148

136-
### Example
149+
### Example - Pipeline and Output
137150

138151
```powershell
139152
function Update-ResourceStatus {
@@ -163,7 +176,7 @@ function Update-ResourceStatus {
163176
Name = $Name
164177
Status = $Status
165178
LastUpdated = $timestamp
166-
UpdatedBy = $env:USERNAME
179+
UpdatedBy = "$($env:USERNAME)"
167180
}
168181
169182
# Only output if PassThru is specified
@@ -183,8 +196,8 @@ function Update-ResourceStatus {
183196
- **ShouldProcess Implementation:**
184197
- Use `[CmdletBinding(SupportsShouldProcess = $true)]`
185198
- Set appropriate `ConfirmImpact` level
186-
- Call `$PSCmdlet.ShouldProcess()` for system changes
187-
- Use `ShouldContinue()` for additional confirmations
199+
- Call `$PSCmdlet.ShouldProcess()` as close the the changes action
200+
- Use `$PSCmdlet.ShouldContinue()` for additional confirmations
188201

189202
- **Message Streams:**
190203
- `Write-Verbose` for operational details with `-Verbose`
@@ -209,69 +222,32 @@ function Update-ResourceStatus {
209222
- Support automation scenarios
210223
- Document all required inputs
211224

212-
### Example
225+
### Example - Error Handling and Safety
213226

214227
```powershell
215-
function Remove-UserAccount {
216-
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
228+
function Remove-CacheFiles {
229+
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
217230
param(
218-
[Parameter(Mandatory, ValueFromPipeline)]
219-
[ValidateNotNullOrEmpty()]
220-
[string]$Username,
221-
222-
[Parameter()]
223-
[switch]$Force
231+
[Parameter(Mandatory)]
232+
[string]$Path
224233
)
225234
226-
begin {
227-
Write-Verbose 'Starting user account removal process'
228-
$ErrorActionPreference = 'Stop'
229-
}
230-
231-
process {
232-
try {
233-
# Validation
234-
if (-not (Test-UserExists -Username $Username)) {
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)
242-
return
243-
}
244-
245-
# Confirmation
246-
$shouldProcessMessage = "Remove user account '$Username'"
247-
if ($Force -or $PSCmdlet.ShouldProcess($Username, $shouldProcessMessage)) {
248-
Write-Verbose "Removing user account: $Username"
249-
250-
# Main operation
251-
Remove-ADUser -Identity $Username -ErrorAction Stop
252-
Write-Warning "User account '$Username' has been removed"
253-
}
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)
235+
try {
236+
$files = Get-ChildItem -Path $Path -Filter "*.cache" -ErrorAction Stop
237+
238+
# Demonstrates WhatIf support
239+
if ($PSCmdlet.ShouldProcess($Path, 'Remove cache files')) {
240+
$files | Remove-Item -Force -ErrorAction Stop
241+
Write-Verbose "Removed $($files.Count) cache files from $Path"
270242
}
271-
}
272-
273-
end {
274-
Write-Verbose 'User account removal process completed'
243+
} catch {
244+
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
245+
$_.Exception,
246+
'RemovalFailed',
247+
[System.Management.Automation.ErrorCategory]::NotSpecified,
248+
$Path
249+
)
250+
$PSCmdlet.WriteError($errorRecord)
275251
}
276252
}
277253
```
@@ -307,50 +283,77 @@ function Remove-UserAccount {
307283
- Use `ForEach-Object` instead of `%`
308284
- Use `Get-ChildItem` instead of `ls` or `dir`
309285

286+
---
287+
310288
## Full Example: End-to-End Cmdlet Pattern
311289

312290
```powershell
313-
function New-Resource {
314-
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
291+
function Remove-UserAccount {
292+
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
315293
param(
316-
[Parameter(Mandatory = $true,
317-
ValueFromPipeline = $true,
318-
ValueFromPipelineByPropertyName = $true)]
294+
[Parameter(Mandatory, ValueFromPipeline)]
319295
[ValidateNotNullOrEmpty()]
320-
[string]$Name,
296+
[string]$Username,
321297
322298
[Parameter()]
323-
[ValidateSet('Development', 'Production')]
324-
[string]$Environment = 'Development'
299+
[switch]$Force
325300
)
326301
327302
begin {
328-
Write-Verbose 'Starting resource creation process'
303+
Write-Verbose 'Starting user account removal process'
304+
$currentErrorActionValue = $ErrorActionPreference
305+
$ErrorActionPreference = 'Stop'
329306
}
330307
331308
process {
332309
try {
333-
if ($PSCmdlet.ShouldProcess($Name, 'Create new resource')) {
334-
# Resource creation logic here
335-
Write-Output ([PSCustomObject]@{
336-
Name = $Name
337-
Environment = $Environment
338-
Created = Get-Date
339-
})
310+
# Validation
311+
if (-not (Test-UserExists -Username $Username)) {
312+
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
313+
[System.Exception]::new("User account '$Username' not found"),
314+
'UserNotFound',
315+
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
316+
$Username
317+
)
318+
$PSCmdlet.WriteError($errorRecord)
319+
return
340320
}
321+
322+
# ShouldProcess enables -WhatIf and -Confirm support
323+
if ($PSCmdlet.ShouldProcess($Username, "Remove user account")) {
324+
# ShouldContinue provides an additional confirmation prompt for high-impact operations
325+
# This prompt is bypassed when -Force is specified
326+
if ($Force -or $PSCmdlet.ShouldContinue("Are you sure you want to remove '$Username'?", "Confirm Removal")) {
327+
Write-Verbose "Removing user account: $Username"
328+
329+
# Main operation
330+
Remove-ADUser -Identity $Username -ErrorAction Stop
331+
Write-Warning "User account '$Username' has been removed"
332+
}
333+
}
334+
} catch [Microsoft.ActiveDirectory.Management.ADException] {
335+
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
336+
$_.Exception,
337+
'ActiveDirectoryError',
338+
[System.Management.Automation.ErrorCategory]::NotSpecified,
339+
$Username
340+
)
341+
$PSCmdlet.ThrowTerminatingError($errorRecord)
341342
} catch {
342343
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
343344
$_.Exception,
344-
'ResourceCreationFailed',
345+
'UnexpectedError',
345346
[System.Management.Automation.ErrorCategory]::NotSpecified,
346-
$Name
347+
$Username
347348
)
348349
$PSCmdlet.ThrowTerminatingError($errorRecord)
349350
}
350351
}
351352
352353
end {
353-
Write-Verbose 'Completed resource creation process'
354+
Write-Verbose 'User account removal process completed'
355+
# Set ErrorActionPreference back to the value it had
356+
$ErrorActionPreference = $currentErrorActionValue
354357
}
355358
}
356359
```

0 commit comments

Comments
 (0)