Course outline

4.3

User Data, AMIs, IMDS and Launch Templates

Bootstrap on first boot, bake golden images, read instance metadata safely with IMDSv2, and template launches.

22 min Reading + animations

Tip: tap or hover any dotted-underlined term to see what it means.

By the end of this lesson you will be able to

  • Write a user data script that configures an instance on its first boot, and know when it runs, as whom, and where its log goes.
  • Explain what an AMI contains, where AMIs come from, and why an AMI belongs to one Region.
  • Choose between baking a golden AMI and bootstrapping with user data, and combine the two.
  • Read instance metadata safely with IMDSv2, and explain why requiring IMDSv2 stops SSRF credential theft.
  • Use launch templates and their versions, and explain why launch configurations are legacy.

#From a blank server to a ready one

When an instance starts, it is only as useful as the software on its disk. A web server needs a web server program, your application, its settings, a monitoring agent, security patches. There are three ways to get them there:

  1. By hand: log in after launch and type the commands. Slow, error-prone, and impossible when an Auto Scaling group launches ten servers at 3 a.m.
  2. At boot, with user data: hand EC2 a script at launch, and the instance runs it on its first boot. This is called bootstrapping.
  3. Ahead of time, in an image: install everything once, save the disk as an AMI (Amazon Machine Image), and launch every new server from that finished image. This is called baking.

A launch template then writes down the whole recipe (which AMI, which instance type, which security group, which user data) so every launch is identical. And while the instance runs, the instance metadata service (IMDS) tells it who it is and hands it the credentials of its IAM role.

#User data: a script for the first boot

User data is text you give EC2 when you launch an instance. On Linux it is usually a shell script starting with #!; it can also be a #cloud-config file that lists settings instead of commands. A program inside the operating system, cloud-init (on Amazon Linux, Ubuntu, Red Hat and most Linux AMIs), fetches it from the instance metadata service and runs it.

#!/bin/bash
dnf install -y httpd
echo "<h1>Hello from $(hostname -f)</h1>" > /var/www/html/index.html
systemctl enable --now httpd

How user data behaves:

BehaviourDetail
Runs onceBy default, only on the first boot of the instance. Reboots, stop/start and resuming from hibernation do not run it again.
Runs as rootNo sudo needed. It can install packages, write anywhere, start services.
Runs late in bootNear the end of boot, after the network is up. The instance can show “running” before the script has finished.
Size limit16 KB of raw data (before base64 encoding). For more, have the script download a bigger script from S3.
LogEverything the script prints goes to /var/log/cloud-init-output.log.
Editable only when stoppedYou can change user data only while the instance is stopped, and the new script still does not run by itself.
Readable by the instanceAny process on the instance can read it from the metadata service, and anyone allowed to describe the instance’s attributes can read it through the API. Never put passwords or keys in user data.

On Windows the same idea works through the EC2Launch v2 agent: the script goes between <powershell> and </powershell> tags, and its log is under C:\ProgramData\Amazon\EC2Launch\log\.

Typical jobs for user data: install updates and packages, download application files, write a config file for this environment, register with a monitoring or configuration tool, join a cluster.

The first boot of an instance with user data

You launch an instance with a script in user data. Watch who runs it, when, and what happens when it fails or the instance restarts.

Scenario

Youconsole or CLI
EC2 instancei-0abc
cloud-initruns inside the OS
IMDS169.254.169.254
Package repository
launch + user data
Step 1 of 8
1

You launch an instance and paste a bash script into the user data field (up to 16 KB). EC2 stores it with the instance; nothing runs yet.

#What goes wrong with user data

  • The script fails halfway. A download without an internet path (private subnet, no NAT gateway or VPC endpoint), a typo, a mirror that is down. The instance still shows running and passes both status checks, because those test the hardware and whether the operating system answers, not your script. Start scripts with set -euo pipefail so they stop at the first error, read /var/log/cloud-init-output.log, and let a load balancer health check catch a server that never became ready.
  • “I changed it and nothing happened.” User data does not run again after the first boot. To run something on every boot, put a script in /var/lib/cloud/scripts/per-boot/, or tell cloud-init to run user scripts always (on Windows, add <persist>true</persist>). For a one-off change to running servers, use Systems Manager Run Command, or bake a new AMI.
  • Secrets in user data. Anything in user data is readable from the instance and by anyone with permission to view the instance’s attributes. Let the script fetch secrets at boot from Secrets Manager or Systems Manager Parameter Store, using the instance’s IAM role.
  • Slow boots. A script that compiles code or downloads gigabytes makes every new instance take many minutes to become useful. That is the moment to bake an AMI.

#AMIs: the image an instance boots from

Every instance is launched from an AMI. The AMI decides the operating system and everything already installed on the root disk.

#What is inside an AMI

An AMI is not one file. It is a registered record that ties together:

  • One or more EBS snapshots: a point-in-time copy of the root volume (operating system, software, files) and of any extra data volumes. (Older instance-store-backed AMIs keep a root volume template in S3 instead; they are rare today and their instances cannot be stopped.)
  • A block device mapping: which volumes to create at launch, their size and type, and which device names they get.
  • Launch permissions: who may launch it: only your account, specific accounts, an organization or organizational unit, or everyone (public).
  • Metadata: architecture (x86_64 or arm64), boot mode, virtualization type, a name, a description, tags.

An AMI itself has no charge; you pay for the snapshot storage behind it (and for the software, if it comes from the Marketplace).

#Where AMIs come from

SourceExamplesNotes
Provided by AWSAmazon Linux 2023, Windows Server, Ubuntu, Red HatMaintained and patched by AWS or the vendor; the usual starting point
AWS MarketplaceA firewall appliance, a hardened OS, a database with its licenceSold by vendors; some charge per hour on top of the instance price
Community (public) AMIsShared publicly by anyoneConvenient but unverified; check the owner before trusting one
Your ownYour golden image, built from one of the abovePrivate to your account until you share it

AMI IDs differ in every Region, and AWS publishes new IDs whenever it patches an image. Instead of hard-coding an ID, look up the latest one from the public Systems Manager parameters, for example /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64, which give the current ID in whatever Region you ask.

#Creating your own AMI

  1. Start from a base AMI

    Launch an instance from a trusted base image, such as Amazon Linux 2023.

  2. Customise it

    Install packages, your application, agents and security settings. Remove anything instance-specific: temporary files, SSH host keys you do not want shared, logs, credentials.

  3. Create the image

    Run create image on the instance. EC2 snapshots its EBS volumes and registers a new AMI. By default EC2 reboots the instance first so the file system is consistent; --no-reboot avoids the downtime but risks a snapshot taken mid-write.

  4. Launch from it

    Every instance launched from the new AMI starts with that exact disk. Keep a version in its name (web-2026-10-24) so you can roll back.

#An AMI lives in one Region

An AMI is built for a specific AWS Region, and its ID only exists there. You cannot launch an instance in another Region with it, but you can copy the AMI to the target Region and launch from the copy.

What to know about copying:

  • copy-image runs in the destination Region and copies the AMI and its snapshots across. The copy gets a new AMI ID.
  • From then on the two AMIs are independent. A new version in the source Region must be copied again.
  • Launch permissions are not copied; share the copy again if other accounts need it.
  • You can encrypt an unencrypted AMI during the copy, or re-encrypt it with a different key. You cannot remove encryption by copying.
  • Copying is also how you take a copy you own of an AMI another account shared with you.

Golden AMI vs bootstrapping every instance

An Auto Scaling group needs three new web servers. Compare installing everything at boot with launching from a prepared image, then take that image to another Region.

Scenario

us-east-1
Auto Scaling group
Package repository
web-1
web-2
web-3
Step 1 of 3
1

Traffic spikes and the group launches three instances from a plain Amazon Linux AMI. Each one runs a long user data script.

#Sharing AMIs with other accounts

You share an AMI by changing its launch permissions: add an account ID, an organization or an organizational unit. The AMI stays in your account; the other account can launch it, or copy it to own a copy.

  • An AMI with encrypted snapshots can only be shared if it uses a customer managed key, and you must also give the other account permission to use that key. Snapshots encrypted with the default AWS managed key (aws/ebs) cannot be shared: copy the AMI with a customer managed key first.
  • Encrypted AMIs can never be made public.
  • Accounts can block public sharing of AMIs (newer accounts have this on by default), which prevents an image with secrets inside from being published by accident.

#Deregistering an AMI

When an image is no longer needed you deregister it. Three things to know:

  • Running instances are not affected. They already have their own volumes.
  • New launches from it fail, including Auto Scaling launches from a launch template that still names it. Update templates first.
  • Its snapshots are not deleted automatically. They stay, and stay billed, until you delete them (newer versions of the console and API offer to delete them together with the AMI). The Recycle Bin can protect deregistered AMIs against accidents if you set a retention rule.

#Bake or bootstrap? Golden AMIs

Baking and bootstrapping are two ends of one trade-off:

Bootstrap with user dataBake a golden AMI
Boot timeSlow: every instance installs everythingFast: the disk is already finished
ConsistencyEach instance may get slightly different package versionsEvery instance is byte-for-byte the same
Dependencies at launchNeeds the package mirrors and network at every launchNothing to download, so a mirror outage does not block scaling
Changing somethingEdit the script; the next launch picks it upBuild, test and roll out a new AMI
Good forSmall, fast steps and anything that differs per environmentThe slow, heavy, rarely changing parts

The usual answer is both:

  • Bake the operating system, patches, runtime, agents and the application into a golden AMI.
  • Bootstrap only what differs per launch: the environment name, a config file, registering with a service, fetching a secret.

This is the standard exam answer to “instances take too long to become ready” or “scale-out is too slow”: create a golden AMI with the software pre-installed and keep user data small. (If the slow part is warming caches in memory, hibernation helps instead; see lesson 4.5.)

Self-checkQuestion

A team uses one AMI in us-east-1. They now need to launch the same server in ap-southeast-2. What should they do?

#EC2 Image Builder at a glance

Building golden AMIs by hand is itself a chore that people forget to repeat. EC2 Image Builder automates it as a pipeline:

  1. A recipe names a base image and a list of components: build steps (install, configure, harden) and test steps.
  2. Image Builder launches a temporary build instance, runs the build components, creates the AMI, then launches a test instance and runs the tests.
  3. Distribution settings copy the new AMI to other Regions, share it with other accounts, and can even update a launch template to point at it.
  4. The pipeline runs on a schedule (for example weekly) or whenever the base image gets an update, so your golden AMI keeps up with security patches.

Image Builder itself is free; you pay for the instances, snapshots and storage it uses. It can also build container images. On the exam: “automate the creation, patching and testing of AMIs” → EC2 Image Builder.

#The instance metadata service (IMDS)

Software on an instance often needs to know about the instance itself: its ID, its private IP, which Availability Zone it is in, and most importantly the credentials of its IAM role. The instance metadata service answers these questions at a special address that is reachable only from inside the instance:

  • http://169.254.169.254 (IPv4, a link-local address; packets to it never leave the host);
  • http://[fd00:ec2::254] (IPv6, on Nitro instances, if you enable it).

It costs nothing and needs no internet access.

#What it exposes

Path (under /latest/)What you get
meta-data/instance-id, meta-data/ami-id, meta-data/instance-typeWho this instance is and what it was launched from
meta-data/local-ipv4, meta-data/public-ipv4, meta-data/macIts addresses
meta-data/placement/availability-zoneWhere it runs
meta-data/iam/security-credentials/<role-name>Temporary credentials of the instance’s IAM role: an access key, a secret key and a session token
user-dataThe user data script
dynamic/instance-identity/documentA signed document proving which instance, account and Region this is

The role credentials are how an application on EC2 gets AWS permissions without any keys stored on disk. You attach an IAM role to the instance (through an instance profile, Chapter 2), and the AWS CLI and AWS libraries fetch the keys from IMDS automatically. The keys expire after a few hours and AWS rotates them before then.

That also makes IMDS a target: whoever can make a request to 169.254.169.254 from the instance can walk away with the role’s keys.

#IMDSv1 and IMDSv2

There are two ways to talk to IMDS:

IMDSv1IMDSv2
HowA plain GET to the pathFirst a PUT /latest/api/token with a TTL header to get a session token; then every GET carries the token in a header
Token lifetimeNo token1 second to 6 hours (21,600 seconds), chosen by the caller
Protects against SSRFNoYes
Hop limit on the token replyNot applicableYes, default 1
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/placement/availability-zone

Each instance has a setting for http-tokens:

  • optional: both versions work (IMDSv1 is still allowed);
  • required: only IMDSv2 works; a request without a valid token gets 401 Unauthorized.

AWS now leans strongly towards required: the console’s launch wizard and many newer AMIs (including Amazon Linux 2023) default to IMDSv2 only, instance types released since mid-2024 default to it, and you can set an account-wide default per Region.

#Why IMDSv2 stops SSRF credential theft

Server-side request forgery (SSRF) is a bug in which an application fetches a URL that an attacker supplies: a “preview this link” feature, an image fetcher, a webhook tester. The attacker gives it http://169.254.169.254/latest/meta-data/iam/security-credentials/…. The request comes from inside the instance, so IMDS answers.

  • With IMDSv1, that one GET returns the role’s keys, the application shows or logs the response, and the attacker uses the keys from anywhere in the world until they expire. A 2019 breach at a large bank exposed data of over 100 million customers in exactly this way.
  • With IMDSv2 required, the GET has no token and gets 401. A typical SSRF bug lets the attacker choose only the URL of a GET; it cannot make the server first send a PUT with a special header and then reuse the token. IMDSv2 also refuses token requests that carry an X-Forwarded-For header, which defeats most misconfigured open reverse proxies.

#The hop limit

The token reply is sent with an IP time-to-live equal to the hop limit (1 to 64; default 1). Each network hop lowers it by one, so with hop limit 1 the reply reaches software on the instance itself but dies before reaching anything one hop further: a container on a bridge network, or another machine abusing an open proxy or a misrouted path.

  • Hop limit 1: the safe default for instances that do not run containers.
  • Hop limit 2: needed when containers on the instance must use IMDSv2 (for example some container hosts and Kubernetes nodes). The better long-term fix is to give each container its own role instead of the instance’s.

Role credentials from the metadata service, and how attackers try to steal them

The web app has an IAM role that can read an S3 bucket. Its temporary keys are served by IMDS at 169.254.169.254, reachable only from inside the instance.

Scenario

Attacker203.0.113.66
Web approle: app-role
Containeron the same instance
IMDS169.254.169.254
S3 bucketcustomer data
PUT /latest/api/token
Step 1 of 5
1

The AWS library inside the app first asks for a session token with an HTTP PUT, saying how long it should live (here 6 hours, the maximum).

#Enforcing IMDSv2

  • On a running instance: aws ec2 modify-instance-metadata-options --http-tokens required (no restart needed).
  • For new instances: set metadata version: V2 only in the launch template, set the account-wide default, or use AMIs registered as IMDSv2-only.
  • Across an organisation: an IAM or service control policy can deny launching instances unless IMDSv2 is required, and deny changing it back.
  • Before switching: the CloudWatch metric for metadata requests made without a token shows which instances still use IMDSv1. Old software may need an update first; current AWS CLI and libraries already use IMDSv2.
  • If an instance needs no metadata at all, you can turn the endpoint off entirely.

Self-checkQuestion

A web application on EC2 has an SSRF bug. Security wants to stop attackers from using it to steal the instance role’s credentials, without changing the application yet. What should they do first?

#Launch templates

A launch template stores the parameters for launching an instance, so nobody has to fill in the launch wizard by hand again. It can hold:

  • the AMI ID, instance type and key pair;
  • network settings: subnet, security groups, whether to assign a public IP;
  • storage: volume sizes, types, encryption;
  • IAM instance profile, user data, metadata options (IMDSv2 required, hop limit), tags;
  • purchasing options (Spot), placement group, capacity reservation, and more.

Every field is optional. A template can be partial, and whatever it leaves out is supplied at launch time.

Launch templates are used by:

  • a single run-instances call or the console (“launch instance from template”);
  • EC2 Auto Scaling groups (Chapter 6), including groups that mix instance types, Spot and On-Demand;
  • EC2 Fleet and Spot Fleet (lesson 4.4);
  • services that launch instances for you, such as some container and batch services.

#Versions and the default version

A launch template has numbered versions, and a version can never be edited. To change something, you create a new version, usually based on an existing one, changing only the fields you need.

  • One version is the default (at first, version 1). You choose which one, and you can change it at any time.
  • When something launches from the template it can ask for a specific version number, for $Default, or for $Latest (the newest version).
  • Creating a version does not change running instances, and does not change what an Auto Scaling group launches unless the group uses $Latest.
  • Auto Scaling launches new instances with the new version, but existing instances keep their old configuration until they are replaced, for example by an instance refresh.

A safe production pattern: the group uses $Default (or a fixed number); you create a new version, test it, and then promote it by making it the default. Rolling back means setting the default back.

Launch template versions and an Auto Scaling group

The group launches every instance from web-lt. Watch which version it uses as the template changes.

Scenario

us-east-1
Auto Scaling group
web-ltlaunch template
EC2 Auto Scaling
web-av1
web-bv1
$Default = 1v1v1

Launch template web-lt: versions

VersionAMIInstance typeIMDS tokensMarker
1ami-0aaat3.microoptional$Default · $Latest
Step 1 of 4
1

Version 1 holds the AMI, instance type, security group and user data. The group is set to use the template’s $Default version, which is 1.

#Launch templates vs launch configurations

Launch configurations were the older way to tell an Auto Scaling group how to launch instances. They are legacy:

Launch templateLaunch configuration
EditingNew versions, a default version, roll backCannot be changed; create a new one and switch the group
Used byAuto Scaling, EC2 Fleet, Spot Fleet, direct launchesAuto Scaling only
Mixed instance types, Spot + On-Demand in one groupYesNo
New instance types and featuresYesNo new instance types since 2023
Can be createdAlwaysNot by accounts created since October 2024

On the exam, the answer is a launch template whenever one of the two is offered.

#How it fits together in a design

A typical production setup for a web tier:

  1. EC2 Image Builder runs weekly: it starts from the latest Amazon Linux 2023, installs patches, the runtime and the application, runs tests, and produces a golden AMI, copied to every Region the application runs in.
  2. A new launch template version points at the new AMI, requires IMDSv2 with hop limit 1, attaches the IAM instance profile, and carries a small user data script that writes the environment name and fetches secrets from Secrets Manager or Parameter Store at boot.
  3. After testing, that version becomes the default. The Auto Scaling group (on $Default) launches all new instances with it, and an instance refresh replaces the old ones gradually.
  4. If anything goes wrong, set the default back to the previous version and refresh again.

The result: fast, identical, patched servers, no keys on any disk, role credentials protected from SSRF, and a one-step rollback.

#Try it yourself

# 1. A user data script: stop on errors, use IMDSv2, write a page that names the AZ
cat > web.sh <<'EOF'
#!/bin/bash
set -euo pipefail
dnf install -y httpd
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 300")
AZ=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/placement/availability-zone)
echo "<h1>Hello from $(hostname -f) in $AZ</h1>" > /var/www/html/index.html
systemctl enable --now httpd
EOF

# 2. Launch it from the latest Amazon Linux 2023 AMI, with IMDSv2 required
#    (the CLI base64-encodes user data for run-instances)
aws ec2 run-instances --instance-type t3.micro \
  --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --security-group-ids $SG --subnet-id $SUBNET --user-data file://web.sh \
  --metadata-options HttpTokens=required,HttpPutResponseHopLimit=1,HttpEndpoint=enabled

# 3. On the instance: see how the script went, and try both IMDS versions
sudo tail -n 20 /var/log/cloud-init-output.log
curl -s http://169.254.169.254/latest/meta-data/instance-id         # IMDSv1: 401 when v2 is required
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id

# 4. Require IMDSv2 on an existing instance
aws ec2 modify-instance-metadata-options --instance-id $ID --http-tokens required --http-endpoint enabled

# 5. Bake an AMI from the configured instance, then copy it to another Region (run in the destination)
AMI=$(aws ec2 create-image --instance-id $ID --name web-2026-10-24 --query ImageId --output text)
aws ec2 copy-image --region eu-west-1 --source-region us-east-1 --source-image-id $AMI \
  --name web-2026-10-24 --encrypted

# 6. A launch template that uses the AMI, then a new version and a new default
aws ec2 create-launch-template --launch-template-name web-lt --launch-template-data \
  "{\"ImageId\":\"$AMI\",\"InstanceType\":\"t3.micro\",\"MetadataOptions\":{\"HttpTokens\":\"required\"}}"
aws ec2 create-launch-template-version --launch-template-name web-lt --source-version 1 \
  --launch-template-data '{"InstanceType":"t3.small"}'
aws ec2 modify-launch-template --launch-template-name web-lt --default-version 2

# 7. Clean up: deregistering does not delete the snapshots unless you ask
aws ec2 deregister-image --image-id $AMI --delete-associated-snapshots

In a launch template, user data must be base64-encoded by you (base64 -w0 web.sh); run-instances encodes it for you.

#Summary

Key takeaways

  • User data is a script EC2 hands to the instance; cloud-init runs it once, at the first boot, as root. Limit 16 KB; log in /var/log/cloud-init-output.log; it does not re-run after reboot or stop/start; never put secrets in it.
  • An AMI = EBS snapshots + block device mapping + launch permissions. Sources: AWS, Marketplace, community, your own. No charge for the AMI; you pay for its snapshots.
  • AMIs are Region-scoped: copy them to another Region (new ID, launch permissions not copied, can encrypt on the way). Share them through launch permissions; encrypted ones need a customer managed key shared too.
  • Deregistering leaves running instances alone, breaks new launches that name it, and keeps its snapshots (and their bill) unless you delete them.
  • Bake the heavy, common parts into a golden AMI for fast, identical launches; bootstrap only per-launch details with user data. EC2 Image Builder automates building, testing and distributing AMIs.
  • IMDS at 169.254.169.254 gives an instance its identity and its IAM role credentials. IMDSv2 needs a PUT-issued session token (up to 6 hours), which blocks SSRF credential theft; the hop limit (default 1) keeps the token from reaching containers or other hosts. Require it with launch templates, account defaults or policies.
  • Launch templates hold the full launch recipe, have immutable numbered versions and a default version, and feed Auto Scaling, EC2 Fleet and Spot Fleet. Launch configurations are legacy.