Skip to main content

Organizational Hierarchy Configuration

Atmos Design Pattern

The Organizational Hierarchy Configuration pattern describes how to structure stacks for enterprise environments with multiple organizations, organizational units (OUs), tenants, and accounts.

Use-cases​

Use the Organizational Hierarchy Configuration pattern when:

  • You have one or more organizations with multiple organizational units/departments/tenants

  • Each OU/department/tenant has multiple accounts (dev, staging, prod, etc.)

  • You need to manage configuration at different levels (org, tenant, account)

Benefits​

The Organizational Hierarchy Configuration pattern provides the following benefits:

  • Defaults for organizations, tenants/OUs, and accounts are defined in just one place, making the configuration extremely DRY

  • New organizations can be added without affecting existing organization configurations

  • New tenants/OUs can be added to an organization without affecting existing tenants

  • New accounts can be added to a tenant without affecting existing accounts

  • The hierarchical structure mirrors your actual organizational structure, making it intuitive to navigate

Organizational Concepts​

Atmos uses context variables to represent different levels of your organizational hierarchy:

namespace
Represents the organization (e.g., org1, org2, acme). Used when managing multiple distinct organizations.
tenant
Represents an organizational unit, department, or team (e.g., core, plat, engineering). Groups related accounts together.
stage
Represents an account or deployment stage (e.g., dev, staging, prod, audit). Typically maps to an AWS account.

The Role of _defaults.yaml Files​

The _defaults.yaml files at each level of the hierarchy contain baseline configurations that are inherited by child levels. These files:

  • Must be explicitly imported — they are not automatically included
  • Create inheritance chains by importing parent-level defaults
  • Are excluded from stack discovery via the excluded_paths configuration
info

The _defaults.yaml naming is a convention, not an Atmos feature. The underscore prefix ensures these files sort to the top of directory listings and are visually distinct from stack configurations. See the _defaults.yaml Design Pattern for details.

Example​

The following example shows how to structure stacks for an organization (acme) with two OUs (core and plat) and multiple accounts. Each account (stage) is a folder containing layer files that group components by function.

Directory Structure​

Terminal
├── stacks
│ ├── catalog # component defaults
│ │ ├── vpc
│ │ │ └── defaults.yaml
│ │ ├── rds
│ │ │ └── defaults.yaml
│ │ └── eks
│ │ └── defaults.yaml
│ └── orgs
│ └── acme
│ ├── _defaults.yaml
│ ├── core # 'core' OU
│ │ ├── _defaults.yaml
│ │ └── audit
│ │ ├── _defaults.yaml
│ │ └── network.yaml
│ └── plat # 'plat' OU
│ ├── _defaults.yaml
│ ├── dev
│ │ ├── _defaults.yaml
│ │ ├── network.yaml
│ │ └── data.yaml
│ ├── staging
│ │ ├── _defaults.yaml
│ │ ├── network.yaml
│ │ └── data.yaml
│ └── prod
│ ├── _defaults.yaml
│ ├── network.yaml
│ ├── data.yaml
│ └── compute.yaml
│
└── components
└── terraform
├── vpc
├── rds
└── eks

Configure atmos.yaml​

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

stacks:
base_path: "stacks"
included_paths:
- "orgs/**/*"
excluded_paths:
- "**/_defaults.yaml"
name_template: "{{.vars.tenant}}-{{.vars.stage}}-{{.vars.layer}}"
Simpler Stack Names

If you prefer simpler stack names without the layer, use:

name_template: "{{.vars.tenant}}-{{.vars.stage}}"

This works when each layer file defines different components (no overlap).

Configure Organization Defaults​

stacks/orgs/acme/_defaults.yaml
vars:
namespace: acme

terraform:
vars:
tags:
Organization: acme

Configure Tenant Defaults​

stacks/orgs/acme/plat/_defaults.yaml
import:
- orgs/acme/_defaults

vars:
tenant: plat

Configure Stage Defaults​

Each stage folder has a _defaults.yaml that sets the stage variable:

stacks/orgs/acme/plat/dev/_defaults.yaml
import:
- orgs/acme/plat/_defaults

vars:
stage: dev
stacks/orgs/acme/plat/prod/_defaults.yaml
import:
- orgs/acme/plat/_defaults

vars:
stage: prod

Configure Layer Stacks​

Each layer file imports its stage defaults and the relevant component catalogs:

stacks/orgs/acme/plat/dev/network.yaml
import:
- orgs/acme/plat/dev/_defaults
- catalog/vpc/defaults

vars:
layer: network
stacks/orgs/acme/plat/dev/data.yaml
import:
- orgs/acme/plat/dev/_defaults
- catalog/rds/defaults

vars:
layer: data

# Dev-specific overrides
components:
terraform:
rds:
vars:
instance_class: db.t3.small
stacks/orgs/acme/plat/prod/network.yaml
import:
- orgs/acme/plat/prod/_defaults
- catalog/vpc/defaults

vars:
layer: network

# Prod-specific overrides
components:
terraform:
vpc:
vars:
map_public_ip_on_launch: false
stacks/orgs/acme/plat/prod/compute.yaml
import:
- orgs/acme/plat/prod/_defaults
- catalog/eks/defaults

vars:
layer: compute

Import Chain​

The following diagram shows how configuration flows through the import chain:

Provision the Components​

Deploy the network layer in dev:

atmos terraform apply vpc -s plat-dev-network

Deploy the data layer in prod:

atmos terraform apply rds -s plat-prod-data

Deploy the compute layer in prod:

atmos terraform apply eks -s plat-prod-compute

Adding New Elements​

Adding a New Account​

Create a new stage folder with its defaults and layer files:

Terminal
stacks/orgs/acme/plat/qa/
├── _defaults.yaml
├── network.yaml
└── data.yaml

Adding a New Layer​

Add a new layer file to each stage that needs it:

Terminal
stacks/orgs/acme/plat/prod/observability.yaml

Combining with Multi-Region​

This pattern can be combined with the Multi-Region Configuration pattern. Add the environment variable to your name template and structure:

name_template: "{{.vars.tenant}}-{{.vars.environment}}-{{.vars.stage}}-{{.vars.layer}}"

See the Multi-Region Configuration pattern for details.

References​