ReferenceResources
exec
Run arbitrary commands on remote hosts.
The exec resource runs commands on the remote host via SSH. It is imperative by default, with optional guard conditions (unless / onlyIf) for conditional execution.
Input
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
command | string | — | Yes | The command to run |
sudo | boolean | false | No | Run with sudo sh -c |
cwd | string | — | No | Working directory |
env | Record<string, string> | — | No | Environment variables |
check | boolean | true | No | If false, non-zero exit codes are not failures |
unless | string | — | No | Skip apply when this guard command exits 0 |
onlyIf | string | — | No | Run apply only when this guard command exits 0 |
Output
| Field | Type | Description |
|---|---|---|
exitCode | number | Exit code of the command |
stdout | string | Standard output |
stderr | string | Standard error |
Behavior
Check phase
Without guards, check() returns inDesiredState: false (imperative behavior).
With guards:
unless: if guard exits0, resource is treated as already in desired state (apply is skipped)onlyIf: if guard exits non-zero, precondition is not met and apply is skippedunlessandonlyIfare mutually exclusive- Guard commands are executed during
check()itself, includingignition run --check, so only use read-only guards if you need dry-run safety
Apply phase
- Builds the command string:
- Prepends environment variables as
KEY=VALUEpairs - Wraps with
cd <cwd> &&ifcwdis specified - Wraps with
sudo sh -c '...'ifsudois true
- Prepends environment variables as
- Executes the command via SSH.
- If the command exits with non-zero and
checkistrue, throwsSSHCommandError. - If
checkisfalse, the exit code is captured but not treated as a failure.
Annotations
| Property | Value |
|---|---|
| Nature | Imperative |
| Idempotent | No |
| Destructive | Yes |
| Read-only | No |
| Required capabilities | exec |
Examples
Basic command
await exec({ command: "nginx -t" })With sudo
await exec({ command: "systemctl daemon-reload", sudo: true })With working directory
await exec({ command: "npm install --production", cwd: "/opt/app" })With environment variables
await exec({
command: "npm run build",
cwd: "/opt/app",
env: { NODE_ENV: "production", CI: "true" },
})Tolerating non-zero exit codes
await exec({ command: "grep -q 'done' /tmp/flag", check: false })With check: false, the resource won't fail even if the command returns a non-zero exit code. The exit code is still captured in the output.
Guarded execution
await exec({
command: "npm install -g pm2",
unless: "command -v pm2",
sudo: true,
})Capturing output
const result = await exec({ command: "cat /etc/hostname" })
const hostname = result.output?.stdout.trim()Gotchas
- Unguarded
execalways reportschangedbecause it can't determine whether the command mutated state. - In check mode (
ignition run --check), unguarded exec reports as "would change" since it's imperative. unlesscan reportokwhen the guard shows the desired state is already met.onlyIfcan also reportok, but that means the precondition failed and apply was skipped.unlessandonlyIfguards run duringcheck()and inignition run --check.- Set
check: falsefor commands where a non-zero exit code is acceptable (e.g.,grepreturning 1 for no match). sudowraps the entire command withsudo sh -c '...'— shell features like pipes and redirects work inside.- Command output (stdout/stderr) can be streamed in real time when trace mode is enabled.