Skip to content

Latest commit

 

History

History
251 lines (188 loc) · 8.72 KB

File metadata and controls

251 lines (188 loc) · 8.72 KB
title !env
sidebar_position 5
sidebar_label !env
sidebar_class_name command
description Retrieve environment variables from stack manifests or OS environment and assign them to sections in Atmos stack manifests

import Intro from '@site/src/components/Intro'

The `!env` Atmos YAML function is used to retrieve environment variables from stack manifest `env:` sections or OS environment variables, and assign them to sections in Atmos stack manifests.

Usage

The !env function can be called with either one or two parameters:

  # Get the value of an environment variable.
  # If the environment variable is not present in the environment, `null` will be assigned
  !env <env-var-name>

  # Get the value of an environment variable.
  # If the environment variable is not present in the environment, the `default-value` will be assigned
  !env <env-var-name> <default-value>

Arguments

`env-var-name`
Environment variable name
<dt>`default-value`</dt>
<dd>(Optional) Default value to use if the environment variable is not present in the environment</dd>

If the function is called with one argument (the name of the environment variable), and the environment variable is not present, null will be assigned to the corresponding section in the Atmos manifest.

If the function is called with two arguments (the name of the environment variable and the default value), and the environment variable is not present, the default value will be assigned to the corresponding section in the Atmos manifest.

Resolution Order

The !env function follows this resolution order when looking up environment variable values:

  1. Stack manifest env: sections - Checks the component's merged env: section (which includes values from globals, imports, base components, and the component itself)
  2. OS environment variables - Falls back to operating system environment variables if not found in stack manifests
  3. Default value - Uses the provided default value if the variable is not found in either location

This means you can define environment variables in your stack manifests and reference them using !env:

# globals.yaml
env:
  APP_ENVIRONMENT: "production"
  API_BASE_URL: "https://api.example.com"

# In your component stack
components:
  terraform:
    my-app:
      env:
        # This will resolve to "production" from globals.yaml
        ENVIRONMENT: !env APP_ENVIRONMENT

        # This will resolve to "https://api.example.com" from globals.yaml
        API_URL: !env API_BASE_URL

:::info The env: section follows the standard Atmos merge priority: GlobalEnv < BaseComponentEnv < ComponentEnv < ComponentOverridesEnv

This means values defined at the component level will override values from globals. :::

Deferred Evaluation During Merge

Atmos uses deferred evaluation when processing YAML functions during configuration merging. This prevents type conflicts that can occur when inheriting and overriding values across multiple stack layers.

The Problem: Type Conflicts

Without deferred evaluation, mixing concrete values and YAML functions in the inheritance chain would cause errors:

# Base catalog (catalog/app/defaults.yaml)
components:
  terraform:
    my-app:
      vars:
        database_url: "postgresql://localhost:5432/mydb"  # Concrete string value

# Environment override (stacks/prod/app.yaml)
components:
  terraform:
    my-app:
      vars:
        database_url: !env DATABASE_URL  # YAML function - different type!

In this scenario, Atmos would attempt to merge a string ("postgresql://localhost:5432/mydb") with a YAML function reference during the configuration merge process, resulting in a type mismatch.

The Solution: Deferred Merge

Atmos now defers the evaluation of YAML functions (including !env, !terraform.output, !terraform.state, !template, !store, !store.get, and !exec) until after all configuration layers have been merged. This three-phase approach eliminates type conflicts:

  1. Defer Phase - YAML functions are identified and temporarily replaced with placeholders
  2. Merge Phase - All configuration layers merge without type conflicts
  3. Evaluate Phase - YAML functions are evaluated and applied to the final merged result

With deferred evaluation, the example above works seamlessly. The YAML function in the override layer is deferred during merge, then evaluated after merging completes, correctly replacing the base value.

Benefits

  • Flexible Configuration Patterns - Mix static values and YAML functions across inheritance layers without conflicts
  • Gradual Migration - Migrate from static to dynamic configurations incrementally
  • Team Collaboration - Different teams can use different approaches (static vs. templated) in their layers
  • Multi-Environment Support - Use static values in dev/staging and YAML functions in production

Limitations

Single-Pass Processing: YAML functions are processed in a single pass. This means !env cannot reference environment variables that are defined by other YAML functions in the same component section.

For example, this will NOT work:

components:
  terraform:
    my-app:
      env:
        # FIRST_LEVEL resolves correctly from globals
        FIRST_LEVEL: !env FOO

        # ❌ SECOND_LEVEL will NOT resolve - it will remain as the literal string "!env FOO"
        # because FIRST_LEVEL hasn't been processed yet when this is evaluated
        SECOND_LEVEL: !env FIRST_LEVEL

However, you can reference env variables from parent contexts (globals, imports, base components):

# globals.yaml
env:
  FOO: "bar"

# component stack
components:
  terraform:
    my-app:
      env:
        # ✅ This works - FOO exists in the merged env section from globals
        MY_VAR: !env FOO

Examples

Using Stack Manifest Environment Variables

# globals.yaml
env:
  DATABASE_HOST: "db.prod.example.com"
  DATABASE_PORT: "5432"

# dev.yaml
components:
  terraform:
    my-service:
      env:
        # Reference env variables defined in globals
        DB_HOST: !env DATABASE_HOST
        DB_PORT: !env DATABASE_PORT
      vars:
        # Can also use in vars section
        connection_string: !env DATABASE_HOST

Using OS Environment Variables

vars:
  # `api_key` will be set to `null` if the OS environment variable `API_KEY` is not present
  api_key: !env API_KEY
  # `app_name` will be set to the default value `my-app` if the OS environment variable `APP_NAME` is not present
  app_name: !env APP_NAME my-app

settings:
  # `provisioned_by_user` will be set to `null` if the OS environment variable `ATMOS_USER` is not present
  provisioned_by_user: !env ATMOS_USER

Mixed Usage (Stack Manifests + OS Environment)

# globals.yaml
env:
  REGION: "us-east-1"

# component stack
components:
  terraform:
    my-app:
      env:
        # From stack manifest globals
        AWS_REGION: !env REGION

        # From OS environment (with default)
        AWS_PROFILE: !env AWS_PROFILE default-profile

Handling default values with spaces

If you need to provide default values with spaces, enclose them in double quotes and use single quotes around the whole expression.

For example:

  # `app_name` will be set to the default value `my app` if the environment variable `APP_NAME` is not present in the environment
  app_name: !env 'APP_NAME "my app"'

  # `app_description` will be set to the default value `my app description` if the environment variable `APP_DESCRIPTION` is not present in the environment
  app_description: !env 'APP_DESCRIPTION "my app description"'

Handling Nested Quotes

If your default value contains both single and double quotes, wrap the entire function call in single quotes while keeping any required double quotes inside the expression:

# Use single quotes to wrap expressions containing double quotes
config_path: !env 'CONFIG_PATH "/etc/app/config.json"'

# Keep default values with mixed quotes readable by surrounding the expression with single quotes
api_endpoint: !env 'API_URL "https://api.example.com/v1"'

:::tip Detailed escaping guidance (including bracket-notation examples) lives in the !terraform.output documentation. The same rules apply to !env because all YAML functions share the parser. :::

:::tip You can use Atmos Stack Manifest Templating in the environment variable names and default values when calling the !env YAML function. Atmos processes the templates first, and then executes the !env function, allowing you to provide the parameters to the function dynamically. :::