Skip to content

<Command>

The <Command> block executes shell commands or scripts with variable substitution. It’s used for performing operations like deployments, resource creation, and system configuration.

<Command
id="trigger-deploy"
command="curl -X POST https://api.example.com/deploy"
title="Trigger Deployment"
successMessage="Deployment triggered!"
failMessage="Failed to trigger deployment"
/>

Command blocks and Check blocks share many features in common, however they each have a distinct purpose. Check blocks are focused on reading the state of the world and validating it, while Command blocks are focused on mutating the state of the world to update it to what is needed.

  • id (string) - Unique identifier for this command block
  • title (string) - Display title shown in the UI. Supports inline markdown (bold, italic, links, code).
  • description (string) - Longer description of what the command does. Supports inline markdown.
  • command (string) - Inline command to execute (alternative to path)
  • path (string) - Path to a shell script file relative to the runbook (alternative to command)
  • inputsId (string | string[]) - ID of an Inputs block to get template variables from. Can be a single ID or an array of IDs. When multiple IDs are provided, variables are merged in order (later IDs override earlier ones).
  • awsAuthId (string) - ID of an AwsAuth block for AWS credentials. The credentials are passed as environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_REGION).
  • successMessage (string) - Message shown when command succeeds (default: “Success”). Supports inline markdown.
  • failMessage (string) - Message shown when command fails (default: “Failed”). Supports inline markdown.
  • runningMessage (string) - Message shown while running (default: “Running…”). Supports inline markdown.

Instead of referencing an external <Inputs> block via inputsId, you can nest an <Inputs> component directly inside the Command:

<Command
id="echo-message"
command='echo "Hello, {{ .Name }}!"'
title="Print Greeting"
>
<Inputs id="inline-greeting">
```yaml
variables:
- name: Name
type: string
description: Your name
validations: "required"
\```
</Inputs>
</Command>

The embedded <Inputs> renders directly within the Command block, allowing users to fill in variables before running the command.

Other blocks can reference this Inputs block using the standard inputsId pattern.

Command blocks run shell scripts to perform operations like deployments, installations, and configuration changes.

Scripts can be defined inline using the command prop or stored in external files using the path prop.

When writing scripts for Command blocks:

  • Exit codes matter. Return 0 for success, any other code for failure
  • Use logging helpers. Standardized functions like log_info and log_error are available
  • Templatize with variables. Use {{ .VariableName }} syntax to inject user input

Scripts run in a non-interactive shell environment. See Execution Context for details.

You can write scripts either inline or by referencing script files.

For simple commands, you can define the script directly in the command prop:

<Command
id="deploy-trigger"
command="curl -X POST https://api.example.com/deploy"
title="Trigger Deployment"
successMessage="Deployment triggered!"
failMessage="Failed to trigger deployment"
/>

Inline scripts work best for one-liners or short commands. For anything more complex, use an external script file.

Instead of inline commands, you can reference external shell scripts:

<Command
id="deploy-app"
path="scripts/deploy.sh"
title="Deploy Application"
description="This will deploy your application to production"
successMessage="Deployment successful!"
failMessage="Deployment failed. Check the logs for details."
/>

External scripts are plain old bash scripts. The referenced script scripts/deploy.sh might look like:

#!/bin/bash
log_info "Starting deployment..."
kubectl apply -f deployment.yaml
if kubectl rollout status deployment/myapp; then
log_info "Deployment complete!"
exit 0
else
log_error "Deployment failed"
exit 1
fi

The Command block interprets your script’s exit codes as follows:

  • Exit code 0: Success ✓ (green)
  • Any other exit code: Failure ✗ (red)

These exit codes will determine how the Runbooks UI renders the result of running a script.

Runbooks provides standardized logging functions for your scripts by automatically importing a logging.sh file that defines a standardized set of Bash logging functions. Using these functions enables consistent output formatting and allows the Runbooks UI to parse log levels for filtering and export.

FunctionOutputDescription
log_info "msg"[timestamp] [INFO] msgGeneral informational messages
log_warn "msg"[timestamp] [WARN] msgWarning conditions
log_error "msg"[timestamp] [ERROR] msgError messages
log_debug "msg"[timestamp] [DEBUG] msgDebug output (only when DEBUG=true)
#!/bin/bash
log_info "Starting deployment..."
log_debug "Target environment: $ENVIRONMENT"
if [ -z "$API_KEY" ]; then
log_warn "API_KEY not set, some features may be unavailable"
fi
if ! deploy_application; then
log_error "Deployment failed"
exit 1
fi
log_info "Deployment complete"

When running scripts locally (outside the Runbooks UI), the logging functions won’t magically be pre-loaded, so if you’d like your scripts to run successfully both locally and in the Runbooks environment, copy/paste this snippet to the top of your script:

Terminal window
# --- Runbooks Logging (https://runbooks.gruntwork.io/authoring/blocks/command#logging) ---
if ! type log_info &>/dev/null; then
source <(curl -fsSL https://raw.githubusercontent.com/gruntwork-io/runbooks/main/scripts/logging.sh 2>/dev/null) 2>/dev/null
type log_info &>/dev/null || { log_info() { echo "[INFO] $*"; }; log_warn() { echo "[WARN] $*"; }; log_error() { echo "[ERROR] $*"; }; log_debug() { [ "${DEBUG:-}" = "true" ] && echo "[DEBUG] $*"; }; }
fi
# --- End Runbooks Logging ---

This snippet checks if the logging functions are already defined, attempts to fetch them from GitHub, and falls back to simple implementations if offline.

There are several ways to collect variables to customize a command or script.

The Command’s command or script pulls its values from a separate Inputs block.

<Inputs id="repo-config">
```yaml
variables:
- name: OrgName
type: string
description: GitHub organization name
- name: RepoName
type: string
description: Repository name
\```
</Inputs>
<Command
id="create-repo"
command="gh repo create {{ .OrgName }}/{{ .RepoName }} --private"
inputsId="repo-config"
title="Create GitHub Repository"
successMessage="Repository {{ .RepoName }} created!"
failMessage="Failed to create repository"
/>

The Command collects input values directly. These values can be shared with other blocks, just like a standalone Inputs block.

<Command
id="echo-message"
command='echo "Hello, {{ .Name }}!"'
title="Print Greeting"
>
<Inputs id="inline-greeting">
```yaml
variables:
- name: Name
type: string
description: Your name
validations: "required"
\```
</Inputs>
</Command>

You can reference multiple Inputs blocks by passing an array of IDs. Variables are merged in order, with later IDs overriding earlier ones:

<Inputs id="lambda-config" templatePath="templates/lambda" />
<Inputs id="repo-config">
```yaml
variables:
- name: GithubOrgName
type: string
description: GitHub organization name
- name: GithubRepoName
type: string
description: Repository name
\```
</Inputs>
<Command
id="deploy-lambda"
path="scripts/deploy.sh"
inputsId={["lambda-config", "repo-config"]}
title="Deploy Lambda Function"
description="Deploy the Lambda function using variables from both inputs"
/>

In this example, the command has access to all variables from both lambda-config and repo-config. If both define a variable with the same name, the value from repo-config (the later ID) takes precedence.

Scripts run in a persistent environment — environment variable changes (export, unset) and working directory changes (cd) carry forward to subsequent blocks. This lets you structure your runbook like a workflow where earlier steps set up the environment for later steps.

Scripts also run in a non-interactive shell, which means shell aliases (like ll) and shell functions (like nvm, rvm) are not available.

For full details, see Shell Execution Context.

Let’s take a look at some example scripts:

scripts/deploy.sh
#!/bin/bash
set -e # Exit on error
log_info "Starting deployment..."
kubectl apply -f deployment.yaml
kubectl rollout status deployment/myapp
log_info "Deployment complete!"
scripts/create-vpc.sh
#!/bin/bash
REGION="{{ .AwsRegion }}"
VPC_NAME="{{ .VpcName }}"
CIDR_BLOCK="{{ .CidrBlock }}"
log_info "Creating VPC $VPC_NAME in $REGION..."
log_debug "CIDR block: $CIDR_BLOCK"
aws ec2 create-vpc \
--cidr-block "$CIDR_BLOCK" \
--tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=$VPC_NAME}]" \
--region "$REGION"
log_info "VPC created successfully!"
scripts/safe-deploy.sh
#!/bin/bash
set -e
function cleanup {
log_info "Cleaning up..."
# Cleanup code here
}
trap cleanup EXIT
log_info "Running pre-deployment checks..."
./check-prerequisites.sh || { log_error "Pre-checks failed"; exit 1; }
log_info "Deploying..."
./deploy.sh
log_info "Running post-deployment validation..."
./validate-deployment.sh || { log_error "Validation failed"; exit 1; }
log_info "Deployment successful!"

Commands can save files to the generated files directory using the $RUNBOOKS_OUTPUT environment variable. Any files written to this directory will automatically appear in the file panel after the command completes successfully.

#!/bin/bash
# Export OpenTofu outputs to generated files
tofu output -json > "$RUNBOOKS_OUTPUT/tf-outputs.json"
# Copy a config file
cp config.yaml "$RUNBOOKS_OUTPUT/"

Or as an inline command:

<Command
id="create-file"
title="Create a greeting file"
command={`echo "Hello from the command!" > "$RUNBOOKS_OUTPUT/greeting.txt"`}
successMessage="File created successfully!"
/>

You can create subdirectories within $RUNBOOKS_OUTPUT to organize your captured files:

#!/bin/bash
mkdir -p "$RUNBOOKS_OUTPUT/terraform"
mkdir -p "$RUNBOOKS_OUTPUT/config"
tofu output -json > "$RUNBOOKS_OUTPUT/opentofu/outputs.json"
echo '{"env": "production"}' > "$RUNBOOKS_OUTPUT/config/settings.json"

This creates:

generated/
├── opentofu/
│ └── outputs.json
└── config/
└── settings.json
  1. Before your script runs, Runbooks creates a temporary capture directory
  2. The $RUNBOOKS_OUTPUT environment variable points to this directory
  3. Your script writes files to $RUNBOOKS_OUTPUT
  4. After successful execution, files are copied to the generated files directory
  5. The temporary directory is cleaned up

The <Command> block works especially well for mutating the world to a desired state. This could be either the user’s local environment, the company’s world, or the external world.

This might manifest as:

  • Installing tools: Install tools needed to execute the runbook
  • Configure environment: Configure the user’s environment
  • Provisioning resources: Hit an API to provision resource.
  • Deployments: Deploy applications or infrastructure to cloud environments
  • Database Operations: Run migrations or seed data
  • Build Steps: Compile code or build Docker images