# atmos.Component

The `atmos.Component` template function allows reading any Atmos section or any attribute from a section for an
Atmos component in a stack, and use it in `Go` templates in Atmos component configurations.

## Usage

```yaml
  {{ (atmos.Component "<component>" "<stack>").<section>.<attribute> }}
```

## Arguments

- **`component`**
  Atmos component name
- **`stack`**
  Atmos stack name
- **`section`**
  Atmos section name. Any section returned by the CLI command

  atmos describe component
   can be used.
  A special 
  `outputs`
   section is also supported to get the outputs (remote state) of Terraform/OpenTofu components,
  or the deployed Outputs of native 
  `aws/cloudformation`
   components.
- **`outputs`**
  Using the 
  `outputs`
   section in the 
  `atmos.Component`
   command is an convenient way to read the outputs (remote state)
  of a component in a stack directly in Atmos stack manifests. It is an alternative to using the 
  `remote-state`

  module and configuring Terraform/OpenTofu components to use the 
  `remote-state`
   module as described in

  Component Remote State
- **`attribute`**

  Attribute name (field) from the `section`. `attribute` is optional, you can use the `section` itself
  if it's a simple type (e.g. `string`). Any number of attributes can be chained using the dot (`.`) notation.
  For example, if the first two attributes are maps, you can chain them and get a field from the last map:
  ```yaml
  {{ (atmos.Component "<component>" "<stack>").<section>.<attribute1>.<attribute2>.<field1> }}
  ```

## Specifying Atmos `stack`

There are multiple ways you can specify the Atmos stack parameter in the `atmos.Component` function.

The `stack` argument is the second argument of the `atmos.Component` function, and it can be specified in a few different ways:

### Hardcoded Stack Name

Hardcoded stack name. Use it if you want to get an output from a component from a different (well-known and static) stack. For example, you have a `tgw` component in a stack `plat-ue2-dev` that requires the `vpc_id` output from the `vpc` component from the stack `plat-ue2-prod`:

```yaml title="plat-ue2-dev"
  components:
    terraform:
      tgw:
        vars:
          vpc_id: '{{ (atmos.Component "vpc" "plat-ue2-prod").outputs.vpc_id }}'
```

### Reference the Current Stack Name

Use the `.stack` (or `.atmos_stack`) template identifier to specify the same stack as the current component is in (for which the `atmos.Component` function is executed):

```yaml
  {{ (atmos.Component "<component>" .stack).<section>.<attribute> }}
  {{ (atmos.Component "<component>" .atmos_stack).<section>.<attribute> }}
```

For example, you have a `tgw` component that requires the `vpc_id` output from the `vpc` component in the same stack:

```yaml
  components:
    terraform:
      tgw:
        vars:
          vpc_id: '{{ (atmos.Component "vpc" .stack).outputs.vpc_id }}'
```

### Use a Format Function

Use the `printf` template function to construct stack names using static strings and dynamic identifiers. This is convenient when you want to override some identifiers in the stack name:

```yaml
  {{ (atmos.Component "<component>" (printf "%s-%s-%s" .vars.tenant .vars.environment .vars.stage)).<section>.<attribute> }}

  {{ (atmos.Component "<component>" (printf "plat-%s-prod" .vars.environment)).<section>.<attribute> }}

  {{ (atmos.Component "<component>" (printf "%s-%s-%s" .settings.context.tenant .settings.context.region .settings.context.account)).<section>.<attribute> }}
```

For example, you have a `tgw` component deployed in the stack `plat-ue2-dev`. The `tgw` component requires the
`vpc_id` output from the `vpc` component from the same environment (`ue2`) and same stage (`dev`), but from a different
tenant `net` (instead of `plat`):

```yaml title="plat-ue2-dev"
  components:
    terraform:
      tgw:
        vars:
          vpc_id: '{{ (atmos.Component "vpc" (printf "net-%s-%s" .vars.environment .vars.stage)).outputs.vpc_id }}'
```

:::tip Important
By using the `printf "%s-%s-%s"` function, you are constructing stack names using the stack context variables/identifiers.

For more information on Atmos stack names and how to define them, refer to `stacks.name_pattern` and `stacks.name_template`
sections in [`atmos.yaml` CLI config file](/cli/configuration/)
:::

## Handling Outputs Containing Maps or Lists

You can use the `atmos.Component` function to read complex outputs (remote state) from Terraform/OpenTofu components, and use those in your stack manifests.
To effectively use `atmos.Component` outputs in stack configurations, it’s important to understand the relationship between YAML and JSON.
YAML is a superset of JSON, meaning you can embed JSON directly inside YAML, but the reverse is not true.

This means:

1. The output of a template function must be valid YAML (including the relative whitespace based on its current position) or JSON to be parsed correctly.
2. Simple types like strings are straightforward — they can be returned as-is.
3. Complex types like lists or maps must be serialized into JSON to ensure compatibility.

:::note
There are a few ways to handle the complex output types (maps and lists) from the `atmos.Component` function:

- Using the `!template` Atmos YAML function and the `toRawJson` or `toJson` template functions (this is the recommended way)
- Adjusting the template delimiters in `atmos.yaml` and using the `toRawJson` or `toJson` template functions
  :::

### Using the `!template` Atmos YAML Function and `toRawJson` or `toJson` template functions

You can use the [`!template`](/functions/yaml/template) Atmos YAML function to handle the outputs containing maps or lists.

The **`!template`** YAML function  correctly handles the JSON returned from the
[`toRawJson` or `toJson`](https://masterminds.github.io/sprig/defaults.html) functions and converts the result into the list or map YAML types.
The final result of type list or map can be sent directly to the corresponding Terraform/OpenTofu component without modifying the types of the input variables.

**File:** `stack.yaml`

```yaml
components:
  terraform:
    my_lambda_component:
      vars:
        vpc_config:
          security_group_ids: ['{{ (atmos.Component "security-group/lambda" .stack).outputs.id }}']

          # Output of type list from the `atmos.Component` template function
          subnet_ids: !template '{{ toJson (atmos.Component "vpc" .stack).outputs.private_subnet_ids }}'

          # Output of type map from the `atmos.Component` template function
          config_map: !template '{{ toJson (atmos.Component "config" .stack).outputs.config_map }}'
```

### Adjusting the template delimiters in `atmos.yaml` and using the `toRawJson` or `toJson` template functions

First, we need to adjust the delimiters in `atmos.yaml`, to avoid issues with YAML parsing:

```yaml title="atmos.yaml"
templates:
  settings:
    # Note the deliberate use of single quotes around the braces to avoid YAML parsing issues
    delimiters: ["'{{", "}}'"]
```

Detailed Explanation

In YAML, the default `{{` and `}}` delimiters used for templating can conflict with YAML’s syntax rules, leading to parsing errors. For example, YAML interprets unquoted braces as JSON maps, which can cause YAML parsing to fail.

1. YAML Parsing Phase First, the file is parsed as YAML to extract its structure, such as imports. At this stage, the file must be valid YAML for Atmos to proceed. If the template expressions inside `{{ ... }}` are not properly handled, they can break the YAML syntax, leading to parsing errors.

2. Go Template Processing Phase: After successfully parsing the YAML structure, Atmos treats the file as a Go template and processes the content. Here’s what happens:
   - Everything between the delimiters (`{{ ... }}`) is replaced by the result of the template function or expression.
   - The output of this replacement must also be valid YAML so the file can remain usable for the next steps (e.g., Terraform processing).

3. How Adjusted Delimiters Work: By configuring delimiters as `["'{{", "}}'"]` in `atmos.yaml`, you ensure that:
   - During the YAML Parsing Phase, the single quotes (`'`) around the template expressions make them valid YAML strings, allowing the file to pass YAML validation.
   - During the Go Template Processing Phase, Atmos processes the content within '`{{` and `}}`' and replaces it with the evaluated result.

Then we can use the [`toRawJson` or `toJson`](https://masterminds.github.io/sprig/defaults.html) functions to serialize complex types into JSON:

```yaml
  components:
  terraform:
    my_lambda_component:
      vars:
        vpc_config:
          security_group_ids: ['{{ (atmos.Component "security-group/lambda" .stack).outputs.id }}']
          subnet_ids: '{{ toRawJson ((atmos.Component "vpc" .stack).outputs.private_subnet_ids) }}'
```

The results of the `toRawJson` function are serialized into JSON, which can be safely embedded in the YAML stack configuration.
The single quotes around the braces in the `delimiters` setting ensure that the JSON is not parsed as YAML, but then processed as a template function which removes the single quotes and replaces it with the JSON string.

## Using `atmos.Component` with `aws/cloudformation` Components

The `outputs` section is not limited to Terraform/OpenTofu targets: when `component` resolves to a native
`aws/cloudformation` component, `atmos.Component(...).outputs` returns the deployed CloudFormation stack's
[Outputs](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html) instead
— fetched live via `DescribeStacks`. This is the same Terraform↔CloudFormation interop bridge as the
[`!aws.cloudformation.output`](/functions/yaml/aws.cloudformation.output) YAML function; use whichever fits the
call site (`atmos.Component` when you also need other sections of the target, like `vars` or `settings`;
`!aws.cloudformation.output` for a single Output value).

```yaml
components:
  terraform:
    my_lambda_component:
      vars:
        # Read the `VpcId` Output of the native aws/cloudformation `vpc` component in the current stack
        vpc_id: '{{ (atmos.Component "vpc" .stack).outputs.VpcId }}'
```

The target `aws/cloudformation` component must have a resolved `stack_name` and already be deployed —
`atmos.Component` calls the live CloudFormation API for these targets, the same as for Terraform/OpenTofu targets
without a `static` remote state backend.

## Examples

The following configurations show different ways of using the `atmos.Component` template function to read values from
different Atmos sections directly in Atmos stack manifests, including the outputs of other
(already provisioned) components.

```
# Global `settings` section
# It will be added and deep-merged to the `settings` section of all components
settings:
  test: true

components:
  terraform:
    test:
      metadata:
        # Point to the Terraform/OpenTofu component
        component: "test"
      vars:
        name: "test"

    test1:
      metadata:
        # Point to the Terraform/OpenTofu component
        component: "test1"
      vars:
        name: "test1"

    test2:
      metadata:
        # Point to the Terraform/OpenTofu component
        component: "test2"
      vars:
        name: "test2"
        # Use the `atmos.Component` function to get the outputs of the Atmos component `test1`
        # The `test1` component must be already provisioned and its outputs stored in the Terraform/OpenTofu state
        # Atmos will execute `terraform output` on the `test1` component in the same stack to read its outputs
        test1_id: '{{ (atmos.Component "test1" .stack).outputs.test1_id }}'
        tags:
          # Get the `settings.test` field from the `test` component in the same stack
          test: '{{ (atmos.Component "test" .stack).settings.test }}'
          # Get the `metadata.component` field from the `test` component in the same stack
          test_terraform_component: '{{ (atmos.Component "test" .stack).metadata.component }}'
          # Get the `vars.name` field from the `test1` component in the same stack
          test1_name: '{{ (atmos.Component "test1" .stack).vars.name }}'
```

## Caching the result of `atmos.Component` function

Atmos caches (in memory) the results of `atmos.Component` template function execution.
If you call the function for the same component in a stack more than once, the first call will produce the result
and cache it, and all the consecutive calls will just use the cached data. This is useful when you use the
`atmos.Component` function for the same component in a stack in multiple places in Atmos stack manifests.
It will speed up the function execution and stack processing.

For example:

```
components:
  terraform:
    test2:
      vars:
        tags:
          test: '{{ (atmos.Component "test" .stack).outputs.id }}'
          test2: '{{ (atmos.Component "test" .stack).outputs.id }}'
          test3: '{{ (atmos.Component "test" .stack).outputs.id }}'
```

In the example, the `test2` Atmos component uses the outputs (remote state) of the `test` Atmos component from the same stack.
The template function `{{ atmos.Component "test" .stack }}` is executed three times (once for each tag).

After the first execution, Atmos caches the result in memory (all the component sections, including the `outputs`),
and reuses it in the next two calls to the function. The caching makes the stack processing about three times faster in this
particular example. In a production environment where many components are used, the speedup can be even more significant.

## Using `atmos.Component` with `static` remote state backend

Atmos supports [brownfield configuration by using the remote state of type `static`](/components/terraform/brownfield/#hacking-remote-state-with-static-backends).

For example:

**File:** `stack.yaml`

```yaml
components:
  terraform:
    # Component `static-backend` is configured with the remote state backend of type `static`
    static-backend:
      remote_state_backend_type: static
      remote_state_backend:
        static:
          region: "us-west-2"
          cluster_name: "production-cluster"
          vpc_cidr: "10.0.0.0/16"
          database:
            type: "postgresql"
            version: "12.7"
            storage_gb: 100
          allowed_ips:
            - "192.168.1.0/24"
            - "10.1.0.0/16"
          tags:
            Environment: "production"
            Owner: "infra-team"

    eks-cluster:
      vars:
        region: '{{ (atmos.Component "static-backend" .stack).outputs.region }}'
        cluster_name: '{{ (atmos.Component "static-backend" .stack).outputs.cluster_name }}'
        vpc_cidr: '{{ (atmos.Component "static-backend" .stack).outputs.vpc_cidr }}'
        db_type: '{{ (atmos.Component "static-backend" .stack).outputs.database.type }}'
        db_storage: '{{ (atmos.Component "static-backend" .stack).outputs.database.storage_gb }}'
        # Use the `!template` YAML function to correctly handle the outputs of types map and list
        allowed_ips: !template '{{ (atmos.Component "static-backend" .stack).outputs.allowed_ips }}'
        tags: !template '{{ (atmos.Component "static-backend" .stack).outputs.tags }}'
```

When the functions are executed, Atmos detects that the `static-backend` component has the `static` remote state configured,
and instead of executing `terraform output`, it just returns the static values from the `remote_state_backend.static` section.

Executing the command `atmos describe component eks-cluster -s <stack>` produces the following result:

```shell
vars:
  region: us-west-2
  cluster_name: production-cluster
  vpc_cidr: 10.0.0.0/16
  db_type: postgresql
  db_storage: 100
  allowed_ips:
    - 192.168.1.0/24
    - 10.1.0.0/16
  tags:
    Environment: production
    Owner: infra-team
```
