Logging in to set up a server does not scale and leaves no record. User data is a script the instance runs for itself at first boot.
The script
#!/bin/bash
dnf install -y httpd
echo "<h1>Monster Market</h1><p>served from $(hostname)</p>" > /var/www/html/index.html
systemctl enable --now httpdIt runs as root, once, at the first boot. Stopping and starting does not run it again; terminating and launching a new instance does. It must start with #!/bin/bash so cloud-init knows it is a script.
aws ec2 run-instances --image-id $AMI --instance-type t3.micro --security-group-ids sg-… \
--iam-instance-profile Name=ssm-profile --user-data file://web.sh \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=app}]'--user-data file://web.sh sends the file. Combined with a security group and an instance profile, this one command produces a serving, manageable web server with nobody logging in.
When it fails
Nothing on the outside says the script broke; the page simply never appears. The record is on the instance:
sh-5.2$ sudo cat /var/log/cloud-init-output.log
sh-5.2$ systemctl status httpdThe log holds every line the script printed, including the dnf error or the typo in a path. Complete! from dnf and enabled from systemctl are the lines you want to see.
The metadata service
sh-5.2$ TOKEN=$(curl -s -X PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
sh-5.2$ curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id
sh-5.2$ curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4From inside, the address 169.254.169.254 answers questions about the instance itself: id, addresses, zone, the role's temporary credentials. Scripts use it to label themselves. Amazon Linux 2023 requires IMDSv2: fetch a token with a PUT, then send it as a header; a plain GET gets 401. That protects the credentials from a web application that can be tricked into fetching URLs.