Atmos Stacks
When you design cloud architectures with Atmos, you break them apart into pieces called components that you implement with Terraform "root modules". Stacks are how you connect your components with configuration, so that everything comes together.
The power of components comes from their ability to be reused: you can compose stacks with one or more components, even reusing any component multiple times within a stack. But as your stacks grow with more and more components, it often makes sense to start splitting them into different files and that's why you might want to make use of imports. This lets you keep your Stack files easier to scan and reuse their configuration in multiple places.
Stacks define the complete configuration of an environment. Think of Stacks like an architectural "Blueprints" composed of one or more Components configurations and defined using a standardized YAML configuration.
Then by running the atmos
command, automate the orchestrate the deployment of loosely coupled components, such as Terraform "root" modules. By doing this, it enables scalable infrastructure-as-code configurations, allowing environments to inherit from one or more common bases (child stacks) by importing configuration that gets deep-merged, thus minimizing config duplication and manual effort. Each stack uses a simple schema that provides a declarative description of your various environments. This approach empowers you to separate your infrastructure’s environment configuration settings from the code it manages (e.g., Terraform components).
By facilitating the infrastructure configurations this way, developers achieve DRY (Don't Repeat Yourself) architectures with minimal configuration. Stacks make infrastructure more streamlined and consistent, significantly enhancing productivity. Best of all, Stacks can deploy vanilla Terraform "root" modules without any code generation, custom vendor extensions, or changes to the HCL code.
Atmos utilizes a custom YAML configuration format for stacks. YAML is ideal because it's portable across multiple toolchains and languages; every developer understands it. The Atmos CLI, the terraform-utils-provider provider, and Spacelift via the terraform-spacelift-cloud-infrastructure-automation module all support stacks. Utilizing the Terraform provider enables native access to the entire infrastructure configuration directly from Terraform.
Use-cases
- Rapid Environment Provisioning: Leverage stacks to swiftly set up and replicate development, testing, and production environments, ensuring consistency and reducing manual setup errors. This accelerates the development cycle and enables businesses to respond quickly to market demands or development needs.
- Multi-Tenant Infrastructure Management: Utilize stacks to manage and isolate resources for different clients or projects within a single cloud infrastructure. This approach supports SaaS companies in providing secure, isolated environments for each tenant, optimizing resource utilization and simplifying the management of complex, multi-tenant architectures.
- Compliance and Governance: Implement stacks to enforce compliance and governance policies across all environments systematically. By defining standard configurations that meet regulatory requirements, businesses can ensure that every deployment is compliant, reducing the risk of violations and enhancing security posture.
Conventions
The differentiation between the following two types of stacks is crucial for understanding how to organize stacks and the basis for the various design patterns.
Stack Names (aka "slugs")
Every stack is uniquely identified by a name. The name is used to reference the stack in the Atmos CLI, or with stack dependencies.
These are computed from either the name_pattern
(old way) or the more configurable
name_template
(new way). These are configured in the atmos.yaml
configuration file.
For example, using the slug, we can reference a stack like this when applying the vpc
stack in the us2-dev
environment:
atmos terraform apply vpc -s us2-dev
Components vs Component instances
Components are different from Stacks.
When a component is added to a stack, we call that a "Component Instance"
Parent Stacks vs Child Stacks
- Parent Stacks
- These are the top-level stacks that are responsible for importing Child stacks. Components inside of Parent stacks are deployable, unlike in Child stacks.
- Child Stacks
- These are any stacks whose components cannot be deployed independently without being imported by a Parent Stack. Catalogs are typically where we keep our Child stacks.
Logical Stacks vs. Physical Stack Manifests
- Logical Stacks
Represent the entire environment defined by context variables and global settings in atmos.yaml. Logical stacks are the in-memory representation of the deep-merged configuration.
- Physical Stacks
- Are the raw YAML files where the specific configurations of components are defined.
Atmos processes each physical stack file, first evaluating any templates and then processing it as YAML. After loading the YAML,
it proceeds to deep-merge the configuration with the current in-memory logical representation of the Stack, then apply any overrides.
This is done iteratively for each physical stack file in the order they are defined in the import
section of the Stack file.
Note, the logical representation is never influenced by file paths or directories. It's only influenced by the configuration itself.
Schema
A Stack file contains a manifest defined in YAML that follows a simple, extensible schema. In fact, every Stack file follows exactly the same schema, and every setting in the configuration is optional. Enforcing a consistent schema ensures we can easily import and deep-merge configurations and use inheritance to achieve DRY configuration.
stack.yaml
Stack Attributes
components
The
components
is the list of all the building blocks.Example:
components:
sometool: # "sometool" can be any tool
somecomponent: # "somecomponent" can be the name of any "sometool" component
vars: # etc...components.terraform
So for
terraform
, it might look something like this:components:
terraform:
vpc:
vars: # etc...
Stack Files
Stack files can be very numerous in large cloud environments (think many dozens to hundreds of stack files). To enable the proper organization of stack files, SweetOps has established some conventions that are good to follow. However, these are just conventions, and there are no limits enforced by the tool.
By convention, we recommend storing all Stacks in a stacks/
folder at the root of your infrastructure repository. This way it's clear where they live and helps keep the configuration separate from your code (e.g. HCL).
The filename of individual environment stacks can follow any convention, and the best one will depend on how you model environments at your organization.
Basic Layout
A basic form of organization is to follow the naming pattern where each $environment-$stage.yaml
is a file. This works well until you have so
many environments and stages.
For example, $environment
might be ue2
(for us-east-2
) and $stage
might be prod
which would result in stacks/ue2-prod.yaml
Some resources, however, are global in scope. For example, Route53 and IAM might not make sense to tie to a region. These are what we call "global
resources". You might want to put these into a file like stacks/global-region.yaml
to connote that they are not tied to any particular region.
Hierarchical Layout
We recommend using a hierarchical layout that follows the way AWS thinks about infrastructure. This works very well when you may have dozens or hundreds of accounts and regions that you operate in. Use Catalogs to organize your Stack configurations.