Well-Architected Framework
Create immutable virtual machines
Immutable virtual machines package your entire application stack, including the operating system, dependencies, and application code, into unchangeable machine images that you build once and deploy across all environments. You build machine images with Packer, deploy them with Terraform, and never modify running virtual machines. When you need to make changes, you rebuild the image with your updates and deploy new virtual machines to replace the old ones. The following sections explain why you should use immutable virtual machines, how to build machine images with Packer, and how to deploy them with Terraform.
Why use immutable virtual machines
Immutable virtual machines address the following operational challenges:
Eliminate configuration drift: Immutable virtual machines prevent drift by deploying identical machine images, ensuring every VM across all environments matches your defined configuration exactly.
Prevent manual change errors: Manual updates introduce human error—typos in commands, forgotten steps, or changes applied to wrong servers cause outages. Immutable virtual machines remove the need for manual server access, applying all changes through tested, version-controlled machine images.
Enable reliable rollbacks: When VM updates fail, rolling back requires remembering what changed and manually reversing those changes across multiple servers. Immutable virtual machines enable rollbacks by redeploying the previous machine image version without troubleshooting what changed.
Build machine images with Packer
Building machine images involves defining your application, dependencies, and operating system configurations as code, then using that code to produce a cloud-specific machine image like an AWS AMI, Azure image, or GCP image. The image contains your complete application stack—the operating system, runtime dependencies, your application code, and system configurations. Once built, the image is immutable and ready for deployment across all environments.
Packer automates machine image creation by launching a temporary instance, provisioning it with your application and dependencies using tools like Ansible, shell scripts, or configuration management tools, creating an image snapshot, and terminating the temporary instance. The resulting machine image is tagged with version information and stored in your cloud provider's image repository.
Learn how to build machine images in Package applications with containers and machine images.
Deploy immutable VMs with Terraform
Terraform deploys immutable virtual machines by querying your most recent Packer-built machine image and creating VM instances from it. When you update your application, Terraform detects the new machine image and replaces old instances with new ones.
Query machine images dynamically
Terraform data sources query your cloud provider to find machine images built by Packer, removing hardcoded image IDs. Using data sources connects your Packer and Terraform workflows, ensuring Terraform always deploys your latest machine image.
The following Terraform configuration queries the most recent Packer-built AMI:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
data "aws_ami" "web_app" {
most_recent = true
owners = ["self"]
filter {
name = "name"
values = ["web-app-*"]
}
filter {
name = "tag:Built-By"
values = ["packer"]
}
}
output "ami_id" {
value = data.aws_ami.web_app.id
description = "Most recent web application AMI"
}
output "ami_name" {
value = data.aws_ami.web_app.name
description = "Name of the AMI"
}
The data source queries AWS for the most recent AMI with name pattern "web-app-*" and tag "Built-By: packer". Every time you run terraform plan or terraform apply, Terraform queries for the latest image, automatically detecting when you've built a new AMI with Packer. Using data sources eliminates manual AMI ID updates in your Terraform code.
Immutable VM workflow
The complete workflow for immutable virtual machines includes the following steps:
Build the machine image: Create a Packer template defining your application, dependencies, and OS configuration. Run
packer buildto create the machine image and upload it to your cloud provider.Create infrastructure code: Write Terraform configuration that uses data sources to query your most recent Packer-built image. Define Auto Scaling Groups with instance refresh enabled to handle immutable updates.
Deploy the infrastructure: Run
terraform applyto create your Auto Scaling Group and related infrastructure. Terraform queries the latest machine image and deploys instances from it.Update and redeploy: When you need to update your application, modify your Packer template, run
packer buildto create a new machine image, then runterraform apply. Terraform detects the new image and triggers an instance refresh, gradually replacing old instances with new ones.Rollback if needed: If the new version causes issues, run
packer buildwith your previous Packer template to recreate the old image, then runterraform apply. Terraform triggers another instance refresh to deploy the previous working version.
You can improve the workflow with the following:
- Automate image builds: Use CI/CD systems like GitHub Actions to automatically run
packer buildwhen you commit code changes. - Automate deployments: Use HCP Terraform with VCS-driven workflows to automatically run
terraform applywhen Packer builds complete. - Track image metadata: Use HCP Packer to store metadata about your machine images, tracking which Git commit built each image, which environments are running each version, and the full ancestry of your image builds.
- Retrieve secrets dynamically: Use Vault to provide secrets to instances at startup through user data scripts, preventing secrets from being embedded in machine images.
HashiCorp resources:
- Learn about immutable infrastructure concepts and benefits
- Create immutable containers with Packer
- Learn how to package applications with Packer for virtual machines
- Learn how to deploy infrastructure with Terraform
- Implement automated testing for infrastructure and machine images
- Implement semi-automated deployments with machine images
- Implement fully-automated deployments with image CI/CD
- Use infrastructure as code to deploy machine images
Packer documentation and tutorials:
- Read the Packer documentation for core image building concepts
- Follow hands-on Packer tutorials for machine image creation
- Build images in AWS, Azure, or GCP
- Use Packer provisioners to configure machine images
- Use the Packer Ansible provisioner for configuration management
- Automate Packer with GitHub Actions
- Learn about HCP Packer for image metadata tracking
Terraform documentation and tutorials:
- Get started with Terraform tutorials for hands-on examples
- Read the Terraform documentation for comprehensive features
- Provision infrastructure with Packer and Terraform
- Use the AWS AMI data source to query Packer-built images
- Learn about Auto Scaling Groups for immutable VM deployments
Next steps
In this section of Define your processes, you learned why immutable virtual machines increase reliability, how to build machine images with Packer, and how to deploy them with Terraform using Auto Scaling Groups. Create immutable virtual machines is part of the Define and automate processes pillar.
Related topics:
To learn more about immutable infrastructure, continue to the following topics:
- Overview of immutable infrastructure concepts
- Create immutable containers