The console throws a warning when you open the ASG that has been running since 2021: “This Auto Scaling group uses a launch configuration. AWS no longer supports creating new launch configurations.” The ASG itself still works — instances launch, scale out, scale in. Nothing is broken. But the team needs to add spot instances to absorb the morning peak, and mixed instances policy is launch-template-only. There is no path forward without migrating. The migration is mechanical but not zero-risk, and the way the existing LC encodes the AMI, user data, and IAM profile leaves a few sharp edges.
What’s happening
Launch configurations were the original way to describe how an ASG should boot an instance: AMI, instance type, key pair, security groups, user data, IAM instance profile. One shot, immutable. To change anything, you created a new LC, attached it to the ASG, and waited for the next instance refresh. Launch templates do everything LCs did and more, with one structural difference: templates are versioned. The ASG references a template version (or $Latest, or $Default), so changing the AMI is now a new template version rather than a new resource.
The features you cannot get with a launch configuration:
- Mixed instances policy. On-demand base + spot for surge, instance type diversification across families. LC-backed ASGs are single-instance-type only.
- Multiple network interfaces. Useful for instances that need both a public and a private interface, or that talk to different VPCs.
- Instance metadata service v2 (IMDSv2) enforcement. You can require token-based metadata access at the launch level, not just per instance.
- Capacity reservations and dedicated host placement. LC has neither.
T2/T3unlimited credit mode at the launch level. You can pin burstable behavior in the template rather than per instance.- License Manager configurations. For BYOL workloads.
- Tag specifications applied at instance and volume launch. LC only tagged the instance and required separate
aws ec2 create-tagsfor volumes.
The features that look identical but are not:
- User data. Both accept it as base64. Launch templates allow per-version user data, which means the rollback path for a bad user-data change is
update-auto-scaling-group --launch-template Version=N-1rather than recreating an LC. - AMI reference. LCs hold an AMI ID. Launch templates also hold an AMI ID, but you can change it by creating a new version of the same template. The ASG referencing
$Latestpicks up the change on the next instance refresh, which is closer to how teams already think about deploys. - Block device mappings. Both support them. Launch templates let you mark a volume as encrypted by default and reference a KMS key, which LC only partially supported.
The structural feature that catches teams: launch templates have a concept of $Default and $Latest. $Latest is always the highest version number. $Default is whichever version you marked as default — often the last known-good. ASGs that reference $Default will not pick up an in-progress new version until you bless it. ASGs that reference $Latest will. Pick one model and document it.
How to verify
Find every ASG still backed by a launch configuration:
aws autoscaling describe-auto-scaling-groups \
--query 'AutoScalingGroups[?LaunchConfigurationName!=null].[AutoScalingGroupName,LaunchConfigurationName]' \
--output table
For each LC, pull the full configuration so you have a side-by-side reference before deleting it:
aws autoscaling describe-launch-configurations \
--launch-configuration-names my-app-prod-lc-2021 \
--query 'LaunchConfigurations[0]' \
--output json > my-app-prod-lc-2021.json
The file holds every field the migration needs to translate: ImageId, InstanceType, KeyName, SecurityGroups, IamInstanceProfile, UserData (base64), BlockDeviceMappings, EbsOptimized, InstanceMonitoring, SpotPrice if any, AssociatePublicIpAddress, and MetadataOptions.
AWS provides a copy helper that creates a launch template from an existing launch configuration. Use it for a starting point, not the final artifact:
aws ec2 create-launch-template \
--launch-template-name my-app-prod-lt \
--source-version 1 \
--launch-template-data file://my-app-prod-lc-2021.json
The helper preserves what it can but misses two things teams hit: the security group format (LCs use security group names, launch templates require IDs in non-default VPCs), and IAM instance profile (LCs accept IamInstanceProfile as a name string, launch templates require it as {"Name": "..."} or {"Arn": "..."}). Open the resulting template JSON and reconcile those two fields manually before launching anything.
Verify the template renders an identical instance before swapping the ASG. Spin up one instance from the template into a non-production VPC:
aws ec2 run-instances \
--launch-template LaunchTemplateName=my-app-prod-lt,Version=1 \
--subnet-id subnet-0123456789abcdef0
SSH in. Confirm the AMI, the IAM role visible via curl http://169.254.169.254/latest/meta-data/iam/info, the user-data ran, the security groups attached, the volume sizes and encryption. The “looks identical” check is what catches the security-group-name-vs-id translation miss.
The fix
The migration on a live ASG, with no instance refresh disruption:
-
Snapshot the current state.
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names my-app-prod-asg --output json > my-app-prod-asg-pre-migration.json. This is the rollback artifact. -
Create the launch template by translating the LC fields, doing the security-group-id and IAM-instance-profile reconciliation, and adding any new features the team has been waiting for (IMDSv2 enforcement is the most common). Version 1 of the template is the LC equivalent. Resist the urge to roll in changes during the migration — translate first, change second.
-
Test-launch one instance out of band, in a non-prod VPC, with the same template version. Confirm everything from the verify step.
-
Swap the ASG reference. This is the critical moment:
aws autoscaling update-auto-scaling-group \ --auto-scaling-group-name my-app-prod-asg \ --launch-template LaunchTemplateName=my-app-prod-lt,Version='$Default'The running instances are unaffected. Future scale-out events will use the template. The LC is now orphaned but still attached as historical metadata.
-
Roll the fleet via an instance refresh, with a sensible minimum-healthy-percentage. Do not roll the whole fleet at once on the first migration:
aws autoscaling start-instance-refresh \ --auto-scaling-group-name my-app-prod-asg \ --preferences MinHealthyPercentage=90,InstanceWarmup=300Watch CloudWatch for the application’s actual health signal, not just
InService. The health-check-grace-period mistake during a refresh is its own KB topic — see EC2 vs ELB health checks. -
Decommission the launch configuration after the refresh completes and you have run a full business cycle on the template.
aws autoscaling delete-launch-configuration --launch-configuration-name my-app-prod-lc-2021.
The gotchas teams hit on 5-year-old ASGs, in order of how often they bite:
Security group name vs ID. LCs from before VPCs were the default could accept SG names. Launch templates in any non-default VPC require IDs. The copy helper silently leaves the name in, the test launch fails with “InvalidGroup.NotFound”, and the team thinks the LC was misconfigured. It was not — the format requirement changed.
IAM instance profile shape. Same root cause. The copy helper passes the string through unchanged. The launch returns a “missing required field” error that does not name the field clearly.
Metadata options default. New launch templates default to IMDSv2 optional (token-or-no-token). If you want to enforce v2-only across the fleet, set HttpTokens=required and HttpEndpoint=enabled in MetadataOptions. Some legacy software (very old SDKs, old cloud-init versions) does not handle v2 — verify in the test launch.
Tag inheritance. LCs propagated tags to instances. Launch templates use TagSpecifications for both the instance and the EBS volumes. Migrate the ASG’s own tag-propagation settings and the template’s TagSpecifications separately — they are not redundant, they cover different resources at different times.
The migration is mechanical when you treat it as a translation exercise rather than a feature upgrade. Translate the LC to a v1 template that boots identical instances. Roll the fleet on that. Then layer in mixed instances, IMDSv2 enforcement, warm pools, and the rest as separate, reviewed changes — each one a new template version with a clean rollback path.
Stack Harbor operates ASG-backed application tiers and cost-capped auto-scaling policies as part of Managed AWS and the Auto-Scaling operating layer. If your scale events are still surprises, start with an infrastructure consult.