Back to all blog posts
Guide

How to configure and run sequences of commands in Stacks using Terramate Scripts

Terramate scripts allow you to specify a recurring sequence of commands to execute, e.g., a deployment, specifying specific options for the cloud. Scripts are especially useful for defining custom commands in Terramate that can be reused among environments and platforms without repeating expressive logic.

Picture of Soren Martius
Sören Martius
· 4 min read
How to configure and run sequences of commands in Stacks using Terramate Scripts

When using Terramate CLI, you often encounter scenarios where you want to execute a sequence of commands in stacks. Either locally or in CI/CD pipelines.

For example, a typical sequence for a Terraform pipeline could look like this:

  • terraform init
  • terraform validate
  • tfsec .
  • terraform apply -auto-approve

This requires you to invoke multiple terramate run commands. The following will execute each step in our defined workflow in stacks:

  • terramate run terraform init
  • terramate run terraform validate
  • terramate run tfsec .
  • terraform run terraform apply -auto-approve

Terramate CLI would first invoke terraform init in all stacks, then continue with terraform validate , and so on. To run the full sequence of commands in each stack, they could be executed in a shell:

terramate run -- sh -c 'terraform init && terraform validate && tfsec . && terraform apply -auto-approve'

The example above would execute the sequence of commands in the first stack before moving on to the next (sequential execution of commands in stacks).

To simplify this, we added a new feature to Terramate called Terramate Scripts, allowing developers to create and execute such sequences of commands conveniently.

Defining scripts

A script in Terramate CLI is defined in a script block placed in any Terramate configuration file on any level of your project hierarchy. The script block has the following syntax:

script "command" "subcommand" { # any level of subcommands is supported
  description = "Execute commands"
  job {
    commands = [
      ["echo", "-n", "Hello"],
      ["echo", " World!"],
    ]
  } 
}

The list of labels of the script block defines the arguments you need to provide to the terramate script run command to execute the script: terramate script run command subcommand . You can define any number of labels.

Attributes and Blocks Reference of the script block

  • description (optional) - A description of the jobs being executed
  • job (required) - One or more blocks, each defining a sequence of commands to be executed in the script. Jobs are executed in the order of definition.

Each job can have the following attributes:

  • commands (required) - A list of commands. Each item is a list has the form [ command, arg1, arg2, ...] that will be executed accordingly. Terramate Functions and variable interpolation of the following namespaces are supported: global , terramate , and env

To run a Terraform deployment, a script can be defined as:

script "deploy" {
  description = "Run a Terraform deployment"
  job {
    commands = [
      ["terraform", "init"],
      ["terraform", "validate"],
      ["tfsec", "."],
      ["terraform", "apply", "-auto-approve"],
    ]
  }
}

Running Scripts

Terramate scripts are run with terramate script run <command...> , which will execute the defined job commands in the context of each stack directory.

Scripts can be defined on any hierarchy level and follow the inheritance rules of Globals and other Terramate features. When calling a script, it will only be executed on stacks that define or inherit the script definition. They are reachable from the current working directory and unaffected by filters. Scripts can be redefined on any hierarchy level and replace the previous definition.

Note: No error or warning will be issued for stacks not selected for script execution.

A more detailed explanation is covered in the Scope and Visibility sections, where

  • Scope defines where a script will be executed in
  • Visibility describes where a script can be called from

Scope

In general, the scope of a script definition includes the directory it is defined in and all nested sub-directories. A script can be run on stacks within its scope.

Figure A shows an example by highlighting the scope of a script named cmd:

Scope of ScriptsWhich stacks are selected further depends on additional criteria. Most importantly, the directory terramate script run is executed from limits the selected stacks to those contained within. This works just like terramate run .

A useful property of scripts is that they allow multiple definitions with the same name at different locations, yet all can be run with a single terramate script run invocation. For example, as shown in Figure B, terramate script run cmd at the root level, each of the two script definitions on stacks within their respective scopes.

As shown in Figure C, a script that is re-defined in a sub-directory overriding its previous definition will only be executed in stacks contained within the sub-directory, while the other stacks execute the original definition of the script.

This behavior enables scenarios where a single terramate script run deploy could execute different scripts for different parts of the infrastructure, based on directory grouping.

Visibility

To call a script, it needs to be visible to the current working directory.

Scripts are generally visible within their scope, as defined in the last section, but additionally, they are also visible to all their parent directories.

This enables us to call a script from the project root without knowing where it is actually defined, as shown in Figure D.

Visibility of ScriptsInspecting scripts

To inspect the behavior of a script, a dry-run can be started with script run --dry-run <command...> . This will print out the commands executed per stack without actually executing them.

Besides this, there are additional experimental commands that can be used to inspect and debug script behavior:

  • terramate script list Shows a list of all uniquely named scripts visible in the current directory, their description, and the root directory of their scope. If multiple definitions have the same name, a parent is selected over a child or a first sibling over a later sibling (ordered by directory name).
  • terramate script info <scriptname> Shows a detailed list of definitions for a given script name. This list includes the jobs and the stacks within the scope of each definition. As with the command, this information is always relative to the current directory.
  • terramate script tree Shows a tree view of all scripts relative to the current directory. The tree expands all sub-directories and the parent path to the project root, showing script definitions per directory.

Summary

Terramate scripts allow you to specify a recurring sequence of commands to execute, e.g., a deployment, specifying specific options for the cloud. Scripts are especially useful for defining custom commands in Terramate that can be reused among environments and platforms without repeating expressive logic.

Join our Community

Your feedback is very valuable for us and the future development of Terramate. We’d love you to join our Discord Community, which is the best way to give us feedback or ask questions regarding Terramate.

Soren is a co-founder and Chief Product Officer of Terramate. Before founding Terramate, he built cloud platforms for some of Europe's fastest-growing scaleups.