Terraform AWS Launch Configuration: Immutability, Lifecycle, and Production Patterns

Creating reliable Auto Scaling Groups in AWS with Terraform requires a precise understanding of launch configurations and how Terraform handles their immutable nature. Launch configurations define the instance template an Auto Scaling Group uses to launch EC2 instances, and because they cannot be modified after creation, the entire update workflow revolves around replacement, naming strategy, and safe dependency ordering.

Introduction

Terraform makes it possible to declaratively define an Amazon EC2 Auto Scaling launch configuration with aws_launch_configuration. The resource allows you to specify launch configuration details such as the Amazon Machine Image, instance type, key pair, security groups, and block device mapping when creating a launch configuration using Terraform.

The core challenge is immutability. A launch configuration is an instance configuration template that an Auto Scaling group uses to launch EC2 instances. Launch configurations are immutable — you can’t modify a launch configuration after you’ve created it. To change the launch configuration for an Auto Scaling group, you must create a new launch configuration and then update your Auto Scaling group with it.

This article covers the cardinality rules, immutable replacement behavior, required and optional arguments, lifecycle management with create_before_destroy, and practical patterns for AMI updates and Spot pricing.

Cardinality and Relationship Rules

Understanding how launch configurations relate to Auto Scaling groups prevents design errors.

There is 1-to-many cardinality between launch config and autoscaling groups. You can specify 1 launch configuration for many Auto Scaling groups. However, 1 ASG can have only 1 launch configuration at a time.

In Terraform, the aws_launch_configuration resource allows you to create and manage an Amazon EC2 Auto Scaling launch configuration. The aws_autoscaling_group resource then references the launch configuration by name.

Example from reference material:

```
resource "awslaunchconfiguration" "example" {
name = "example"
imageid = "ami-002"
instance
type = "t2.micro"
}

resource "awsautoscalinggroup" "example" {
launchconfiguration = awslaunchconfiguration.example.id
min
size = 1
max_size = 2
}
```

When image_id in the Launch Configuration is changed, Terraform will replace the Launch Configuration and update the Auto Scaling Group accordingly. The Auto Scaling Group is being updated in place, meaning it’s being modified without being destroyed and recreated. The change in the Auto Scaling Group is in the launch_configuration attribute.

Immutability and Replacement Behavior

Terraform treats a Launch Configuration as replace-on-change. In order to update a Launch Configuration, Terraform will destroy the existing resource and create a replacement.

A typical plan for an AMI change shows the replacement pattern:

```

module.ec2.awsautoscalinggroup.example_asg will be updated in-place

~ resource "awsautoscalinggroup" "exampleasg" {
id = "acme-us-east-2"
~ launch
configuration = "acme-us-east-2-20231009074215927400000001" -> (known after apply)
name = "acme-us-east-2"
# (23 unchanged attributes hidden)
}

module.ec2.awslaunchconfiguration.example_lc must be replaced

+/- resource "awslaunchconfiguration" "examplelc" {
~ arn = "arn:aws:autoscaling:us-east-2:204004877656:launchConfiguration:d0737a01-dab9-4592-a147-af755884b25f:launchConfigurationName/acme-us-east-2-20231009074215927400000001" -> (known after apply)
~ id = "acme-us-east-2-20231009074215927400000001" -> (known after apply)
~ image
id = "ami-001" -> "ami-002" # forces replacement
~ name = "acme-us-east-2-20231009074215927400000001" -> (known after apply)
# (9 unchanged attributes hidden)
}
```

Plan: 1 to add, 1 to change, 1 to destroy.

This is why regular maintenance such as AMI updates for CVE removals requires a new launch configuration rather than an in-place edit. We are often updating the AMIs we’re using due to security issues and CVE discovery.

Safe Update Pattern with Lifecycle

Because replacement destroys first by default, a common failure mode is that the Auto Scaling Group temporarily references a non-existent launch configuration. The recommended pattern is to specify create_before_destroy in a lifecycle block.

Either omit the Launch Configuration name attribute, or specify a partial name with name_prefix. Example:

```
data "awsami" "ubuntu" {
most
recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}

resource "awslaunchconfiguration" "asconf" {
name
prefix = "terraform-lc-example-"
imageid = data.awsami.ubuntu.id
instancetype = "t2.micro"
lifecycle {
create
before_destroy = true
}
}

resource "awsautoscalinggroup" "bar" {
name = "terraform-asg-example"
launchconfiguration = awslaunchconfiguration.asconf.name
minsize = 1
max
size = 2
lifecycle {
createbeforedestroy = true
}
}
```

With this setup Terraform generates a unique name for your Launch Configuration and can then update the AutoScaling Group without conflict before destroying the previous Launch Configuration.

The name_prefix approach is preferred over a static name because Terraform can generate a new unique name on each replacement, avoiding name collisions during the create-before-destroy window.

Argument Reference and Configuration Options

The aws_launch_configuration resource supports a set of required and optional arguments.

Required arguments:

  • image_id - The EC2 image ID to launch.
  • instance_type - The size of instance to launch.

Optional arguments documented in reference material:

  • name - The name of the launch configuration. If you leave this blank, Terraform will auto-generate a unique name.
  • name_prefix - Creates a unique name beginning with the specified prefix. Conflicts with name.
  • iam_instance_profile - The IAM instance profile to associate with launched instances.
  • key_name - The key name that should be used for the instance.
  • security_groups - A list of associated security group IDs.
  • associate_public_ip_address - Associate a public ip address with an instance in a VPC.
  • vpc_classic_link_id - The ID of a ClassicLink-enabled VPC.
  • region - Region where this resource will be managed. Defaults to the Region set in the provider configuration.
  • ebs_block_device - Additional EBS block devices to attach to the instance.
  • ebs_optimized - If true, the launched EC2 instance will be EBS-optimized.
  • enable_monitoring - Enables/disables detailed monitoring.

Additional optional arguments:

  • spot_price - Set the spot instance pricing to be used for the Auto Scaling Group to reserve instances. Simply specifying the spot_price parameter will set the price on the Launch Configuration which will attempt to reserve your instances at this price.

A complete argument table:

Argument Type Required Description
image_id string Yes The EC2 image ID to launch
instance_type string Yes The size of instance to launch
name string No The name of the launch configuration
name_prefix string No Prefix for auto-generated unique name, conflicts with name
iaminstanceprofile string No IAM instance profile to associate
key_name string No Key pair name for the instance
security_groups list(string) No Security group IDs
associatepublicip_address bool No Associate public IP in VPC
spot_price string No Spot price for Spot instances
ebs_optimized bool No EBS optimized instance
enable_monitoring bool No Enable detailed monitoring

Example with Spot pricing:

resource "aws_launch_configuration" "as_conf" { image_id = data.aws_ami.ubuntu.id instance_type = "m4.large" spot_price = "0.001" lifecycle { create_before_destroy = true } }

Practical AMI Update Workflow

The aim of this page is to explain how to create a launch configuration for an EC2 instance using Terraform based on the particular example of updating an AMI attribute of launch configuration for an ASG due as part of regular maintenance.

A typical workflow uses a data source to find the latest AMI and feeds it into the launch configuration:

```
data "awsami" "ubuntu" {
most
recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"]
}

resource "awslaunchconfiguration" "asconf" {
image
id = data.awsami.ubuntu.id
instance
type = "m4.large"
spotprice = "0.001"
lifecycle {
create
before_destroy = true
}
}

resource "awsautoscalinggroup" "bar" {
name = "terraform-asg-example"
launchconfiguration = awslaunchconfiguration.asconf.name
}
```

When the data source returns a new AMI ID, Terraform plans a replacement of the launch configuration and an in-place update of the Auto Scaling Group to point to the new configuration name.

Launch Templates vs Launch Configurations

While the focus here is launch configurations, AWS now recommends launch templates for new designs. Launch templates offer versioning, more granular block device mapping, and user data handling.

A basic launch template example from reference material shows:

```
data "awsami" "al2023" {
most
recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}

resource "awslaunchtemplate" "app" {
nameprefix = "app-"
description = "Launch template for application servers"
image
id = data.awsami.al2023.id
instance
type = "t3.medium"
lifecycle {
createbeforedestroy = true
}
vpcsecuritygroupids = [
aws
securitygroup.app.id,
aws
securitygroup.monitoring.id,
]
iam
instanceprofile {
name = aws
iaminstanceprofile.app.name
}
userdata = base64encode(templatefile("${path.module}/scripts/bootstrap.sh", {
environment = var.environment
app
version = var.appversion
}))
tag
specifications {
resource_type = "instance"
tags = {
Name = "app-server"
Environment = var.environment
}
}
}
```

Launch templates are mutable via versioning, whereas launch configurations are immutable and must be replaced.

Best Practices Summary

  • Use name_prefix instead of a static name to allow Terraform to generate unique names on replacement.
  • Always set lifecycle { create_before_destroy = true } on both the launch configuration and the Auto Scaling Group to avoid downtime.
  • Reference AMIs via a data "aws_ami" block with most_recent = true and filters to enable automated patching.
  • For Spot usage, set spot_price on the launch configuration.
  • Prefer launch templates for new projects; keep launch configurations only where legacy Auto Scaling Groups require them.

Conclusion

Terraform launch configurations provide a declarative way to define instance templates for Auto Scaling Groups, but their immutable nature forces a replace-on-change lifecycle. The effective pattern is to combine name_prefix with create_before_destroy to generate unique names and avoid conflicts during updates, and to drive AMI changes through data sources so security patches propagate via controlled replacements.

The 1-to-many cardinality means one launch configuration can be shared across multiple Auto Scaling Groups, but each group can only reference one configuration at a time, making coordinated rollouts predictable. Optional features such as Spot pricing, EBS optimization, and monitoring flags add flexibility, while arguments like image_id and instance_type remain the core of the definition.

Understanding the plan output — where the Auto Scaling Group is updated in-place while the launch configuration is replaced — is key to safe production operations. For long-term designs, migrating to launch templates offers versioning and richer configuration, but the principles of create-before-destroy and unique naming remain essential.

Sources

  1. Explaining AWS Launch Configuration Changes via Terraform
  2. awslaunchconfiguration
  3. terraform/providers/aws/r/launch_configuration
  4. create-launch-templates-for-auto-scaling-in-terraform

Related Posts