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:

QuestionAnswer
TaskList file names and lengths in one caller-supplied directory.
InputOne existing directory path supplied through -Path.
OutputObjects containing Name and Length.
PrivilegesThe caller's existing read access; no elevation.
Allowed effectsRead directory contents only.
Failure behaviorReturn 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:

text
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:

powershell
param(
    [Parameter(Mandatory)]
    [string] $Path
)

Get-ChildItem -LiteralPath $Path -File -ErrorAction SilentlyContinue |
    Select-Object -Property Name, Length

Keeping 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:

powershell
Invoke-PSRafScan `
    -Path ./candidate.ps1 `
    -IntendedUse Ps1File `
    -FailOn Warning `
    -OutputFormat Json `
    -OutputPath ./candidate.psrafscan.json `
    -NonInteractive

PSRafScan 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:

powershell
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:

diff
 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 Name and Length.
  • 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:

powershell
Invoke-PSRafScan `
    -Path ./candidate-revised.ps1 `
    -IntendedUse Ps1File `
    -FailOn Warning `
    -OutputFormat Json `
    -OutputPath ./candidate-revised.psrafscan.json `
    -NonInteractive

Confirm 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:

  1. Confirm the output contains the expected file names and lengths.
  2. Supply a missing directory and confirm the error is visible.
  3. 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:

AreaQuestions
CommandsDo you recognize every cmdlet, executable, module, provider, and API?
Files and stateWhich paths, services, accounts, settings, or resources can change?
PrivilegesDoes any step require elevation or a broader identity?
CredentialsCould secrets appear in source, output, logs, errors, or reports?
ErrorsAre failures visible and specific? Can the script continue after a partial failure?
OutputWhat result proves success, and how will partial output be recognized?
RepeatabilityWhat 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

  1. Define the goal and allowed effects.
  2. Save the complete original response.
  3. Copy only the PowerShell into a candidate file.
  4. Choose Ps1File or TerminalPaste.
  5. Scan and read every finding.
  6. Make one focused revision.
  7. Review the diff.
  8. Rescan with the same settings.
  9. Test with known inputs in an appropriate environment.

Sources