I have an AWS Auto-Scaling Group, a Launch Configuration, and an Auto-Scaling Group Policy defined in Terraform like this:
resource "aws_autoscaling_group" "default" {
name = "..."
health_check_type = "EC2"
vpc_zone_identifier = ["${...}"]
min_size = "${var.asg_capacity}"
max_size = "${var.asg_capacity * 2}"
desired_capacity = "${var.asg_capacity}"
launch_configuration = "${aws_launch_configuration.default.id}"
termination_policies = ["OldestInstance"]
}
resource "aws_autoscaling_policy" "default" {
name = "..."
autoscaling_group_name = "${aws_autoscaling_group.default.name}"
scaling_adjustment = "${var.asg_capacity}"
adjustment_type = "ChangeInCapacity"
cooldown = 300
}
resource "aws_launch_configuration" "default" {
name_prefix = "..._"
image_id = "${var.coreos_ami_id}"
instance_type = "${var.ec2_instance_type}"
iam_instance_profile = "${aws_iam_instance_profile.default.arn}"
key_name = "..."
security_groups = ["${aws_security_group.default.id}"]
user_data = "${data.template_file.cloud_init.rendered}"
lifecycle {
create_before_destroy = true
}
}
When I change my user data, a new launch configuration is created and then attached to the auto-scaling group. I would assume that this would cause the auto-scaling group to scale up by var.asg_capacity
instances, wait 300 seconds, and then tear down the old ones as per OldestInstance
.
When I have done similar things in CloudFormation, I have used the following configuration options:
ASG:
Type: AWS::AutoScaling::AutoScalingGroup
UpdatePolicy:
AutoScaleRollingUpdate:
# during a scale, 6 instances in service
MaxBatchSize: 3
MinInstancesInService: 3
PauseTime: PT5M
Properties:
...
Is there an analog for this in Terraform? I would really like my auto-scaling groups to change when I change the launch configuration.
I would assume that this would cause the auto-scaling group to scale up by var.asg_capacity instances, wait 300 seconds, and then tear down the old ones as per OldestInstance.
This assumption, unfortunately, is incorrect. When you change the launch configuration, the only thing that happens is a new launch configuration is created in your AWS account and associated with the Auto Scaling Group (ASG). That means all future Instances in that ASG will launch with the new launch configuration. However, merely changing the launch configuration does not trigger the launch of any instances, so you won't see your changes.
To force new instances to launch, you have two options:
In 2020, AWS introduced AWS instance refresh, which is a native way to roll out changes to Auto Scaling Groups. If you configure the instance_refresh block in your aws_autoscaling_group
resource, then when you make a change to the launch configuration and run apply
, AWS will kick off the instance refresh process automatically (note: this process runs in the background, so apply
will complete very quickly, but the refresh will happen after, often taking 5-30 min).
resource "aws_launch_configuration" "example" {
image_id = var.ami
instance_type = var.instance_type
user_data = data.template_file.user_data.rendered
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "example" {
name = "example-asg"
launch_configuration = aws_launch_configuration.example.id
availability_zones = data.aws_availability_zones.all.names
min_size = var.min_size
max_size = var.max_size
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 50
}
}
}
You can use some native Terraform functionality to do a rolling deploy:
name
parameter of the ASG to depend directly on the name of the launch configuration. That way, each time the launch configuration changes (which it will when you update the AMI or User Data), Terraform will try to replace the ASG.create_before_destroy
parameter of the ASG to true
, so each time Terraform tries to replace it, it will create the replacement before destroying the original.min_elb_capacity
parameter of the ASG to the min_size
of the cluster so that Terraform will wait for at least that many servers from the new ASG to register in the ELB before it'll start destroying the original ASG.Here's a rough idea of what the Terraform code would look like:
resource "aws_launch_configuration" "example" {
image_id = "${var.ami}"
instance_type = "${var.instance_type}"
user_data = "${data.template_file.user_data.rendered}"
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "example" {
name = "${var.cluster_name}-${aws_launch_configuration.example.name}"
launch_configuration = "${aws_launch_configuration.example.id}"
availability_zones = ["${data.aws_availability_zones.all.names}"]
min_size = "${var.min_size}"
max_size = "${var.max_size}"
min_elb_capacity = "${var.min_size}"
lifecycle {
create_before_destroy = true
}
}
For a fully working example, check out the zero-downtime deployment example code from the book Terraform: Up & Running.
As of version 3.22.0 of the AWS provisioner, you can add an instance_refresh configuration block to your aws_autoscaling_group
resource. The simplest config that can possibly work is:
instance_refresh {
strategy = "Rolling"
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With