Skip to main content

Sharing Data and Outputs Between Components

Breaking up your infrastructure into loosely-coupled components is a great way to manage complexity and reuse code. However, these smaller components often need to share data with each other. Atmos provides several ways to easily share outputs, configurations, and settings between components.

You will learn

  • Read Terraform/OpenTofu outputs with state functions and CloudFormation Outputs with !aws.cloudformation.output
  • Share persisted values across tools with external stores
  • Read component configuration with template functions and pass results between workflow steps with named outputs

Why Components Need to Share State​

Imagine building a typical web application infrastructure:

  1. VPC creates the network and outputs vpc_id
  2. Subnets need the vpc_id to create subnets
  3. EKS cluster needs subnet IDs to launch in the right network
  4. Application needs the EKS cluster endpoint to deploy

Each component depends on outputs from previous components. This is called a loosely coupled architecture---components are independent but can reference each other's outputs.

For Terraform/OpenTofu outputs with a supported backend, prefer the !terraform.state YAML function. It reads outputs directly from your Terraform backend without initializing Terraform, downloading providers, or generating configuration files. See its backend support and limitations before choosing it for your deployment.

Terraform CLI lookups can be very slow at scale

!terraform.output and atmos.Component for Terraform/OpenTofu components use the Terraform execution path. An uncached lookup without a static remote-state backend prepares the component, checks initialization, selects its workspace, and runs terraform output. When initialization is required, it can download modules and providers. Repeating that work across many components or stacks can dominate configuration processing time.

atmos.Component eagerly retrieves outputs even when your template only reads .vars or .settings. Both functions cache equivalent lookups within a single Atmos command, but the output cache does not persist across commands. Automatic initialization can skip an unchanged init; output subprocesses and backend reads still remain. Prefer direct state reads for deployed outputs and shared configuration through imports or inheritance for reusable settings.

stacks/dev.yaml
components:
terraform:
vpc:
vars:
cidr_block: "10.0.0.0/16"

eks:
vars:
# Read VPC outputs directly from the state backend
vpc_id: !terraform.state vpc vpc_id
subnet_ids: !terraform.state vpc private_subnet_ids

Why !terraform.state is recommended:

  • Avoids Terraform initialization overhead when reading outputs
  • No provider downloads - reads state files directly from backends (S3, GCS, Azure, local)
  • Same syntax as !terraform.output - easy migration
  • Automatic caching - repeated calls are served from memory
Learn More About !terraform.state

Read Terraform/OpenTofu outputs directly from supported state backends. Get Started

Alternative Methods​

Choose based on the producer and the lifetime of the data. A component's configuration, its deployed outputs, and a command's result are different things:

MethodData sourceWhen to use it
!terraform.stateTerraform/OpenTofu stateRead deployed outputs directly from a supported backend
!terraform.outputTerraform/OpenTofu CLIUse when the engine's output handling is required; account for initialization and subprocess overhead
!aws.cloudformation.outputDeployed CloudFormation stackRead a native aws/cloudformation component's Outputs
atmos.ComponentResolved component configuration; deployed outputs for supported typesRead component data in templates; Terraform targets eagerly load outputs even for configuration-only access
Remote State ModuleTerraform state through HCLKeep output lookups inside Terraform code
!storeExternal key-value storePublish and consume persisted values across tools and deployments
Named step outputsResults of earlier workflow stepsPass values within a workflow run, without assuming persistence between runs

For sensitive values, use secret stores and !secret. An ordinary store or captured step result is not automatically a secret-handling mechanism.

Quick Comparison​

!terraform.state - Reads directly from state backends. Fastest option.

vpc_id: !terraform.state vpc vpc_id

!terraform.output - Prepares the component, initializes Terraform when needed, selects its workspace, and runs terraform output for an uncached non-static lookup. This can be substantially slower than reading state directly.

vpc_id: !terraform.output vpc vpc_id

Remote State Module - Native Terraform approach using the remote-state module in HCL.

module "vpc" {
source = "cloudposse/stack-config/yaml//modules/remote-state"
component = "vpc"
context = module.this.context
}

!store - Read from external key-value stores.

vpc_id: !store ssm/prod vpc vpc_id

CloudFormation Outputs​

A Terraform, Helmfile, or other component can consume a CloudFormation Output in its stack configuration:

components:
terraform:
app:
vars:
vpc_id: !aws.cloudformation.output network shared-network VpcId

Here, network is the native aws/cloudformation component in the shared-network Atmos stack. Deploy the producer before reading its Outputs. See the function reference for authentication, supported deployed-stack states, and output value types.

Configuration and Template Functions​

atmos.Component can read another component's resolved configuration:

vars:
region: '{{ (atmos.Component "network" .stack).vars.region }}'

Configuration values and deployed outputs are different data, but atmos.Component eagerly loads Terraform/OpenTofu outputs before returning either. The example above can therefore incur Terraform initialization and output retrieval just to read region. For reusable settings, prefer shared variables through imports and component inheritance. Use !terraform.state for deployed Terraform outputs when direct backend access is suitable; it does not replace configuration access. For native CloudFormation components, .outputs uses the CloudFormation integration instead of Terraform.

Other Component Types and Workflow Results​

For integrations without a native deployed-output reader, publish non-sensitive values to an external store using supported hooks or your tool's commands. Consumers can read those values in later runs. Publishing is an explicit part of the producer's workflow.

When the value is needed only by later steps in the same run, use named step outputs:

workflows:
report:
steps:
- name: discover
type: shell
command: printf 'https://api.example.com'
outputs:
endpoint: "{{ .value }}"
- name: summary
type: markdown
content: 'Endpoint: {{ .steps.discover.outputs.endpoint }}'

These results belong to the workflow run. They are not a persistent registry of deployed component outputs.

Cross-Stack References​

Deployed-output functions can reference producers in other Atmos stacks. For example, Terraform state lookups accept an explicit stack:

stacks/prod.yaml
components:
terraform:
app:
vars:
# Read VPC from prod stack
vpc_id: !terraform.state vpc prod vpc_id

# Read database from shared-services stack
db_endpoint: !terraform.state rds shared-services endpoint

This enables patterns like:

  • Shared services with centralized databases or networking
  • Hub-and-spoke networks with VPC peering
  • Cross-environment references (e.g., staging uses prod DNS zone)

Choosing the Right Method​

Use this decision tree to choose a starting point:

General guidance:

  • Declare component dependencies separately from value lookups; reading an output does not deploy its producer.
  • Use the remote-state module when you prefer to keep Terraform output lookups in HCL.
  • For Terraform planning before a producer exists, see component mocks. Do not assume Terraform mocks apply to other component types.