Not everything you need is a tool. A step can run an arbitrary shell command with
ssh.cmd, or upload and execute a script with ssh.script.file. Both are deliberately
available and deliberately weaker than a named tool.
The trade-off, stated plainly#
A named tool declares what it does, so Opafra can reason about it. A raw command is opaque text, so it cannot.
| Named tool | Raw command | |
|---|---|---|
| Dry run | Can predict the outcome on 15 of them | Shows you the resolved text, predicts nothing |
| Changed vs unchanged | Reported | Unknown; an exit code is not a change report |
| Idempotent | Where the tool says so | Only if you wrote it that way |
| Failure | Reported per host | Reported per host |
This is not a limitation Opafra could engineer away. apt-get install -y nginx and
rm -rf /var/lib/mysql are both a string, and nothing can tell you what an arbitrary
string will do to a host without running it.
Prefer a tool where one fits#
The clearest example is a line in a config file:
- name: Disable password auth
tool: file.lineinfile
args:
path: /etc/ssh/sshd_config
regexp: "^#?PasswordAuthentication"
line: "PasswordAuthentication no"- name: Disable password auth
tool: ssh.cmd
args:
command: echo "PasswordAuthentication no" >> /etc/ssh/sshd_configBoth work the first time. The second appends a duplicate line every subsequent run, and after four runs the file has four copies. The first is idempotent, its dry run reads the current file and shows you the diff, and rerunning it reports no change.
The tool catalogue lists everything available. Reach for a command when nothing there fits, not by default.
Running a command#
- name: Check the queue depth
targets: [worker-01]
tool: ssh.cmd
args:
command: "systemctl show -p NRestarts {{ vars.service }}"Variables are substituted before the command is sent. Capture its output if a later step needs it, rather than running it twice:
capture:
var: restarts
mode: regex
pattern: "NRestarts=([0-9]+)"Running a script#
Scripts live in Opafra, not on the host. A step names one, and Opafra uploads it, runs it, and removes it afterwards.
- name: Run the migration check
targets: [db-01]
tool: ssh.script.file
args:
script_name: migration-precheck
script_args: "--verbose"Cleanup is the default, so the host is left as it was found. A script is stored per
tenant, so two organisations can each have a deploy script without collision.
Prefer a script over a very long ssh.cmd for anything past a few lines. A script is
reviewable, reusable across plans, and its body appears in the dry-run preview so an
approver can read what would run.
Privilege#
A command runs as the user the server was registered with. Where it needs more, use
sudo in the command, and remember that sudo must not prompt: a run cannot answer
a password prompt and will hang until it times out rather than succeeding.
Secrets in a command#
Reference a secret rather than pasting one:
command: "mysql -u root -p'secret://db/root_password' -e 'FLUSH LOGS'"The reference is resolved at the moment of use. It is redacted in the dry-run preview, in the run log, and in the execution record, so the plan stays safe to read and safe to keep in git.
What a raw command still gives you#
It is worth being clear that dropping to a command does not drop out of everything. A raw step is still targeted per host, still subject to the approval gate when the environment demands it, still recorded per host in the run record, and still halts the run on failure.
What you lose is prediction and change reporting, not custody.
Next steps#
- Tool catalogue to check whether a tool already covers it
- Dry runs for what a preview of a raw command can show