Well-Architected Framework
Deploy atomic infrastructure components
Atomic deployments deploy infrastructure as small, isolated units of change rather than deploying all infrastructure components together. Large, monolithic deployments that modify multiple systems simultaneously create risk, make troubleshooting difficult, and slow down rollback operations when issues occur. Deploy infrastructure atomically to reduce blast radius, enable independent team workflows, and accelerate your deployment velocity.
Why deploy atomically
Deploying infrastructure atomically addresses the following operational and security challenges:
Reduce deployment risk and blast radius: Monolithic deployments that change many infrastructure components can make it difficult to identify which change caused failures. Atomic deployments isolate changes to specific components, limiting the impact of failures.
Enable faster rollback capabilities: When deployments fail, large change sets make it difficult to identify and revert the problematic changes without affecting working components. Atomic deployments allow you to rollback specific infrastructure components independently, reducing downtime and preserving working infrastructure.
Improve troubleshooting and root cause analysis: Deployments that modify dozens of resources across multiple systems create complex failure scenarios. Atomic deployments limit the scope of changes, making it easier to identify which specific modification caused issues. Smaller change sets reduce the variables you need to investigate.
Accelerate deployment velocity: Monolithic deployments require coordination across teams and force all changes to deploy together on the same schedule. Atomic deployments enable teams to deploy their infrastructure components independently, removing coordination bottlenecks and allowing faster iteration on specific components.
Organize Terraform modules for atomic deployments
Structure your Terraform code into independent modules that define logical deployment boundaries. Each module manages a specific infrastructure component with its own lifecycle, enabling teams to deploy, test, and rollback components independently.
Use separate modules when infrastructure components have different deployment schedules, ownership by different teams, or different rates of change. For example, networking infrastructure changes less frequently than application infrastructure, so separating them into different modules allows application teams to deploy without touching networking.
The following example shows a Terraform directory structure organized for atomic deployments:
infrastructure/
├── networking/
│ ├── main.tf # VPC, subnets, routing
│ ├── variables.tf
│ └── outputs.tf
├── database/
│ ├── main.tf # RDS instances
│ ├── variables.tf
│ └── outputs.tf
├── application/
│ ├── main.tf # EC2, ECS, or Kubernetes resources
│ ├── variables.tf
│ └── outputs.tf
└── monitoring/
├── main.tf # CloudWatch, alerting
├── variables.tf
└── outputs.tf
The directory structure creates four atomic deployment units. The networking team can deploy VPC changes independently, database administrators can deploy RDS updates separately, and application teams can deploy compute resources without coordinating with other teams. Each module contains only the resources it manages, creating clear boundaries.
Automate atomic deployments with CI/CD
Integrate atomic deployments into CI/CD pipelines to automate testing and deployment of individual infrastructure components. Configure pipelines to detect changes in specific module directories and deploy only the affected modules, reducing deployment time and risk.
The following example shows a GitHub Actions workflow that deploys atomic modules based on changed files:
name: Deploy Infrastructure
on:
push:
branches: [main]
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
networking: ${{ steps.changes.outputs.networking }}
database: ${{ steps.changes.outputs.database }}
application: ${{ steps.changes.outputs.application }}
steps:
- uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: changes
with:
filters: |
networking:
- 'infrastructure/networking/**'
database:
- 'infrastructure/database/**'
application:
- 'infrastructure/application/**'
deploy-networking:
needs: detect-changes
if: needs.detect-changes.outputs.networking == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy networking
run: |
cd infrastructure/networking
terraform init
terraform plan
terraform apply -auto-approve
deploy-database:
needs: [detect-changes, deploy-networking]
if: needs.detect-changes.outputs.database == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy database
run: |
cd infrastructure/database
terraform init
terraform plan
terraform apply -auto-approve
deploy-application:
needs: [detect-changes, deploy-networking, deploy-database]
if: needs.detect-changes.outputs.application == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy application
run: |
cd infrastructure/application
terraform init
terraform plan
terraform apply -auto-approve
The GitHub Actions workflow detects which modules changed and deploys only the affected components. If only application code changes, the pipeline deploys only the application module, skipping networking and database. Deploying only changed modules reduces deployment time and limits the scope of changes in each deployment.
Identify atomic deployment boundaries
Define atomic deployment boundaries based on infrastructure component lifecycles, team ownership, and dependency relationships. Components that change together should be deployed together, while components with independent lifecycles should be separated.
Use the following criteria to identify atomic boundaries:
Separate modules when:
- Different teams own and maintain the infrastructure components
- Components have different deployment schedules or change frequencies
- Rollback of one component should not affect others
- Components have different compliance or security requirements
Keep modules together when:
- Resources have tight coupling and always change together
- Components share the same lifecycle and deployment schedule
- Separating them creates excessive orchestration complexity
- Dependencies between resources make independent deployment difficult
For example, separate networking infrastructure like VPCs, subnets, and routing, from application infrastructure like EC2 instances and load balancers, because they change at different frequencies and have different owners. Keep an EC2 instance and its security group together because they are tightly coupled and always deploy together.
Avoid using the Terraform -target flag to deploy individual resources within a module, as this breaks dependency tracking and can create inconsistent state. Instead, organize resources into appropriately-sized modules that define your atomic boundaries.
HashiCorp resources:
- Implement zero-downtime deployments with atomic infrastructure components
- Learn how to automate deployments with Terraform
- Implement automated testing for atomic components
- Use infrastructure as code to define atomic boundaries
- Implement a GitOps workflow for atomic deployments
Terraform foundations for atomic deployments:
- Read the Terraform introduction to understand infrastructure as code
- Get started with Terraform tutorials for hands-on examples
- Learn about Terraform state for tracking infrastructure components
- Understand backend configuration for separate state files
Terraform modules for atomic components:
- Read the Terraform modules documentation for module basics
- Learn how to organize Terraform configuration
- Learn to create reusable modules
- Read about module composition
- Use module sources for organizing atomic components
- Learn about input variables for module configuration
- Use module outputs for sharing data between atomic components
Automating atomic deployments:
- Automate Terraform with GitHub Actions
- Read about HCP Terraform workspaces for managing atomic components
- Learn about HCP Terraform run triggers for orchestrating atomic deployments
- Use VCS-driven workflows for automated atomic deployments
Next steps
In this section of Deploy with confidence, you learned how to implement atomic deployments for infrastructure by organizing Terraform modules, managing separate state files, and automating deployments with CI/CD. Atomic deployments are part of the Define and automate processes pillar.
Refer to the following documents to learn more about deployment strategies:
- Implement zero-downtime deployments with blue/green, canary, and rolling strategies
- Automate deployments with Terraform and container orchestrators
- Test infrastructure with Terraform test and Sentinel before deploying
- Create reusable modules to standardize infrastructure components