Skip to main content

!exec

The !exec Atmos YAML function is used to execute shell scripts and assign the results to the sections in Atmos stack manifests.

Availability and evaluation​

Supported locations:

  • CLI configuration (atmos.yaml, atmos.d, and profiles): during configuration loading.
  • Resolved stack values: after inheritance, merging, and component template processing.
  • Workflow prompt fields: default, prompt, placeholder, and options where supported by the input, confirm, choose, filter, file, and write step types, before the step executes.

Automatic evaluation requires native YAML tags from edition 2026-10-20, including unpinned projects. Quoted function strings remain literal; earlier editions retain legacy string-form behavior.

See YAML function scope for field restrictions, import timing, and native tags versus quoted strings.

Usage​

var1: !exec echo 42

var2: !exec get-data.sh

var3: !exec atmos terraform output <component> -s <stack> --skip-init -- -json test_label_id

var4: !exec atmos terraform output <component> -s {{ .stack }} --skip-init -- -json test_map

var5: !exec atmos terraform output <component> -s {{ .stack }} --skip-init -- -json test_list

var6: |
!exec
foo=0
for i in 1 2 3; do
foo+=$i
done
echo $foo

You can execute inline shell scripts, scripts defined in external files, or even Atmos or Terraform commands (e.g. !exec atmos terraform output).

warning

!exec atmos terraform output is just as examples to show that you can get the outputs of an Atmos component using the !exec YAML function, but it's not recommended to use. Instead, use the !terraform.output YAML function. It produces the same results, correctly handles complex types (lists and maps), has a much simpler syntax, and automatically caches the results for the same component and stack (so if you are calling it many times on the same component in the same stack, it will execute terraform output only once and the stack processing will be much faster)

The result of a script execution can be a simple type (string, number, or boolean), in which case Atmos assigns it without modification.

If the result is a complex type (list, map or object), the script must return it as a JSON-encoded string. After receiving the JSON-encoded string, Atmos automatically decodes it into the corresponding YAML complex type.

info

Atmos uses the interp Go package to execute the shell scripts in the !exec YAML function.

Package interp implements an interpreter that executes shell programs. It aims to support POSIX and to behave like Bash, but it does not support all of its features.

tip

You can use Atmos Stack Manifest Templating in the !exec YAML function expressions. In stack component values, Atmos processes templates before executing the !exec function, allowing you to provide the parameters to the function dynamically.

Concurrent execution​

Configure settings.functions.exec.max_concurrency in atmos.yaml to allow multiple ordinary !exec expressions in a document to run concurrently:

settings:
functions:
exec:
max_concurrency: 4

Your stack entries stay unchanged:

vars:
network: !exec ./lookup-network
cluster: !exec ./lookup-cluster
registry: !exec ./lookup-registry

The limit defaults to 1 for serial execution and must be a positive integer. With a higher limit, calls can finish in any order; results retain their original YAML locations and types. Atmos does not detect shell side-effect dependencies. Users enabling concurrency are responsible for coordinating shared files and other external state. Skipped !exec expressions remain unexecuted.

Commands inherit Atmos standard input. Concurrent commands share that input stream; input is not broadcast to every command, and which command reads each part is not guaranteed. Coordinate commands that read stdin, or set the limit to 1 when they must read serially.

Type-Aware Merging

Atmos supports type-aware merging of YAML functions and concrete values, allowing them to coexist in the inheritance chain without type conflicts. See the full explanation: YAML Function Merging