Skip to main content
emnode
Compliance

Migrate ASGs from Launch Configurations to Launch Templates

Security Hub AutoScaling.9: Launch Configurations are deprecated and missing modern features. Move every ASG to Launch Templates.

11 min·10 sections·AWS

Last reviewed

Remediates AWS Security Hub: AutoScaling.9

Launch Configurations vs Launch Templates: the basics

What is AutoScaling.9 actually checking?

An Auto Scaling Group (ASG) needs a recipe that tells it how to launch new instances: which AMI, which instance type, which key pair, which security groups, which user data. AWS has shipped two versions of that recipe over the years: the original Launch Configuration (LC) from 2011, and Launch Templates (LT) introduced in 2017. LCs are immutable single-version blobs; LTs are versioned, support every modern EC2 feature, and can be referenced by multiple ASGs at once.

Security Hub control AutoScaling.9 fails any ASG that still uses an LC instead of an LT. AWS announced back in October 2021 that it would no longer add support for new EC2 features to Launch Configurations, and has since begun restricting new accounts from creating them; if you're in a sufficiently old account you can still create LCs today, but the format is a dead end. AutoScaling.9 is AWS telling you: cut over now, while it's a planned migration, not later when a forced cutoff turns it into an incident.

An ASG "in violation" looks like this: the ASG has LaunchConfigurationName set instead of LaunchTemplate. Functionally it still scales; structurally it's locked out of IMDSv2 enforcement, Mixed Instance Policies, Spot Allocation Strategies, T-instance unlimited mode, Capacity Reservations, and the version pinning that makes safe rollouts possible. Every one of those gaps is a feature your security and FinOps teams already want.

In this lesson you'll learn why AWS deprecated Launch Configurations, what Launch Templates unlock, and how to migrate a running ASG with zero downtime. You'll see the CLI calls to discover LC-backed ASGs, create an equivalent Launch Template from existing settings, attach it to the ASG, and roll the fleet via instance refresh, plus the field-mapping gotchas that trip up most migrations.

Fun fact

Created in 2017, still ignored in 2024

Launch Templates have been available since November 2017. In October 2021, nearly four years later, AWS announced on the Compute Blog that Auto Scaling would no longer add support for new EC2 features to Launch Configurations, telling everyone to migrate. Yet plenty of accounts still hadn't moved years on, leaving fleets stuck on a frozen format that locks out Mixed Instances Policies and Spot blending and predates a stack of modern hardening defaults. AutoScaling.9 exists because the nudge of a blog post didn't move everyone.

Migrating an ASG in action

Marco runs platform engineering at a fintech. A quarterly Security Hub review hands him a list of 23 AutoScaling.9 findings across production accounts: every ASG older than 2020 still backed by a Launch Configuration. The CISO wants IMDSv2 enforced everywhere by quarter-end, which means the LCs have to go first.

He picks the lowest-risk ASG to migrate first: a stateless API tier with rolling deploys already wired up. The plan is straightforward: inventory the LC's settings, create an equivalent LT with IMDSv2=required, attach it to the ASG, then trigger an instance refresh to roll the fleet.

He starts by listing every ASG and flagging which ones are still LC-backed.

First, find every ASG that still uses a Launch Configuration. The query slices to just the fields that decide whether AutoScaling.9 fires.

$ aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[*].{Name:AutoScalingGroupName,LC:LaunchConfigurationName,LT:LaunchTemplate.LaunchTemplateName}" --output table
┌────────────────────────┬──────────────────────┬──────────────────────┐
│ Name │ LC │ LT │
├────────────────────────┼──────────────────────┼──────────────────────┤
│ prod-api-asg │ prod-api-lc-2019 │ None │
│ prod-worker-asg │ prod-worker-lc-v3 │ None │
│ prod-batch-asg │ None │ prod-batch-lt │
│ staging-api-asg │ staging-api-lc-old │ None │
└────────────────────────┴──────────────────────┴──────────────────────┘
# Three ASGs still LC-backed. Each one is an AutoScaling.9 finding.

ASG inventory: anything in the LC column needs migrating.

Next, create a Launch Template that mirrors the existing LC's settings, plus IMDSv2=required and any other modern hardening you want from day one.

$ aws ec2 create-launch-template --launch-template-name prod-api-lt --version-description initial-from-lc --launch-template-data file://prod-api-lt.json
{
"LaunchTemplate": {
"LaunchTemplateId": "lt-0fe3d2c1b4a5968e7",
"LaunchTemplateName": "prod-api-lt",
"DefaultVersionNumber": 1,
"LatestVersionNumber": 1
}
}
# LT created with IMDSv2 required, same AMI/instance type/SGs as the old LC.

New Launch Template, version 1, ready to attach.

What changes at the ASG API layerdeep dive

An ASG's launch source is one of three mutually exclusive fields: LaunchConfigurationName (the legacy single-version blob), LaunchTemplate (a versioned reference, optionally pinned to a specific version or $Latest/$Default), or MixedInstancesPolicy (a Launch Template plus Spot/On-Demand mixing rules). The migration call is a single update-auto-scaling-group that swaps one for the other; AWS clears the LC reference automatically when you set an LT.

Running instances keep running. The ASG only consults the launch source when it has to launch a new instance: for scale-out, replacement of a terminated host, or an instance refresh. That's what makes the migration safe online: you cut over the recipe at any time, then control rollout speed through instance refresh's MinHealthyPercentage and InstanceWarmup knobs.

Field mapping is mostly 1:1 but watch three gotchas. First, BlockDeviceMappings use a different JSON shape: LC uses string sizes ("VolumeSize": "100"), LT uses integers ("VolumeSize": 100). Second, the IAM instance profile is referenced by name in an LC but by ARN or name in an LT (ARN is safer, names get reused). Third, carry the metadata options across: an LC's --metadata-options maps to the LT's MetadataOptions block, so set HttpTokens: required (and HttpPutResponseHopLimit: 1) to enforce IMDSv2. The real reason to leave LCs behind isn't IMDSv2 (they support it) it's that they're frozen: no instance types added after January 1 2023, and no Mixed Instances Policies or Spot blending.

# Inspect the source LC's settings before writing the LT JSON.
aws autoscaling describe-launch-configurations \
  --launch-configuration-names prod-api-lc-2019 \
  --query 'LaunchConfigurations[0].{AMI:ImageId,Type:InstanceType,Key:KeyName,SGs:SecurityGroups,Profile:IamInstanceProfile,UserData:UserData}'

# AutoScaling.9 detection: any ASG with a non-null LC name is a finding.
aws autoscaling describe-auto-scaling-groups \
  --query "AutoScalingGroups[?LaunchConfigurationName!=null].AutoScalingGroupName"

What is the impact of staying on Launch Configurations?

The most direct impact is the features you can't have. LCs are frozen: AWS stopped adding new instance types to them after January 1 2023 and never added Mixed Instances Policies or Spot blending. (LCs can require IMDSv2 via their --metadata-options flag, so that one isn't the gap, but every other modern hardening default lives in the LT.) Migrating to a Launch Template is the moment to set HttpTokens: required and close out IMDSv1, the exact attack surface that exfiltrated Capital One's credentials in 2019. AutoScaling.9 and EC2.8 are best handled together: the LT cutover is the natural change window to enforce IMDSv2 across the fleet.

The second-order impact is FinOps. LCs can't reference a Mixed Instance Policy, which is the only way to combine Spot and On-Demand capacity in one ASG. Teams on LCs end up running parallel ASGs (one Spot, one On-Demand) and reinventing the allocation strategy AWS already provides for free. The Spot Allocation Strategies (price-capacity-optimized, capacity-optimized) that consistently produce 60-90% savings on stateless workloads are LT-only.

The third-order impact is operational. LCs are immutable: every change creates a brand-new LC that you have to re-attach to the ASG manually. LTs are versioned: you create version 2, point the ASG at $Latest, instance refresh rolls the fleet. Roll-back is one CLI call to flip the default version. On LCs, rollback means re-creating the old LC from memory or version control because the original is gone.

And there's the deprecation cliff. AWS has not announced a hard end-of-life date, but new accounts have been blocked from creating LCs since 2023. When the hard cutoff comes (AWS deprecation timelines historically run 18-36 months from announcement), every LC-backed ASG that hasn't migrated stops being able to launch new instances, which means it stops being able to scale or replace failed hosts. Migrating now is planned work; migrating then is an outage.

How do you migrate ASGs to Launch Templates safely?

Migration is a four-step loop per ASG. Done in order, every step is reversible and the fleet keeps serving traffic the whole way through.

1. Inventory every LC-backed ASG and IaC source

Run describe-auto-scaling-groups filtered on non-null LaunchConfigurationName to get the runtime list. Then check your CloudFormation/Terraform/CDK sources: if the LC is managed in IaC, you have to update the source concurrently or the next apply will recreate it. CloudFormation has AWS::EC2::LaunchTemplate; Terraform has aws_launch_template plus launch_template on aws_autoscaling_group. Don't migrate runtime and then let IaC drift it back.

2. Create the Launch Template with hardening built in

Pull the LC's current settings and translate them into LT JSON: AMI, instance type, key pair, security group IDs, user data (base64-encoded), IAM instance profile ARN. Add a MetadataOptions block with HttpTokens: required to enforce IMDSv2 from day one. This is the moment to close AutoScaling.9 and EC2.8 in the same change.

3. Attach the LT and roll the fleet via instance refresh

update-auto-scaling-group --launch-template LaunchTemplateId=lt-...,Version=$Latest swaps the source in place: existing instances keep running. Then start-instance-refresh with a sane MinHealthyPercentage (90 for stateless tiers, higher for stateful) drains and replaces each instance against the new template. Watch the refresh status; abort and roll back the LT version if anything goes wrong.

4. Prevent recurrence with AWS Config and SCPs

Enable the AWS Config managed rule autoscaling-launch-template (the same rule that backs AutoScaling.9) to fail-fast on any ASG that's still LC-backed. For prevention at the org level, attach a Service Control Policy that denies autoscaling:CreateLaunchConfiguration outright: there's no legitimate reason to create a new LC in 2026. Add an alarm on the AutoScaling.9 control so a fresh finding pages someone before it ages.

# Swap the ASG's launch source from LC to LT (running instances are untouched).
aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name prod-api-asg \
  --launch-template LaunchTemplateId=lt-0fe3d2c1b4a5968e7,Version='$Latest'

# Roll the fleet to the new template at 90% min-healthy.
aws autoscaling start-instance-refresh \
  --auto-scaling-group-name prod-api-asg \
  --preferences '{"MinHealthyPercentage":90,"InstanceWarmup":120}'

# Verify the LC reference is gone; AutoScaling.9 should clear on next eval.
aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names prod-api-asg \
  --query 'AutoScalingGroups[0].{LC:LaunchConfigurationName,LT:LaunchTemplate}'

Quick quiz

Question 1 of 5

You've inventoried 23 LC-backed ASGs. What's the right first move for the lowest-risk ASG?

You've completed Migrate ASGs from Launch Configurations to Launch Templates. You can now find every LC-backed ASG in an account, translate its settings into a Launch Template with IMDSv2 enforced, swap the launch source in place, and roll the fleet via instance refresh without touching running traffic. The next time AutoScaling.9 fires, you'll have a four-step loop ready to run, and you'll close EC2.8 in the same change window.

Back to the library