AWS for DevOps — Compute Exercises
3.6 — Practice Exercises
Test your understanding of EC2 instances, AMIs, security groups, user data, and Auto Scaling.
Exercise 1 — Launch and Connect
Launch a t3.micro instance with Amazon Linux 2023, tag it Name=test-server, and connect to it via SSH. After connecting, check the instance metadata to confirm the instance type.
Show Answer
# Launch the instance
$ aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--key-name devops-key \
--security-group-ids sg-0abc123def456 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=test-server}]'
# Wait for it to be running
$ aws ec2 wait instance-running --instance-ids i-0abc123def456
# Get the public IP
$ aws ec2 describe-instances --instance-ids i-0abc123def456 \
--query 'Reservations[0].Instances[0].PublicIpAddress' --output text
3.110.45.67
# Connect
$ ssh -i devops-key.pem ec2-user@3.110.45.67
# Check metadata from inside the instance (IMDSv2)
$ TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 300")
$ curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/instance-type
t3.micro
Cleanup:
$ aws ec2 terminate-instances --instance-ids i-0abc123def456
Exercise 2 — Security Group Chaining
Create three security groups for a three-tier architecture: alb-sg (allows 443 from internet), app-sg (allows 8080 from alb-sg only), and db-sg (allows 5432 from app-sg only).
Show Answer
# Create the three security groups
$ aws ec2 create-security-group \
--group-name alb-sg --description "ALB" --vpc-id vpc-0abc123
$ aws ec2 create-security-group \
--group-name app-sg --description "App tier" --vpc-id vpc-0abc123
$ aws ec2 create-security-group \
--group-name db-sg --description "Database tier" --vpc-id vpc-0abc123
# ALB: allow HTTPS from internet
$ aws ec2 authorize-security-group-ingress \
--group-id sg-alb \
--ip-permissions \
'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=0.0.0.0/0}]'
# App: allow 8080 from ALB SG only
$ aws ec2 authorize-security-group-ingress \
--group-id sg-app \
--ip-permissions \
'IpProtocol=tcp,FromPort=8080,ToPort=8080,UserIdGroupPairs=[{GroupId=sg-alb}]'
# DB: allow 5432 from App SG only
$ aws ec2 authorize-security-group-ingress \
--group-id sg-db \
--ip-permissions \
'IpProtocol=tcp,FromPort=5432,ToPort=5432,UserIdGroupPairs=[{GroupId=sg-app}]'
Why this is secure: The database cannot be reached from the internet. Traffic must flow: Internet → ALB → App → DB. Each tier only accepts connections from the tier directly in front of it.
Exercise 3 — User Data Script
Write a user data script that installs Docker, pulls the nginx:latest image, and runs it on port 80. The instance should serve a custom page that displays its hostname.
Show Answer
#!/bin/bash
set -euxo pipefail
# Install Docker
yum update -y
yum install -y docker
systemctl enable docker
systemctl start docker
# Create a custom index page
HOSTNAME=$(hostname)
mkdir -p /tmp/html
cat > /tmp/html/index.html <<EOF
<html>
<body>
<h1>Hello from ${HOSTNAME}</h1>
<p>This page is served by Nginx in Docker on EC2.</p>
</body>
</html>
EOF
# Run Nginx with the custom page
docker run -d \
--name web \
--restart unless-stopped \
-p 80:80 \
-v /tmp/html:/usr/share/nginx/html:ro \
nginx:latest
To launch with this script:
$ aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--key-name devops-key \
--security-group-ids sg-web \
--user-data file://setup.sh
To verify: Visit http://<public-ip> — you should see “Hello from ip-10-0-1-50” (or whatever the hostname is).
Exercise 4 — Create a Custom AMI
You have a running instance i-0abc123 with Nginx installed. Create a custom AMI from it, then launch two new instances from that AMI.
Show Answer
# Create the AMI (stop first for consistency)
$ aws ec2 stop-instances --instance-ids i-0abc123
$ aws ec2 wait instance-stopped --instance-ids i-0abc123
$ aws ec2 create-image \
--instance-id i-0abc123 \
--name "nginx-golden-$(date +%Y%m%d)" \
--description "Nginx pre-installed on Amazon Linux 2023"
{
"ImageId": "ami-0golden123"
}
# Wait for the AMI to be available
$ aws ec2 wait image-available --image-ids ami-0golden123
# Launch two instances from the custom AMI
$ aws ec2 run-instances \
--image-id ami-0golden123 \
--instance-type t3.micro \
--count 2 \
--key-name devops-key \
--security-group-ids sg-web \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-from-ami}]'
Result: Both instances launch with Nginx already installed — no user data needed. Boot time is much faster because there’s nothing to install.
Exercise 5 — Auto Scaling Group
Create an Auto Scaling Group with min=2, max=6, desired=2 that scales out when CPU exceeds 70%. The ASG should spread across two availability zones and use ELB health checks.
Show Answer
# Step 1 — Create a launch template
$ aws ec2 create-launch-template \
--launch-template-name exercise-template \
--launch-template-data '{
"ImageId": "ami-0golden123",
"InstanceType": "t3.micro",
"KeyName": "devops-key",
"SecurityGroupIds": ["sg-app"],
"MetadataOptions": {"HttpTokens": "required"}
}'
# Step 2 — Create the ASG
$ aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name exercise-asg \
--launch-template LaunchTemplateName=exercise-template,Version='$Latest' \
--min-size 2 \
--max-size 6 \
--desired-capacity 2 \
--vpc-zone-identifier "subnet-1a,subnet-1b" \
--target-group-arns arn:aws:elasticloadbalancing:ap-south-1:123456789012:targetgroup/app-tg/abc123 \
--health-check-type ELB \
--health-check-grace-period 300
# Step 3 — Add a target tracking scaling policy
$ aws autoscaling put-scaling-policy \
--auto-scaling-group-name exercise-asg \
--policy-name cpu-70-target \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"TargetValue": 70.0,
"ScaleInCooldown": 300,
"ScaleOutCooldown": 60
}'
# Verify
$ aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names exercise-asg \
--query 'AutoScalingGroups[0].{Min:MinSize,Max:MaxSize,Desired:DesiredCapacity,AZs:AvailabilityZones}'
{
"Min": 2,
"Max": 6,
"Desired": 2,
"AZs": ["ap-south-1a", "ap-south-1b"]
}
What happens under load:
- CPU exceeds 70% → ASG launches additional instances (up to 6)
- CPU drops below 70% → ASG terminates excess instances (down to 2)
- If an instance fails the ELB health check → ASG replaces it automatically
Next Chapter → Networking (VPC)