Blueprint Configuration
The Blueprint Configuration pattern creates reusable deployment templates that bundle multiple components with sensible defaults. Top-level stacks import a blueprint and customize only what differs per deployment.
What Is a Blueprint?
A blueprint is a stack manifest that bundles multiple components into a reusable deployment template. It sits above catalogs and archetypes in the abstraction hierarchy:
- A catalog defines defaults for a single component (e.g.,
catalog/vpc/defaults.yaml) - An archetype is a pre-configured variant of a single component for a specific use case (e.g.,
catalog/s3-bucket/logging.yaml), typically usingmetadata.inherits - A blueprint composes multiple components together into a complete deployment type (e.g., networking + compute + storage for a multi-tenant platform)
Catalogs and archetypes answer "how should this component be configured?" Blueprints answer "what components make up this type of deployment, and what are the sensible defaults?"
Use-cases
Use the Blueprint Configuration pattern when:
-
You manage many deployments of the same architecture (e.g., multi-tenant, multi-region)
-
Each deployment follows a common pattern but needs deployment-specific values (CIDRs, instance sizes, etc.)
-
You want to prevent drift between deployments by sharing a single source of truth
-
Different deployment types have different component compositions (e.g., multi-tenant vs single-tenant)
Benefits
-
One blueprint per deployment type — changes propagate to all deployments automatically
-
Top-level stacks are minimal — just an import and deployment-specific values
-
Composable — blueprints import layers, layers import component defaults
-
No
overridesneeded — plainvarsat the same scope level handles customization
Example
The following is one opinionated implementation using dedicated layers/ and blueprints/ directories. Your project may organize these differently — what matters is the pattern of composing component catalogs into a reusable template that deployment stacks import.
Directory Structure
├── stacks
│ ├── catalog # Component defaults
│ │ ├── vpc
│ │ │ └── defaults.yaml
│ │ ├── eks
│ │ │ └── defaults.yaml
│ │ └── rds
│ │ └── defaults.yaml
│ ├── layers # Infrastructure layers
│ │ ├── networking.yaml
│ │ ├── compute.yaml
│ │ └── storage.yaml
│ ├── blueprints # Deployment type templates
│ │ ├── multi-tenant.yaml
│ │ └── single-tenant.yaml
│ └── deploy # One file per deployment
│ ├── mt-prod-us.yaml
│ ├── mt-prod-eu.yaml
│ └── st-prod-us-customer1.yaml
│
└── components
└── terraform
├── vpc
├── eks
└── rds
Component Defaults (Catalog)
Component defaults define the baseline configuration for each Terraform component:
stacks/catalog/vpc/defaults.yaml
stacks/catalog/rds/defaults.yaml
Layers
Layers group related component defaults by infrastructure function:
stacks/layers/networking.yaml
stacks/layers/storage.yaml
Blueprints
Blueprints import layers and define configurable defaults. The key is to define defaults at the global vars level so that deployment stacks can easily override them:
stacks/blueprints/multi-tenant.yaml
stacks/blueprints/single-tenant.yaml
Deployment Stacks
Each deployment imports its blueprint and overrides only what's specific to that deployment:
stacks/deploy/mt-prod-us.yaml
stacks/deploy/mt-prod-eu.yaml
stacks/deploy/st-prod-us-customer1.yaml
This works because both the blueprint and the deployment stack define vars at the same scope level (global). The importing file's values override the imported file's values.
Why You Don't Need overrides
The overrides section exists for a different problem: file-scoped variable application. When multiple manifests (e.g., team A and team B) are imported into the same top-level stack, overrides prevents one team's settings from leaking into the other team's components.
In the blueprint pattern, each deployment stack imports a single blueprint. There's no competing manifest to leak into, so overrides adds no value. Plain vars at the same scope level is all you need.
The Scope Trap
If your blueprint defines defaults at the component level instead of the global level, you must override at the component level too.
Blueprint with component-level defaults
Deployment stack (wrong way)
Deployment stack (right way)
Related Patterns
- Layered Stack Configuration — Organize components into infrastructure layers
- Component Catalog — Define reusable component defaults
- Defaults Pattern — Share common configuration across stacks
References
- Stack Imports — Import syntax and resolution
- Overrides — File-scoped variable application