Component Catalog
The Component Catalog is a powerful pattern for organizing and managing the configuration of components across multiple stacks, enabling easy reuse of default configurations. Use this pattern to maintain a DRY (Don't Repeat Yourself) configuration approach, ensuring consistent and efficient deployments across various environments and regions.
Use-cases
Use the Component Catalog pattern when:
-
You have many components that are provisioned in multiple stacks (many OUs, accounts, regions) with different configurations for each stack
-
You need to make the components' default configurations reusable across different stacks
-
You want the component catalog folders structures to mirror the Terraform components folder structure to make it easy to find and manage
-
You want to keep the configurations DRY
Benefits
The Component Catalog pattern provides the following benefits:
-
The defaults for the components are defined in just one place (in the catalog) making the entire configuration DRY
-
The defaults for the components are reusable across many environments by using hierarchical imports
-
It's easy to add a new manifest in the component's catalog to enable a new component's feature, then import the manifest into the corresponding stacks where the feature is required
Design Pattern
The Component Catalog pattern prescribes the following:
-
For each Terraform component, create a folder with the same name in
stacks/catalog
to make it symmetrical and easy to find. For example, thestacks/catalog/vpc
folder should mirror thecomponents/terraform/vpc
folder. -
In the component's catalog folder, create
defaults.yaml
manifest with all the default values for the component (the defaults that can be reused across multiple environments). Define all the required Atmos sections, e.g.metadata
,settings
,vars
,env
. -
In the component's catalog folder, add other manifests for different combinations of component configurations. We refer to them as archetype manifests. Each archetype can import the
defaults.yaml
file to reuse the default values and make the entire config DRY. For example:stacks/catalog/vpc/disabled.yaml
- component manifest with the component disabled (vars.enabled: false
)stacks/catalog/vpc/dev.yaml
- component manifest with the settings related to thedev
accountstacks/catalog/vpc/staging.yaml
- component manifest with the settings related to thestaging
accountstacks/catalog/vpc/prod.yaml
- component manifest with the settings related to theprod
accountstacks/catalog/vpc/ue2.yaml
- component manifest with the settings forus-east-2
regionstacks/catalog/vpc/uw2.yaml
- component manifest with the settings forus-west-2
regionstacks/catalog/vpc/feature-1.yaml
- component manifest withfeature-1
setting enabled
Having the dev
, staging
, prod
, ue2
and uw2
manifests in the component's catalog makes the most sense for multi-org, multi-OU and/or
multi-region architectures, such that there will be multiple dev/staging/prod or region configurations, which get imported into multiple Org/OU
top-level stack manifests.
-
After we have defined the manifests for different use-cases, we import them into different top-level stacks depending on a particular use-case. For example:
- import the
catalog/vpc/ue2.yaml
manifest into thestacks/mixins/region/us-east-2.yaml
mixin since we need thevpc
component with theus-east-2
region-related config provisioned in the region - import the
catalog/vpc/uw2.yaml
manifest into thestacks/mixins/region/us-west-2.yaml
mixin since we need thevpc
component with theus-west-2
region-related config provisioned in the region - import the
catalog/vpc/dev.yaml
manifest into thestacks/orgs/acme/plat/dev/us-east-2.yaml
top-level stack since we need thevpc
component with the dev-related config provisioned in the stack - import the
catalog/vpc/prod.yaml
manifest into thestacks/orgs/acme/plat/prod/us-east-2.yaml
top-level stack since we need thevpc
component with the prod-related config provisioned in the stack - import the
catalog/vpc/staging.yaml
manifest into thestacks/orgs/acme/plat/staging/us-east-2.yaml
top-level stack since we need thevpc
component with the dev-related config provisioned in the stack - import the
catalog/vpc/disabled.yaml
manifest into a stack where we want thevpc
component to be disabled (e.g. temporarily until it's needed) - etc.
- import the
Example
The following example shows the Atmos stack and component configurations to provision the vpc
and vpc-flow-logs-bucket
components into
a multi-account, multi-region environment. The components' configurations for each account and region are defined in the components' catalog.
│ # Centralized stacks configuration (stack manifests)
├── stacks
│ └── catalog # component-specific defaults
│ ├── vpc
│ │ ├── defaults.yaml
│ │ ├── disabled.yaml
│ │ ├── dev.yaml
│ │ ├── prod.yaml
│ │ ├── staging.yaml
│ │ ├── ue2.yaml
│ │ └── uw2.yaml
│ └── vpc-flow-logs-bucket
│ ├── defaults.yaml
│ └── disabled.yaml
│ # Centralized components configuration
└── components
└── terraform # Terraform components (a.k.a Terraform "root" modules)
├── vpc
└── vpc-flow-logs-bucket
Add the following minimal configuration to atmos.yaml
CLI config file :
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 configuration to the stacks/catalog/vpc-flow-logs-bucket/defaults.yaml
manifest:
components:
terraform:
vpc-flow-logs-bucket:
metadata:
# Point to the Terraform component
component: vpc-flow-logs-bucket
vars:
enabled: true
name: "vpc-flow-logs"
traffic_type: "ALL"
force_destroy: true
lifecycle_rule_enabled: false
Add the following default configuration to the stacks/catalog/vpc/defaults.yaml
manifest:
components:
terraform:
vpc:
metadata:
# Point to the Terraform component
component: vpc
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.9.0.0/18
Add the following default configuration to the stacks/catalog/vpc/ue2.yaml
manifest:
import:
- catalog/vpc/defaults
components:
terraform:
vpc:
vars:
availability_zones:
- us-east-2a
- us-east-2b
- us-east-2c
Add the following default configuration to the stacks/catalog/vpc/uw2.yaml
manifest:
import:
- catalog/vpc/defaults
components:
terraform:
vpc:
vars:
availability_zones:
- us-west-2a
- us-west-2b
- us-west-2c
Add the following default configuration to the stacks/catalog/vpc/dev.yaml
manifest:
components:
terraform:
vpc:
vars:
ipv4_primary_cidr_block: 10.7.0.0/18
Add the following default configuration to the stacks/catalog/vpc/staging.yaml
manifest:
components:
terraform:
vpc:
vars:
ipv4_primary_cidr_block: 10.9.0.0/18
Add the following default configuration to the stacks/catalog/vpc/prod.yaml
manifest:
components:
terraform:
vpc:
vars:
ipv4_primary_cidr_block: 10.8.0.0/18
# In `prod`, don't map public IPs on launch
# Override `map_public_ip_on_launch` from the defaults
map_public_ip_on_launch: false
Import stacks/catalog/vpc/ue2.yaml
into the stacks/mixins/region/us-east-2.yaml
manifest:
import:
# Import the `ue2` manifest with `vpc` configuration for `us-east-2` region
- catalog/vpc/ue2
# All accounts (stages) in `us-east-2` region will have the `vpc-flow-logs-bucket` component
- catalog/vpc-flow-logs-bucket/defaults
vars:
region: us-east-2
environment: ue2
# Other defaults for the `us-east-2` region
Import stacks/catalog/vpc/uw2.yaml
into the stacks/mixins/region/us-west-2.yaml
manifest:
import:
# Import the `uw2` manifest with `vpc` configuration for `us-west-2` region
- catalog/vpc/uw2
# All accounts (stages) in `us-west-2` region will have the `vpc-flow-logs-bucket` component
- catalog/vpc-flow-logs-bucket/defaults
vars:
region: us-west-2
environment: uw2
# Other defaults for the `us-west-2` region
Import mixins/region/us-east-2
and stacks/catalog/vpc/dev.yaml
into the stacks/orgs/acme/plat/dev/us-east-2.yaml
top-level stack:
import:
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Override the `vpc` component configuration for `dev` by importing the `catalog/vpc/dev` manifest
- catalog/vpc/dev
Import mixins/region/us-west-2
and stacks/catalog/vpc/dev.yaml
into the stacks/orgs/acme/plat/dev/us-west-2.yaml
top-level stack:
import:
- orgs/acme/plat/dev/_defaults
- mixins/region/us-west-2
# Override the `vpc` component configuration for `dev` by importing the `catalog/vpc/dev` manifest
- catalog/vpc/dev
Import mixins/region/us-east-2
and stacks/catalog/vpc/staging.yaml
into the stacks/orgs/acme/plat/staging/us-east-2.yaml
top-level stack:
import:
- orgs/acme/plat/staging/_defaults
- mixins/region/us-east-2
# Override the `vpc` component configuration for `staging` by importing the `catalog/vpc/staging` manifest
- catalog/vpc/staging
Import mixins/region/us-west-2
and stacks/catalog/vpc/staging.yaml
into the stacks/orgs/acme/plat/staging/us-west-2.yaml
top-level stack:
import:
- orgs/acme/plat/staging/_defaults
- mixins/region/us-west-2
# Override the `vpc` component configuration for `staging` by importing the `catalog/vpc/staging` manifest
- catalog/vpc/staging
Import mixins/region/us-east-2
and stacks/catalog/vpc/prod.yaml
into the stacks/orgs/acme/plat/prod/us-east-2.yaml
top-level stack:
import:
- orgs/acme/plat/prod/_defaults
- mixins/region/us-east-2
# Override the `vpc` component configuration for `prod` by importing the `catalog/vpc/prod` manifest
- catalog/vpc/prod
Import mixins/region/us-west-2
and stacks/catalog/vpc/prod.yaml
into the stacks/orgs/acme/plat/prod/us-west-2.yaml
top-level stack:
import:
- orgs/acme/plat/prod/_defaults
- mixins/region/us-west-2
# Override the `vpc` component configuration for `prod` by importing the `catalog/vpc/prod` manifest
- catalog/vpc/prod
Limitations
The Component Catalog pattern has the following limitations and drawbacks:
- Although it's always recommended to use, the catalog structure described by the pattern can be complex for basic infrastructures, e.g. for a very simple organizational structure (one organization and OU), and just a few components deployed into a few accounts and regions
To address the limitations of the Component Catalog Design Pattern when you are provisioning a very basic infrastructure, use the following patterns:
Related Patterns
- Component Catalog with Mixins
- Component Catalog Template
- Component Inheritance
- Inline Component Configuration
- Inline Component Customization
- Organizational Structure Configuration