Skip to content

    Commands and scripts

    A step can run raw shell instead of a tool. It is always available, and it costs you the preview, the change report and idempotence.

    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 toolRaw command
    Dry runCan predict the outcome on 15 of themShows you the resolved text, predicts nothing
    Changed vs unchangedReportedUnknown; an exit code is not a change report
    IdempotentWhere the tool says soOnly if you wrote it that way
    FailureReported per hostReported 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:

    do this
    - name: Disable password auth
      tool: file.lineinfile
      args:
        path: /etc/ssh/sshd_config
        regexp: "^#?PasswordAuthentication"
        line: "PasswordAuthentication no"
    not this
    - name: Disable password auth
      tool: ssh.cmd
      args:
        command: echo "PasswordAuthentication no" >> /etc/ssh/sshd_config

    Both 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