Exec

The exec resource executes commands to bring the system into the desired state. It is idempotent when used with the creates, onlyif, or unless properties, or refreshonly mode.

Warning

Specify commands with their full path, or use the path property to set the search path.

- exec:
    - /usr/bin/touch /tmp/hello:
        creates: /tmp/hello
        timeout: 30s
        cwd: /tmp

Alternatively for long commands or to improve UX for referencing execs in require or subscribe:

- exec:
    - touch_hello:
        command: /usr/bin/touch /tmp/hello
        creates: /tmp/hello
        timeout: 30s
        cwd: /tmp
ccm ensure exec "/usr/bin/touch /tmp/hello" --creates /tmp/hello --timeout 30s
{
  "protocol": "io.choria.ccm.v1.resource.ensure.request",
  "type": "exec",
  "properties": {
    "name": "/usr/bin/touch /tmp/hello",
    "creates": "/tmp/hello",
    "timeout": "30s",
    "cwd": "/tmp"
  }
}

The command runs only if /tmp/hello does not exist.

Providers

The exec resource supports two providers:

ProviderDescription
posixDefault. Executes commands directly without a shell. Arguments are parsed and passed to the executable.
shellExecutes commands via /bin/sh -c "...". Use this for shell features like pipes, redirections, and builtins.

The posix provider is the default and is suitable for most commands. Use the shell provider when you need shell features:

- exec:
    - cleanup-logs:
        command: find /var/log -name '*.log' -mtime +30 -delete && echo "Done"
        provider: shell

    - check-service:
        command: systemctl is-active httpd || systemctl start httpd
        provider: shell

    - process-data:
        command: cat /tmp/input.txt | grep -v '^#' | sort | uniq > /tmp/output.txt
        provider: shell
Note

The shell provider passes the entire command string to /bin/sh -c, so shell quoting rules apply. The posix provider parses arguments using shell-like quoting but does not invoke a shell.

Properties

PropertyDescription
nameThe command to execute (used as the resource identifier)
commandAlternative command to run instead of name
cwdWorking directory for command execution
environment (array)Environment variables in KEY=VALUE format
pathSearch path for executables as a colon-separated list (e.g., /usr/bin:/bin)
returns (array)Exit codes indicating success (default: [0])
timeoutMaximum execution time (e.g., 30s, 5m); command is killed if exceeded
createsFile path; if this file exists, the command does not run
onlyifGuard command; the exec runs only if this command exits 0
unlessGuard command; the exec runs only if this command exits non-zero
refreshonly (boolean)Only run when notified by a subscribed resource
subscribe (array)Resources to subscribe to for refresh notifications (type#name or type#alias)
logoutput (boolean)Log the command output
providerForce a specific provider (posix or shell)

Guard commands

The onlyif and unless properties act as guard commands that control whether the exec runs. They are evaluated before execution and share the exec’s cwd, environment, and path settings. Guard commands run even in noop mode to accurately report what would happen.

When creates is also set, it takes precedence: if the creates file exists, the command is skipped regardless of guard results. Subscribe-triggered refreshes override all guards.

- exec:
    - install-app:
        command: /usr/local/bin/install-app.sh
        onlyif: test -f /tmp/app-package.tar.gz
        # Runs only if the package file exists

    - configure-firewall:
        command: /usr/sbin/iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
        unless: /usr/sbin/iptables -C INPUT -p tcp --dport 8080 -j ACCEPT
        # Runs only if the iptables rule does not already exist
# Runs only if the package file exists
ccm ensure exec /usr/local/bin/install-app.sh --exec-if "test -f /tmp/app-package.tar.gz"

# Runs only if the iptables rule does not already exist
ccm ensure exec "/usr/sbin/iptables -A INPUT -p tcp --dport 8080 -j ACCEPT" \
  --exec-unless "/usr/sbin/iptables -C INPUT -p tcp --dport 8080 -j ACCEPT"
{
  "protocol": "io.choria.ccm.v1.resource.ensure.request",
  "type": "exec",
  "properties": {
    "name": "install-app",
    "command": "/usr/local/bin/install-app.sh",
    "onlyif": "test -f /tmp/app-package.tar.gz"
  }
}
{
  "protocol": "io.choria.ccm.v1.resource.ensure.request",
  "type": "exec",
  "properties": {
    "name": "configure-firewall",
    "command": "/usr/sbin/iptables -A INPUT -p tcp --dport 8080 -j ACCEPT",
    "unless": "/usr/sbin/iptables -C INPUT -p tcp --dport 8080 -j ACCEPT"
  }
}