Partial Stack Configuration
The Partial Stack Configuration Design Pattern describes the mechanism of splitting an Atmos top-level stack's configuration across many Atmos stack manifests to manage and modify them separately and independently.
Each partial top-level stack manifest imports or configures a set of related Atmos components. Each Atmos component belongs to just one of the partial top-level stack manifests. The pattern helps to group all components by category or function and to make each partial stack manifest smaller and easier to manage.
Use-cases
Use the Partial Stack Configuration pattern when:
-
You have top-level stacks with complex configurations. Some parts of the configurations must be managed and modified independent of the other parts, possibly by different people or teams
-
You need to group the components in a top-level stack by category or function
-
You want to keep the configuration easy to manage and DRY
Benefits
The Partial Stack Configuration pattern provides the following benefits:
-
Allows defining Atmos stacks with complex configurations by splitting the configurations into smaller manifests and by grouping the components by category or function
-
Makes the configurations easier to understand
-
Allows creating and modifying the partial stack manifests independently, possibly by different teams
Example
In the following structure, we have many Terraform components (Terraform root modules) in the components/terraform
folder.
In the stacks/catalog
folder, we define the defaults for each component using the Component Catalog Atmos
Design Pattern.
In the stacks/orgs/acme/plat/dev/us-east-2
folder, we split the top-level stack manifest into the following parts by category:
load-balancers.yaml
data.yaml
dns.yaml
logs.yaml
notifications.yaml
firewalls.yaml
networking.yaml
eks.yaml
│ # Centralized stacks configuration (stack manifests)
├── stacks
│ ├── catalog # component-specific defaults
│ │ ├── alb
│ │ │ └── defaults.yaml
│ │ ├── aurora-postgres
│ │ │ └── defaults.yaml
│ │ ├── dns
│ │ │ └── defaults.yaml
│ │ ├── eks
│ │ │ └── defaults.yaml
│ │ ├── efs
│ │ │ └── defaults.yaml
│ │ ├── msk
│ │ │ └── defaults.yaml
│ │ ├── ses
│ │ │ └── defaults.yaml
│ │ ├── sns-topic
│ │ │ └── defaults.yaml
│ │ ├── network-firewall
│ │ │ └── defaults.yaml
│ │ ├── network-firewall-logs-bucket
│ │ │ └── defaults.yaml
│ │ ├── waf
│ │ │ └── defaults.yaml
│ │ ├── vpc
│ │ │ └── defaults.yaml
│ │ └── vpc-flow-logs-bucket
│ │ └── 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
│ │ ├── prod.yaml
│ │ └── staging.yaml
│ └── orgs # Organizations
│ └── acme
│ ├── _defaults.yaml
│ └── plat # 'plat' represents the "Platform" OU (a.k.a tenant)
│ ├── _defaults.yaml
│ └── dev # 'dev' account
│ ├── _defaults.yaml
│ ├── # Split the top-level stack 'plat-ue2-dev' by category of components
│ └── us-east-2
│ ├── load-balancers.yaml
│ ├── data.yaml
│ ├── dns.yaml
│ ├── logs.yaml
│ ├── notifications.yaml
│ ├── firewalls.yaml
│ ├── networking.yaml
│ └── eks.yaml
│ # Centralized components configuration
└── components
└── terraform # Terraform components (a.k.a Terraform "root" modules)
├── alb
├── aurora-postgres
├── dns
├── eks
├── efs
├── msk
├── ses
├── sns-topic
├── network-firewall
├── network-firewall-logs-bucket
├── waf
├── vpc
└── vpc-flow-logs-bucket
Note that the partial stack manifests are parts of the same top-level Atmos stack plat-ue2-dev
since they all import the same context variables
namespace
, tenant
, environment
and stage
. A top-level Atmos stack is defined by the context variables, not by the file names or locations
in the filesystem (file names can be anything, they are for people to better organize the entire configuration).
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/orgs/acme/plat/dev/us-east-2/load-balancers.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/alb/defaults
# Import other Load Balancer components
Add the following configuration to the stacks/orgs/acme/plat/dev/us-east-2/data.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/aurora-postgres/defaults
- catalog/msk/defaults
- catalog/efs/defaults
# Import other Data components
Add the following configuration to the stacks/orgs/acme/plat/dev/us-east-2/dns.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/dns/defaults
# Import other DNS components
Add the following configuration to the stacks/orgs/acme/plat/dev/us-east-2/logs.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/network-firewall-logs-bucket/defaults
- catalog/vpc-flow-logs-bucket/defaults
# Import other Logs components
Add the following configuration to the stacks/orgs/acme/plat/dev/us-east-2/notifications.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/ses/defaults
- catalog/sns-topic/defaults
# Import other Notification components
Add the following configuration to the stacks/orgs/acme/plat/dev/us-east-2/firewalls.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/network-firewall/defaults
- catalog/waf/defaults
# Import other Firewall components
Add the following configuration to the stacks/orgs/acme/plat/dev/us-east-2/networking.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/vpc/defaults
# Import other Networking components
Add the following configuration to the stacks/orgs/acme/plat/dev/us-east-2/eks.yaml
partial stack manifest:
import:
# The `orgs/acme/plat/dev/_defaults` and `mixins/region/us-east-2` manifests
# define the top-level Atmos stack `plat-ue2-dev`
- orgs/acme/plat/dev/_defaults
- mixins/region/us-east-2
# Import the related component manifests into this partial stack manifest
- catalog/eks/defaults
Limitations
The Partial Stack Configuration pattern has the following limitations and drawbacks:
-
The structure described by the pattern can become big and complex in a production-ready enterprise-grade infrastructure
-
In the example above, we showed how to split just one top-level stack manifest (one organization, one OU/tenant, one account, one region) into smaller parts and import the related components. This is useful and not complicated with one or a few top-level stacks, but the configuration can become too complex when we need to do the same for all organizations, OUs/tenants, accounts and regions. We'll have to repeat the same filesystem layout many times and import the same components into many partial stack manifests
To address the limitations of the Partial Stack Configuration Design Pattern, consider the Layered Stack Configuration Design Pattern
Related Patterns
- Organizational Structure Configuration
- Layered Stack Configuration
- Component Overrides
- Component Catalog
- Component Catalog with Mixins