AI-generated PowerShell should enter your workflow as a draft, not as a command to paste and hope for the best. Save the original response, separate the code from its surrounding prose, scan it, make focused changes, compare the diff, and rescan before deciding where to test it.
The process is straightforward, and it works for code from any model or assistant.
1. Define the task in your own words
Write down the expected behavior before reading the implementation in detail. This prevents plausible-looking code from quietly changing the task.
For the example in this guide, the requirements are:
| Question | Answer |
|---|---|
| Task | List file names and lengths in one caller-supplied directory. |
| Input | One existing directory path supplied through -Path. |
| Output | Objects containing Name and Length. |
| Privileges | The caller's existing read access; no elevation. |
| Allowed effects | Read directory contents only. |
| Failure behavior | Return a visible error when the directory cannot be read. |
For a real script, also identify modules, credentials, network endpoints, files, services, accounts, and any state the script can change.
2. Save the original response
Keep an unchanged copy of the complete response, including its explanation. Create a separate candidate file for the PowerShell itself.
Models often return Markdown like this:
Here is a script that lists file names and sizes:
```powershell
param(
[Parameter(Mandatory)]
[string] $Path
)
Get-ChildItem -LiteralPath $Path -File -ErrorAction SilentlyContinue |
Select-Object -Property Name, Length
```The fence lines and prose are document formatting. Copy only the PowerShell into candidate.ps1:
param(
[Parameter(Mandatory)]
[string] $Path
)
Get-ChildItem -LiteralPath $Path -File -ErrorAction SilentlyContinue |
Select-Object -Property Name, LengthKeeping extraction separate from editing makes the first diff meaningful. PSRafScan's MarkdownCodeFence rule also catches raw fence lines that remain in executable source.
3. Choose the intended use
This candidate has a top-level param() block and is meant to remain a saved file, so use Ps1File.
Use TerminalPaste when the exact code will be entered directly into a terminal. That mode adds checks for script-only constructs and paste-sensitive formatting.
4. Scan without running the candidate
Run PSRafScan against the saved source:
Invoke-PSRafScan `
-Path ./candidate.ps1 `
-IntendedUse Ps1File `
-FailOn Warning `
-OutputFormat Json `
-OutputPath ./candidate.psrafscan.json `
-NonInteractivePSRafScan uses PowerShell's native parser and its own rules to inspect the source. The scan command does not intentionally invoke candidate.ps1.
Read the complete report. FailOn controls the process result, but findings below the threshold still matter.
In this example, SilentErrorAction points to -ErrorAction SilentlyContinue. The parameter suppresses a non-terminating error message and continues, which conflicts with the requirement that a directory-read failure remain visible.
5. Make one focused change
Revise the error handling without changing the parameter, output properties, or read-only behavior:
param(
[Parameter(Mandatory)]
[string] $Path
)
try {
Get-ChildItem -LiteralPath $Path -File -ErrorAction Stop |
Select-Object -Property Name, Length
}
catch {
Write-Error "Could not list files at '$Path': $($_.Exception.Message)"
}The important word here is focused. Address the finding and the stated requirement without adding unrelated commands, convenience features, dependencies, or side effects.
6. Compare the diff
A diff makes scope changes easier to spot:
param(
[Parameter(Mandatory)]
[string] $Path
)
-Get-ChildItem -LiteralPath $Path -File -ErrorAction SilentlyContinue |
- Select-Object -Property Name, Length
+try {
+ Get-ChildItem -LiteralPath $Path -File -ErrorAction Stop |
+ Select-Object -Property Name, Length
+}
+catch {
+ Write-Error "Could not list files at '$Path': $($_.Exception.Message)"
+}Check each change against the requirements:
- The input parameter is unchanged.
- The command still reads files rather than modifying them.
- The output remains
NameandLength. - Failure now becomes visible to the caller.
- No network access, process launch, elevation, prompt, or dependency was added.
Reject a revision that broadens the task merely to make a finding disappear. Handle new behavior as a separate change with its own review.
7. Rescan the revision
Save the new version separately, then use the same scan settings:
Invoke-PSRafScan `
-Path ./candidate-revised.ps1 `
-IntendedUse Ps1File `
-FailOn Warning `
-OutputFormat Json `
-OutputPath ./candidate-revised.psrafscan.json `
-NonInteractiveConfirm that no syntax error was introduced, the targeted finding changed as expected, and the full report still matches the task. Compare the two source files again if the revision came from an external assistant.
8. Decide how to test it
For this read-only example, a useful test uses a temporary directory with a few known files and a standard user account. Check both success and failure:
- Confirm the output contains the expected file names and lengths.
- Supply a missing directory and confirm the error is visible.
- Confirm the script did not create, change, or delete files.
A state-changing script needs stronger controls. Use the smallest practical environment, minimal privileges, nonproduction data, expected results, and a cleanup or recovery plan.
Review the whole script, not only the finding
PSRafScan helps direct attention, but a human still needs to understand the script's behavior. Before testing, ask:
| Area | Questions |
|---|---|
| Commands | Do you recognize every cmdlet, executable, module, provider, and API? |
| Files and state | Which paths, services, accounts, settings, or resources can change? |
| Privileges | Does any step require elevation or a broader identity? |
| Credentials | Could secrets appear in source, output, logs, errors, or reports? |
| Errors | Are failures visible and specific? Can the script continue after a partial failure? |
| Output | What result proves success, and how will partial output be recognized? |
| Repeatability | What happens when the script runs twice or stops halfway? |
Ask another PowerShell practitioner to help when the purpose changes, an unfamiliar command is introduced, sensitive data is involved, or you cannot define a suitable test.
A repeatable short version
- Define the goal and allowed effects.
- Save the complete original response.
- Copy only the PowerShell into a candidate file.
- Choose
Ps1FileorTerminalPaste. - Scan and read every finding.
- Make one focused revision.
- Review the diff.
- Rescan with the same settings.
- Test with known inputs in an appropriate environment.
Related guidance
- Follow the AI-generated PowerShell workflow
- Run a PSRafScan review
- Choose the intended-use mode
- Browse PSRafScan rules
