Cronomicon
The self-hosted Script Orchestrator — schedule and run Bash, Ansible, Terraform, PowerShell, Perl and Python jobs across your fleet from one auditable web console.
How to run PowerShell well inside Cronomicon — chiefly against Windows fleets over OpenSSH: how a powershell run actually reaches its target, what that means for Windows hosts (and the one thing to know for Linux targets), how env, run inputs and outputs look from inside a PowerShell script, the exit-code semantics that decide whether your run reads Success or Failed, and how to use Cronomicon's features — scopes, run inputs, reference bindings, workflow outputs — idiomatically from PowerShell.
PowerShell is a shell-family run type, so most of the machinery is shared with Bash: read the Bash Guide for the two-executor model, targeting, bastions and secrets in full depth — this guide states the shared rules briefly and goes deep only where PowerShell differs. Companions: the User Manual, the Administrator Manual (§4, SSH executor), and the Runner Install Guide / Runner Config Guide / Runner Security Guide guides.
The one-sentence model. A PowerShell script is a Script (run type
powershell), a Scope names the hosts it may touch, a Job binds the two to schedules and env — and the run executes over SSH: the dialing side (the Cronomicon server or a runner) connects to each target and invokespowershell -NonInteractive -Commandthere.
1 · How a PowerShell run reaches its target
Like Bash, a powershell run can execute on either executor — the in-app SSH executor (default via the resolution chain; the pool is off until an operator sets CRONOMICON_SSH_EXECUTOR_ENABLED=true, or shell runs queue forever) or a runner inside the target's segment. On both, the actual mechanics are identical: the dialing side opens an SSH connection to each target and runs
powershell -NonInteractive -Command <your script>on the target (when the run injects environment, the invocation becomes powershell -NonInteractive -Command - and the program — an $env: prelude plus your body — arrives on stdin, so no value ever touches argv; see §4). Two consequences worth internalizing:
- Windows targets are the primary case. The target needs an OpenSSH server (a built-in optional feature since Windows Server 2019 / Windows 10) and
powershellresolvable in the SSH session's PATH — true by default, since Windows PowerShell 5.1 ships with the OS. The default SSH shell (cmd or powershell) doesn't matter: Cronomicon invokespowershellexplicitly. - Linux/macOS targets need one symlink. PowerShell 7 installs itself as
pwsh, and the remote invocation callspowershell— so on a non-Windows target, provide the name (e.g.ln -s /usr/bin/pwsh /usr/local/bin/powershell) or the run fails per-host with command not found.
Runner capability nuance. A runner claims the
powershellrun type whenpwshis on its own PATH (the auto-detect probe) — even though execution happens on the targets over SSH. For a Linux runner that serves a Windows fleet but has no local pwsh, declare the capability explicitly (-capabilities bash,powershell/CRONOMICON_RUNNER_CAPABILITIES); otherwise the run queues “waiting for a powershell-capable runner.” A runner's-osflag records the runner host's own OS — it does not restrict what OS its targets run.
2 · Getting scripts in
Identical to the rest of the catalog (Bash Guide §2 in depth): any raw *.ps1 under scripts/ in the definitions repo is auto-discovered as a Script with run type powershell; or author a kind: Script YAML with exactly one of command / script / scriptPath and optional declared prompts; jobs bind by script_ref, or in-app via the Composer. Wire cronomicon validate . into CI — and Invoke-ScriptAnalyzer (PSScriptAnalyzer) sits beside it as naturally as shellcheck does for bash.
3 · Windows fleets: scopes, host records & auth
Targeting follows the shared shell-family rules — a scope fans out to its member hosts, a pinned target_host wins outright on every run path, a per-run host subset is SSH-executor-only (422 scope_membership otherwise) — with these Windows-fleet specifics:
- Declare
powershellon the scope. Supported run types are chips on the scope (the “bash floor” meansbashis always present; addpowershellfor your Windows scopes — e.g. aWindows-Fleetscope declaring powershell ansible). A pragma does the same for a Git-synced inventory:# cronomicon:v1 types=bash,powershell. - Host records are OS-agnostic. Address, port (22 for Windows OpenSSH too), user, optional bastion
via, and the auth key. The OS field on a host record is descriptive; execution behavior comes from the run type. - Key-based auth on Windows OpenSSH works like anywhere else, with one server-side gotcha worth knowing when Test connection says cred_error but the key is right: for members of the Administrators group, Windows OpenSSH reads authorized keys from
C:\ProgramData\ssh\administrators_authorized_keys(with restrictive ACLs), not the user profile'sauthorized_keys. - Host keys fail closed on both executors — stored-key strict match or loud TOFU on the in-app executor; the runner's
known_hosts+ Scan & approve on the agent path. Bastion hops are pinned too. Same rules, same fixes as Bash Guide §3.
4 · Env, inputs, outputs — and exit codes that tell the truth
Reading the environment
Injected environment (job env ← schedule env ← per-run overrides, plus bound references and the dispatcher's CRONOMICON_RUN_* context set) arrives as an $env: prelude ahead of your body, delivered on stdin — never argv, never /proc-visible. Read it the normal way:
$release = $env:TARGET_RELEASE # a run-input answer (§ below)
$apiKey = $env:CRONOMICON_SECRET_API_KEY # a bound secret reference
Write-Output "run $($env:CRONOMICON_RUN_ID) on scope $($env:CRONOMICON_RUN_SCOPE)"Run inputs, not Read-Host
The invocation is -NonInteractive: Read-Host, credential prompts and -Confirm dialogs don't hang — they throw (“Read and Prompt functionality is not available”). Declare operator questions as run inputs (spec.prompts) and read the answers from $env:; add -Confirm:$false / -Force to cmdlets that would otherwise prompt.
Exit codes: make failure loud
Cronomicon judges each host by the process exit code — and PowerShell's defaults are looser than Bash's. powershell -Command exits 1 on a terminating error, but non-terminating errors (most cmdlet failures) and failed native commands do not fail the run by themselves. Open every script with:
$ErrorActionPreference = 'Stop' # non-terminating errors become terminating
Set-StrictMode -Version Latest
# after any native command you care about:
some.exe --do-thing
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }That is PowerShell's set -euo pipefail. Per-host results aggregate exactly as for bash: all ok → success, all failed → failure, mixed → warning (partial), with each host's output prefixed [hostname].
Workflow outputs (A12)
Hand values to downstream workflow steps with the standard marker on stdout:
Write-Output "::cronomicon-output name=HOTFIX_KB::KB5031364"A downstream step binds an input to it and receives it as env. The output_secret_leak fail-close applies here too — never emit a bound secret as an output. See User Manual §7.5.
5 · Secrets from PowerShell
The delivery model is the shared one (Bash Guide §5): declare reference bindings on the job, receive $env:CRONOMICON_SECRET_<name> / $env:CRONOMICON_VAR_<name>; the in-app executor resolves and injects fail-closed with STDIN delivery, the runner path needs the per-runner Secret injection flag, and CRONOMICON_KEY_* material is runner-only. PowerShell-specific habits:
- Bridge to credential-taking cmdlets without echoing:
$sec = ConvertTo-SecureString $env:CRONOMICON_SECRET_SVC_PASS -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential($env:SVC_USER, $sec) - Don't re-broadcast. Redaction masks known injected values in stored logs, but a value your script fetches or decrypts itself was never registered — keep it out of
Write-Host/Write-Output, out of other processes' argv, and away from-Verbosestreams. - Nothing secret in the repo, schedule env or run overrides — plaintext by design; secrets belong in the encrypted store / Vault behind a binding.
6 · Best-practice checklist
- Open with
$ErrorActionPreference = 'Stop'+Set-StrictMode, and propagate$LASTEXITCODEafter native commands — exit codes are the contract Cronomicon reads. - Write for Windows PowerShell 5.1 compatibility when targeting stock Windows hosts (the invocation is
powershell, notpwsh) — or standardize your fleet on PowerShell 7 and aliaspowershellto it deliberately. - No prompts, ever: run inputs instead of
Read-Host;-Confirm:$false/-Forceon cmdlets that ask;-NonInteractiveturns forgotten prompts into immediate errors, which is the good outcome. - Be idempotent — retries, schedule re-fires and re-runs all mean “again”; test with
-WhatIfduring authoring, then remove it. - Pin single-host jobs with
target_host; sizetimeout_secondsto reality (a hung Windows Update call is the classic overrun);concurrency_policy: Forbidfor non-reentrant maintenance. - Keep scripts in Git behind
script_ref, lint with PSScriptAnalyzer +cronomicon validatein CI, and use::cronomicon-outputmarkers — not temp files — to pass data between workflow steps.
7 · Troubleshooting
| Symptom | Likely cause → fix |
|---|---|
Runs sit queued forever (executor ssh) | The SSH executor pool is disabled — CRONOMICON_SSH_EXECUTOR_ENABLED=true, or route to a runner. |
| Queued, “waiting for a powershell-capable runner” | No online runner claims powershell — the probe needs local pwsh; declare -capabilities explicitly for a runner that only dials PowerShell targets (§1). |
Per-host failure: powershell: command not found | A non-Windows target with only pwsh — add the powershell symlink/alias (§1). On Windows: the OpenSSH session's PATH is broken. |
| Error “Read and Prompt functionality is not available” | Something asked a question under -NonInteractive — Read-Host, a confirm, a credential dialog. Replace with run inputs / -Confirm:$false (§4). |
| Run reads Success but the work failed | A non-terminating error or a failed native command didn't move the exit code. Add the §4 preamble and the $LASTEXITCODE check. |
| cred_error on a Windows host despite a correct key | The account is an Administrator and the key isn't in administrators_authorized_keys (§3). |
Host-key mismatch / conn_error / unpinned_bastion_target / executor_lost / cronomicon: job timed out | Shared shell-family causes — see the Bash Guide §7 table; the rules are identical for PowerShell. |
For the SSH executor's internals see Administrator Manual §4; for agent-side key custody and host-key approval, Runner Security Guide.