Skip to main content

Multiple Component Instances

The Multiple Component Instances Design Pattern describes how to provision many instances of a Terraform component in the same environment by configuring multiple Atmos component instances.

An Atmos component can have any name that can be different from the Terraform component name. More than one instance of the same Terraform component (with the same or different settings) can be provisioned into the same environment by defining multiple Atmos components that provide configuration for the Terraform component.

For example, two different Atmos components vpc/1 and vpc/2 can provide configuration for the same Terraform component vpc.

Use-cases

Use the Multiple Component Instances pattern when:

  • You need to provision multiple instances of a Terraform component in the same environment (organization, OU, account, region)

Benefits

The Multiple Component Instances pattern provides the following benefits:

  • Separation of the code (Terraform component) from the configuration (Atmos components)

  • The same Terraform code is reused by multiple Atmos component instances with different configurations

  • The defaults for the components are defined in just one place making the entire configuration DRY

Example

The following example shows the Atmos stack and component configurations to provision two Atmos components (vpc/1 and vpc/2) that use the same Terraform component vpc. The vpc/1 and vpc/2 components are provisioned in the same stack. In the catalog/vpc folder, we have the defaults.yaml manifest that configures the base abstract component vpc/defaults to be inherited by the derived VPC components vpc/1 and vpc/2.

   │   # Centralized stacks configuration (stack manifests)
   ├── stacks
   │   ├── catalog # component-specific defaults
   │   │   └── vpc
   │   │       └── defaults.yaml
   │   ├── mixins
   │   │   ├── tenant # tenant-specific defaults
   │   │   │   └── plat.yaml
   │   │   ├── region # region-specific defaults
   │   │   │   ├── us-east-2.yaml
   │   │   │   └── us-west-2.yaml
   │   │   └── stage # stage-specific defaults
   │   │       ├── dev.yaml
   │   │       ├── staging.yaml
   │   │       └── prod.yaml
   │   └── orgs # Organizations
   │       └── acme
   │           ├── _defaults.yaml
   │           └── plat # 'plat' represents the "Platform" OU (a.k.a tenant)
   │               ├── _defaults.yaml
   │               ├── dev
   │               │   ├── _defaults.yaml
   │               │   ├── us-east-2.yaml
   │               │   └── us-west-2.yaml
   │               ├── staging
   │               │   ├── _defaults.yaml
   │               │   ├── us-east-2.yaml
   │               │   └── us-west-2.yaml
   │               └── prod
   │                   ├── _defaults.yaml
   │                   ├── us-east-2.yaml
   │                   └── us-west-2.yaml
   │   # Centralized components configuration
   └── components
       └── terraform # Terraform components (a.k.a Terraform "root" modules)
           └── vpc

Add the following minimal configuration to atmos.yaml CLI config file :

atmos.yaml
components:
terraform:
base_path: "components/terraform"

stacks:
base_path: "stacks"
name_pattern: "{tenant}-{environment}-{stage}"
included_paths:
# Tell Atmos to search for the top-level stack manifests in the `orgs` folder and its sub-folders
- "orgs/**/*"
excluded_paths:
# Tell Atmos that the `defaults` folder and all sub-folders don't contain top-level stack manifests
- "defaults/**/*"

schemas:
jsonschema:
base_path: "stacks/schemas/jsonschema"
opa:
base_path: "stacks/schemas/opa"
atmos:
manifest: "stacks/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json"

Add the following default configuration to the stacks/catalog/vpc/defaults.yaml manifest:

stacks/catalog/vpc/defaults.yaml
components:
terraform:
vpc/defaults:
metadata:
# Abstract components can't be provisioned and serve as base components (blueprints) for real components
type: abstract
settings:
# All validation steps must succeed to allow the component to be provisioned
validation:
validate-vpc-component-with-jsonschema:
schema_type: jsonschema
schema_path: "vpc/validate-vpc-component.json"
description: Validate 'vpc' component variables using JSON Schema
check-vpc-component-config-with-opa-policy:
schema_type: opa
schema_path: "vpc/validate-vpc-component.rego"
module_paths:
- "catalog/constants"
description: Check 'vpc' component configuration using OPA policy
vars:
enabled: true
name: "common"
max_subnet_count: 3
map_public_ip_on_launch: true
assign_generated_ipv6_cidr_block: false
nat_gateway_enabled: true
nat_instance_enabled: false
vpc_flow_logs_enabled: true
vpc_flow_logs_traffic_type: "ALL"
vpc_flow_logs_log_destination_type: "s3"
nat_eip_aws_shield_protection_enabled: false
subnet_type_tag_key: "acme/subnet/type"
ipv4_primary_cidr_block: 10.0.0.0/18

Configure multiple vpc component instances in the stacks/orgs/acme/plat/prod/us-east-2.yaml top-level stack:

stacks/orgs/acme/plat/prod/us-east-2.yaml
import:
import:
- orgs/acme/plat/prod/_defaults
- mixins/region/us-east-2
# Import the defaults for all VPC components
- catalog/vpc/defaults

components:
terraform:
# Atmos component `vpc/1`
vpc/1:
metadata:
# Point to the Terraform component in `components/terraform/vpc`
component: vpc
# Inherit the defaults for all VPC components
inherits:
- vpc/defaults
# Define/override variables specific to this `vpc/1` component
vars:
name: vpc-1
ipv4_primary_cidr_block: 10.9.0.0/18

# Atmos component `vpc/2`
vpc/2:
metadata:
# Point to the Terraform component in `components/terraform/vpc`
component: vpc
# Inherit the defaults for all VPC components
inherits:
- vpc/defaults
# Define/override variables specific to this `vpc/2` component
vars:
name: vpc-2
ipv4_primary_cidr_block: 10.10.0.0/18
map_public_ip_on_launch: false

To provision the components in the stack, execute the following commands:

atmos terraform apply vpc/1 -s plat-ue2-prod
atmos terraform apply vpc/2 -s plat-ue2-prod

The provisioned VPCs will have the following names:

acme-plat-ue2-prod-vpc-1
acme-plat-ue2-prod-vpc-2

The names are generated from the context using the following template:

{namespace}-{tenant}-{environment}-{stage}-{name}