Running PowerShell in NORA: The Complete Guide
PowerShell is a first-class citizen in NORA. You can run any PowerShell command — pipelines, API calls, registry operations, file manipulation, anything — directly inside a workflow node. But there are a few behaviors that are unconventional compared to running PowerShell directly in a terminal. This guide explains how it actually works, what to expect, and how to avoid the most common frustrations.
How NORA Executes Commands
When you run a Command Node in NORA, the command string goes through two layers before it executes:
- Shell detection — NORA analyzes the command text to determine which shell to use (CMD, PowerShell, Bash, or a fallback)
- Shell wrapping — The command is wrapped in
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "..."(or equivalent for other shells) and passed to Node.js’sexec()
That second step — the wrapping — is where most PowerShell quirks come from. The command is passed through cmd.exe as an intermediate layer before PowerShell receives it, which affects how quotes are interpreted.
The Shell Dropdown (Most Important Setting)
Every Command Node has a Shell dropdown. The options are:
| Setting | Behavior |
|---|---|
| Auto | NORA guesses the shell from your command text |
| PowerShell | Always uses powershell.exe (Windows PowerShell 5.1) |
| pwsh | Always uses pwsh.exe (PowerShell 7+, if installed) |
| CMD | Always uses cmd.exe |
| Bash | Always uses bash (WSL or Git Bash) |
Always set this to PowerShell when writing PowerShell commands. Do not rely on Auto for anything beyond simple cmdlets.
Why Auto-Detection Can Fail
Auto-detection works by scanning your command for known PowerShell patterns: Get-, Set-, $env:, Where-Object, $_. If your command doesn’t contain one of those patterns, NORA defaults to CMD — even if the rest of the command is entirely PowerShell syntax.
Example that fails with Auto:
$token = 'abc123'; Invoke-RestMethod -Uri 'https://api.example.com' -Method GET
This contains $token (a plain variable) and Invoke-RestMethod — neither of which are in NORA’s auto-detection list. NORA routes it to CMD. CMD does not understand $token and throws:
'$token' is not recognized as an internal or external command
Fix: Set Shell to PowerShell explicitly. Auto-detection is a convenience, not a guarantee.
The Quoting Rule: Use Single Quotes for Values
This is the most important behavioral difference between running PowerShell in NORA vs. running it directly in a terminal.
What Happens Internally
When Shell is set to PowerShell and you run a command, NORA wraps it like this:
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "<your command here>"
That outer "..." is passed through cmd.exe. CMD interprets \" as a literal backslash+quote — not as an escaped double quote. This means any double-quoted strings inside your command get mangled before PowerShell ever sees them.
The Problem in Practice
This will fail (double quotes inside):
$token = "ghp_abc123"; Invoke-RestMethod -Uri "https://api.github.com/repos/..." -Method GET
What cmd.exe actually sees:
powershell.exe -NoProfile -Command "$token = "ghp_abc123"; Invoke-RestMethod -Uri "https://api.github.com/..."
The extra unescaped quotes break the command completely.
This works (single quotes for values):
$token = 'ghp_abc123'; Invoke-RestMethod -Uri 'https://api.github.com/repos/...' -Method GET
Single quotes pass through cmd.exe untouched. PowerShell treats single-quoted strings as literals (no variable expansion), which is exactly what you want for static values like tokens, file paths, and URLs.
When You Need Variable Expansion
There is one case where you still need double quotes: when you want PowerShell to expand a variable inside a string.
$token = 'ghp_abc123'; Invoke-RestMethod -Headers @{ Authorization = "Bearer $token" }
"Bearer $token" must be double-quoted so PowerShell expands $token. Everything else — the token value itself, the URL, file paths — stays single-quoted.
Summary: Quote Selection Rules
| What you’re writing | Use |
|---|---|
| Static string values (tokens, URLs, paths) | Single quotes '...' |
| Variable expansion inside strings | Double quotes "..." |
| PowerShell hashtable keys | Single quotes 'Content-Type' |
Authorization headers with $variable |
Double quotes "Bearer $token" |
Write Commands on One Line
NORA Command Nodes accept a single command string. Multi-line PowerShell blocks do not work as-is — they need to be collapsed to one line using semicolons.
Multi-line (works in a terminal, not in NORA):
$token = 'abc123'
$body = @{ message = 'hello' } | ConvertTo-Json
Invoke-RestMethod -Uri 'https://...' -Method PUT -Body $body
One line (works in NORA):
$token = 'abc123'; $body = @{ message = 'hello' } | ConvertTo-Json; Invoke-RestMethod -Uri 'https://...' -Method PUT -Body $body
Semicolons work the same in PowerShell as newlines for statement separation. Collapse every line with ; and it runs identically.
Real Example: Pushing a File to GitHub via API
Here’s a complete working example that pushes a local file to a GitHub repository using the GitHub Contents API — no git required.
What it does: Reads a local file, base64-encodes it, and calls the GitHub API to create or update the file in a repo.
$token = 'YOUR_PAT_HERE'; $filePath = 'C:\Users\YourUser\Documents\NORA\automation\workflows\my-workflow.js'; $content = [Convert]::ToBase64String([IO.File]::ReadAllBytes($filePath)); $body = @{ message = 'Add workflow file'; content = $content } | ConvertTo-Json; Invoke-RestMethod -Uri 'https://api.github.com/repos/youruser/yourrepo/contents/folder/my-workflow.js' -Method PUT -Headers @{ Authorization = "Bearer $token"; 'Content-Type' = 'application/json' } -Body $body
Key points in this command:
– $token = 'YOUR_PAT_HERE' — single quotes, no expansion needed
– $filePath = 'C:\Users\...' — single quotes, backslashes safe inside single quotes in PowerShell
– Authorization = "Bearer $token" — double quotes needed here because $token must expand
– 'Content-Type' = 'application/json' — single quotes for the key and value
– Everything on one line with ; separators
Updating an Existing File (SHA Required)
If the file already exists in the repo, GitHub returns a 422 error unless you include the file’s current SHA in the request body. Get the SHA first, then include it:
$token = 'YOUR_PAT_HERE'; $filePath = 'C:\Users\YourUser\Documents\NORA\automation\workflows\my-workflow.js'; $content = [Convert]::ToBase64String([IO.File]::ReadAllBytes($filePath)); $sha = (Invoke-RestMethod -Uri 'https://api.github.com/repos/youruser/yourrepo/contents/folder/my-workflow.js' -Headers @{ Authorization = "Bearer $token" }).sha; $body = @{ message = 'Update workflow file'; content = $content; sha = $sha } | ConvertTo-Json; Invoke-RestMethod -Uri 'https://api.github.com/repos/youruser/yourrepo/contents/folder/my-workflow.js' -Method PUT -Headers @{ Authorization = "Bearer $token"; 'Content-Type' = 'application/json' } -Body $body
Common Errors and What They Mean
'$token' is not recognized as an internal or external command
Cause: Shell is set to Auto and NORA routed to CMD because no auto-detection markers were found in the command.
Fix: Set the node’s Shell dropdown to PowerShell.
Variable reference is not valid. ':' was not followed by a valid variable name character
Cause: You wrote $env: "somevalue" — that’s not valid syntax. $env: must be followed directly by a variable name ($env:GITHUB_TOKEN), not a value.
Fix: Use $token = 'somevalue' for plain variable assignment. Use $env:VARNAME = 'value' only for setting environment variables.
Unexpected token '"somevalue"' or ParserError
Cause: Double quotes in your command are being stripped or mangled by the cmd.exe intermediary layer before PowerShell sees them.
Fix: Switch all static string values from double quotes to single quotes.
422 Unprocessable Entity from GitHub API
Cause: The file already exists at that path and you didn’t include the current SHA in the request body.
Fix: Fetch the SHA first and include it in $body (see the update example above).
401 Unauthorized from GitHub API
Cause: Token is invalid, expired, or was rotated.
Fix: Generate a new Personal Access Token at github.com/settings/tokens with repo scope. Do not hardcode tokens in workflow configs you export or share.
Using $env: for Tokens (Safer Pattern)
Instead of putting your token directly in the command string, you can set it as an environment variable in a prior step and reference it later:
$env:GH_TOKEN = 'YOUR_PAT_HERE'; $filePath = 'C:\path\to\file.js'; $content = [Convert]::ToBase64String([IO.File]::ReadAllBytes($filePath)); $body = @{ message = 'Add file'; content = $content } | ConvertTo-Json; Invoke-RestMethod -Uri 'https://api.github.com/repos/youruser/yourrepo/contents/file.js' -Method PUT -Headers @{ Authorization = "Bearer $env:GH_TOKEN"; 'Content-Type' = 'application/json' } -Body $body
Using $env:GH_TOKEN also has a side benefit: $env: is one of NORA’s auto-detection markers, so the node will route to PowerShell correctly even if Shell is set to Auto.
PowerShell Pipelines Work Normally
Complex pipelines, filtering, and object manipulation all work as expected once Shell is set correctly:
Get-Process | Where-Object { $_.CPU -gt 10 } | Select-Object Name, CPU | ConvertTo-Json
Get-ChildItem 'C:\Logs' -Filter '*.log' | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } | Select-Object Name, LastWriteTime
Get-Volume | Where-Object { $_.SizeRemaining / $_.Size -lt 0.15 } | Select-Object DriveLetter, @{N='FreeGB';E={[math]::Round($_.SizeRemaining/1GB,2)}} | ConvertTo-Json
Quick Reference Card
| Situation | What to do |
|---|---|
| Any PowerShell command | Set Shell dropdown to PowerShell |
| Multi-line script | Collapse to one line with ; between statements |
| String values (tokens, paths, URLs) | Use single quotes '...' |
| Variable expansion inside a string | Use double quotes "Bearer $token" |
| File already exists on GitHub | Include sha in request body |
| Auto-detection needed (Auto mode) | Start command with $env:DUMMY='x'; to force PS detection |
Get NORA
Download at software.reibuys.com/nora. Windows 10 or later. One-time purchase, no subscription.