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

   ├── 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:

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:

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