Terraform Stacks
Terraform Stacks is an orchestration layer built on top of Terraform modules that enables you to manage complex infrastructure deployments across multiple environments, regions, and teams.Overview
Stacks provide a higher-level abstraction for organizing and managing infrastructure by orchestrating zero or more trees of Terraform modules. This architecture allows you to:- Orchestrate multiple module configurations as a single unit
- Manage dependencies between components automatically
- Deploy infrastructure across multiple instances with
for_each - Maintain consistent configurations across environments
Architecture
The Stacks system consists of several key components:Core Components
Stack Addresses (stackaddrs)
A stacks-specific addressing system for referring to objects within the stacks language and runtime. This builds on Terraform’s standard addrs package since the stacks runtime wraps the modules runtime.
Stack Configuration (stackconfig)
Implements loading, parsing, and static decoding for the stacks language, similar to how the configs package works for Terraform’s module language.
Location: internal/stacks/stackconfig/
Stack Plan and State (stackplan and stackstate)
Provide models and marshalling/unmarshalling logic for the Stacks variants of Terraform’s plan and state concepts.
stackruntime)
Handles the runtime behavior of stacks, including:
- Creating plans based on comparison between desired and actual state
- Applying those plans
- All dynamic behavior of the stacks language
internal/stacks/stackruntime/
Runtime Architecture
The stacks runtime uses an implicit data flow graph constructed dynamically during evaluation, which differs from the traditional Terraform modules runtime.Config vs. Dynamic Objects
Stacks use paired types to represent static configuration and dynamic instances:- Config objects: Represent static configuration (e.g.,
ComponentConfig) - Dynamic objects: Represent runtime instances (e.g.,
Component,ComponentInstance)
- Early validation during the validation phase
- Avoiding redundant error reporting across multiple instances
- Clear distinction between static checks and dynamic operations
Calls vs. Instances
For objects supportingfor_each, there’s a three-tier hierarchy:
- Config (
ComponentConfig): Validates the component declaration - Call (
Component): Evaluatesfor_eachand produces instance mappings - Instance (
ComponentInstance): Represents individual instances with their own evaluation context
Evaluation Phases
Stacks support multiple evaluation phases:ValidatePhase
Performs static validation without dynamic evaluation. Created viaNewForValidating().
PlanPhase
Creates plans based on comparison between desired and actual state. Created viaNewForPlanning() with prior state and planning options.
ApplyPhase
Applies previously created plans. Created viaNewForApplying() bound to a specific stack plan.
InspectPhase
Provides read-only access to state for utilities and testing. Created viaNewForInspecting().
Expression Evaluation
Stacks use a sophisticated expression evaluation system:Stack, Component, StackCall).
Referenceable
Implemented by objects that can be referred to in expressions (e.g., InputVariable, Component).
Apply-Phase Scheduling
During apply, Stacks use explicit scheduling to ensure correct operation ordering:- Uses
ChangeExecfunction for managing apply operations - Builds dependency graph between components during planning
- Ensures operations execute in correct order relative to dependencies
- Prevents issues like destroying dependent resources before their dependencies
internal/stacks/stackruntime/internal/stackeval/
Working with Stacks
Component Management
Components are the primary building blocks in Stacks:Stack Calls
Nest stacks within stacks for hierarchical organization:Protocol Buffers Schema
Stacks use Protocol Buffers for preserving plan and state data between runs:- Package:
tfstackdata1 - Format: Internal implementation detail
- Public interface: Terraform Core RPC API (implemented in
rpcapi)
Best Practices
- Use Config objects for validation: Perform checks at the configuration level when possible
- Leverage evaluation phases: Use the appropriate phase for each operation
- Respect singleton patterns: Each object should be instantiated once per evaluation phase
- Handle diagnostics properly: Use
Check*methods for propagating diagnostics - Consider dependencies: Ensure components declare their dependencies correctly
Related Resources
- Testing Framework - Learn how to test your Stacks configurations
- Cloud Integration - Deploy Stacks with HCP Terraform
- Debugging - Debug Stacks runtime issues
Implementation Details
For maintainers and contributors, detailed internal documentation is available in:internal/stacks/README.md- High-level overviewinternal/stacks/stackruntime/internal/stackeval/README.md- Runtime architecture details