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:
- VPC creates the network and outputs
vpc_id - Subnets need the
vpc_idto create subnets - EKS cluster needs subnet IDs to launch in the right network
- 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.
Recommended: !terraform.state
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.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.
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
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:
| Method | Data source | When to use it |
|---|---|---|
!terraform.state | Terraform/OpenTofu state | Read deployed outputs directly from a supported backend |
!terraform.output | Terraform/OpenTofu CLI | Use when the engine's output handling is required; account for initialization and subprocess overhead |
!aws.cloudformation.output | Deployed CloudFormation stack | Read a native aws/cloudformation component's Outputs |
atmos.Component | Resolved component configuration; deployed outputs for supported types | Read component data in templates; Terraform targets eagerly load outputs even for configuration-only access |
| Remote State Module | Terraform state through HCL | Keep output lookups inside Terraform code |
!store | External key-value store | Publish and consume persisted values across tools and deployments |
| Named step outputs | Results of earlier workflow steps | Pass 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:
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-statemodule 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.