# 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.

> **Key points**
>
> - 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.

```mermaid
graph LR
    VPC[VPC Component] -->|vpc_id| Subnets[Subnet Component]
    Subnets -->|subnet_ids| EKS[EKS Component]
    EKS -->|cluster_endpoint| App[Application Component]
```

## Recommended: `!terraform.state`

For **Terraform/OpenTofu outputs with a supported backend**, prefer the [`!terraform.state`](/stacks/sharing-state/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](/functions/yaml/terraform.state) before choosing it for your deployment.

:::caution 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](/cli/commands/terraform/init#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.
:::

**File:** `stacks/dev.yaml`

```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[Read more](/stacks/sharing-state/terraform-state)

## 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`](/stacks/sharing-state/terraform-state) | Terraform/OpenTofu state | Read deployed outputs directly from a supported backend |
| [`!terraform.output`](/stacks/sharing-state/terraform-output) | Terraform/OpenTofu CLI | Use when the engine's output handling is required; account for initialization and subprocess overhead |
| [`!aws.cloudformation.output`](/functions/yaml/aws.cloudformation.output) | Deployed CloudFormation stack | Read a native `aws/cloudformation` component's Outputs |
| [`atmos.Component`](/functions/template/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](/stacks/sharing-state/remote-state-module) | Terraform state through HCL | Keep output lookups inside Terraform code |
| [`!store`](/stacks/sharing-state/stores) | External key-value store | Publish and consume persisted values across tools and deployments |
| [Named step outputs](/steps/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`](/cli/configuration/secrets). 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.

```yaml
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.

```yaml
vpc_id: !terraform.output vpc vpc_id
```

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

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

**`!store`** - Read from external key-value stores.

```yaml
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:

```yaml
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](/functions/yaml/aws.cloudformation.output) for authentication, supported deployed-stack states, and output value types.

### Configuration and Template Functions

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

```yaml
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](/stacks/imports) and [component inheritance](/design-patterns/inheritance-patterns/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](/stacks/sharing-state/stores) 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](/steps/outputs):

```yaml
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:

**File:** `stacks/prod.yaml`

```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:

```mermaid
flowchart TD
    A{What data do you need?} -->|Terraform outputs| B{Direct backend reads supported?}
    B -->|Yes| C[!terraform.state]
    B -->|No| D[!terraform.output]
    A -->|CloudFormation Outputs| E[!aws.cloudformation.output]
    A -->|Component configuration| F[atmos.Component]
    A -->|Persisted external data| G[External store]
    A -->|Earlier step result| H[Named step outputs]
```

**General guidance:**

- Declare [component dependencies](/stacks/dependencies/components) 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](/migration/terragrunt#mock_outputs--component-mocks). Do not assume Terraform mocks apply to other component types.
