17
votes

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.

2

2 Answers

22
votes

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 to do a few things:

  1. Configure the 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.
  2. Set the 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.
  3. Set the 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.

1
votes

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"
}