Phase 3 · CLOUD
EC2 — launch, connect, security groups
By the end of today
- Launch a free-tier t2.micro EC2 instance from the AWS CLI
- Lock a security group to allow SSH from your IP only
- SSH into your instance, run a command, then terminate it
EC2 and security groups: a server you rent by the second
Amazon EC2 (Elastic Compute Cloud) is the oldest and most-used AWS service: virtual servers you rent by the second. You pick an operating-system image, a size, and a key, and about thirty seconds later you have a Linux box with a public IP you can SSH into from anywhere. When you are done, you throw it away. That disposability is the whole point — and the whole risk, because a box you forget to throw away bills you for every hour it runs.
Four things go into every launch:
- An AMI (Amazon Machine Image) — the disk image the instance boots from: Ubuntu 24.04, Amazon Linux 2023, or your own baked image.
- An instance type — the hardware size.
t2.micro(1 vCPU burstable, 1 GB RAM) is free-tier eligible and plenty for learning. - A key pair — an SSH key. AWS keeps the public half; you download the private
.pemonce, and it is the only way in. - A security group — the instance’s firewall.
The security group is the part that trips people up and the part that matters most today. It is a stateful, allow-only virtual firewall wrapped around the instance’s network interface. “Allow-only” means you write rules for what to permit — there are no deny rules; anything you don’t allow is blocked. “Stateful” means that if you allow an inbound connection, its return traffic is allowed back automatically — you never write the reverse rule. By default a new security group denies all inbound and allows all outbound, so a fresh instance is unreachable until you add an inbound rule.
The rule everyone writes first is SSH: allow TCP port 22 so you can log in. The dangerous shortcut is to allow it from 0.0.0.0/0 — the entire internet. Do that and within minutes bots are brute-forcing your box; it is the single most common way a learner’s instance gets compromised. Allow port 22 from your IP only — 203.0.113.4/32, a single-host CIDR — and the door opens for you alone.
Real world: A security group is the bouncer with a guest list, not a locked door. The default list is empty, so nobody gets in. You add “port 22, from my address” and the bouncer lets exactly you through — and because he is stateful, he remembers you walked in and lets your replies back out without a second check. Writing
0.0.0.0/0is telling the bouncer to wave in anyone who shows up.
Netflix runs a huge share of its streaming control plane on EC2, launching and retiring thousands of instances a day through automation — the same run-instances and terminate-instances calls you will make by hand today, just wrapped in tooling. The habit you build now — launch minimal, lock the security group to a known source, terminate when done — is exactly the discipline that keeps a fleet that size both secure and affordable.
Today you launch one t2.micro, lock its door to your IP, SSH in, run a command, and — the step that keeps the bill at zero — terminate it.
Hands-On Lab
Budget about 30 minutes. You’ll drive this from the AWS CLI v2 as your Day 47 IAM user (not root), in the us-east-1 region — swap in your own region if you prefer, but stay consistent. Type every command and read the output before moving on.
What this costs: ₹0 if you stay on the free tier and terminate at the end. A single
t2.microis free-tier eligible for 750 hours/month during your first 12 months, and security groups, key pairs and the SSM lookup are always free. That 750-hours-for-12-months allowance applies to accounts on the legacy Free Tier; accounts created since mid-2025 get a credit-based Free Tier instead — but either way the ₹0 outcome depends on terminating promptly. The only way this bills you is leaving the instance running — a t2.micro left on is roughly ₹700–900/month. Steps 10–12 terminate it and delete everything, so finish the lab, don’t just walk away.
# 1. Confirm you're the IAM user from Day 47 (NOT root) and set a default region.
aws sts get-caller-identity
export AWS_DEFAULT_REGION=us-east-1
# Output (your Account and Arn will differ):
# {
# "UserId": "AIDA...EXAMPLE",
# "Account": "123456789012",
# "Arn": "arn:aws:iam::123456789012:user/devops-you"
# }
# 2. Create an SSH key pair. AWS keeps the public half; save the private .pem locally.
aws ec2 create-key-pair --key-name m90-ec2 \
--query 'KeyMaterial' --output text > ~/m90-ec2.pem
chmod 400 ~/m90-ec2.pem
ls -l ~/m90-ec2.pem
# Output:
# -r-------- 1 you you 1675 Jul 11 10:01 /home/you/m90-ec2.pem
# 3. Find your public IP so the security group can allow SSH from you ONLY.
MY_IP=$(curl -s https://checkip.amazonaws.com)
echo "$MY_IP"
# Output (yours will differ):
# 203.0.113.4
# 4. Create a security group in your default VPC; capture its ID into a variable.
SG=$(aws ec2 create-security-group \
--group-name m90-ssh --description "SSH from my IP only" \
--query 'GroupId' --output text)
echo "$SG"
# Output (this GroupId is yours — the variable carries it into the next steps):
# sg-0abc123def4567890
# 5. Allow inbound TCP 22 from YOUR IP only — never 0.0.0.0/0.
aws ec2 authorize-security-group-ingress \
--group-id "$SG" --protocol tcp --port 22 --cidr "${MY_IP}/32"
# Output (trimmed) — one rule added, scoped to your /32:
# {
# "Return": true,
# "SecurityGroupRules": [
# { "IpProtocol": "tcp", "FromPort": 22, "ToPort": 22,
# "CidrIpv4": "203.0.113.4/32" }
# ]
# }
# 6. Get the latest Ubuntu 24.04 AMI ID from the public SSM parameter (no hard-coding).
AMI=$(aws ssm get-parameters \
--names /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id \
--query 'Parameters[0].Value' --output text)
echo "$AMI"
# Output (changes as Canonical publishes new builds):
# ami-0abcd1234ef567890
# 7. Launch ONE free-tier t2.micro with your key and security group.
IID=$(aws ec2 run-instances \
--image-id "$AMI" --instance-type t2.micro \
--key-name m90-ec2 --security-group-ids "$SG" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=m90-ec2}]' \
--query 'Instances[0].InstanceId' --output text)
echo "$IID"
# Output (your instance ID will differ):
# i-0123456789abcdef0
# 8. Wait until it's running, then read its public IP.
aws ec2 wait instance-running --instance-ids "$IID"
aws ec2 describe-instances --instance-ids "$IID" \
--query 'Reservations[0].Instances[0].PublicIpAddress' --output text
# Output (yours will differ — this is the public IP to SSH to):
# 54.81.152.7
# 9. SSH in as the 'ubuntu' user with your key, run a command, then log out.
ssh -i ~/m90-ec2.pem ubuntu@54.81.152.7 'uname -a && uptime'
# Output (accept the host key with "yes" on first connect):
# Linux ip-172-31-20-14 6.8.0-1010-aws #10-Ubuntu SMP x86_64 GNU/Linux
# 10:04:12 up 1 min, 0 users, load average: 0.08, 0.02, 0.00
# 10. TERMINATE the instance — this is the step that keeps the bill at ₹0.
aws ec2 terminate-instances --instance-ids "$IID" \
--query 'TerminatingInstances[0].CurrentState.Name' --output text
# Output:
# shutting-down
# 11. Confirm it's gone. Wait for the terminated state, then check.
aws ec2 wait instance-terminated --instance-ids "$IID"
aws ec2 describe-instances --instance-ids "$IID" \
--query 'Reservations[0].Instances[0].State.Name' --output text
# Output — a terminated instance stops billing (and disappears within an hour):
# terminated
# 12. Delete the security group and key pair so nothing is left behind.
aws ec2 delete-security-group --group-id "$SG"
aws ec2 delete-key-pair --key-name m90-ec2
rm -f ~/m90-ec2.pem
echo "cleaned up"
# Output:
# cleaned up
Read the last outputs back: terminated in step 11 is the one that matters — it is the difference between a ₹0 lab and a running instance quietly billing you for the rest of the month.
Common Errors & Fixes
These three catch almost everyone launching their first instance. Read the error text slowly — parsing it is the actual skill.
Common error: Running
run-instancesas an IAM user whose policy doesn’t include EC2 launch permissions:An error occurred (UnauthorizedOperation) when calling the RunInstances operation: You are not authorized to perform this operation. User: arn:aws:iam::123456789012:user/devops-you is not authorized to perform: ec2:RunInstances on resource: ...Why: Day 47 built a least-privilege IAM user — often scoped to billing and IAM, not compute. AWS denies by default, so any action the user’s attached policies don’t explicitly allow is refused, and it names the exact missing action (
ec2:RunInstances).Fix: Attach an EC2 policy to the user. For learning,
AmazonEC2FullAccessis fine; in a real account you’d scope it tighter. Then retry — theUnauthorizedOperationmessage always tells you the preciseservice:Actionto grant.How you’d spot it in prod: A pipeline that fails with
UnauthorizedOperationon one specific call is almost never a broken script — it’s an IAM role missing one permission. Copy theservice:Actionfrom the message straight into the policy rather than reaching for a wildcard.
Common error: SSH hangs for about 30 seconds, then fails, because the security group doesn’t allow port 22 from your current address:
ssh: connect to host 54.81.152.7 port 22: Connection timed outWhy: No inbound rule permits port 22 from your IP. Either the group was left at its all-denied default, or — the common one — your home or office IP changed since you wrote the
/32rule, so yesterday’s rule no longer matches you.Fix: Re-check your current IP with
curl -s https://checkip.amazonaws.com, then re-runauthorize-security-group-ingressfor the new/32. Confirm the live rules withaws ec2 describe-security-groups --group-ids "$SG".How you’d spot it in prod: A timeout (not “connection refused” or “permission denied”) points at a network layer — a security group, NACL, or missing route — not at SSH or the app. “Refused” means something answered and said no; “timed out” means nothing answered at all.
Common error: SSH refuses to use a
.pemfile that other users can read — common after copying the key onto a Windows path under WSL:@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Permissions 0644 for '/home/you/m90-ec2.pem' are too open.Why: SSH refuses to use a private key that is group- or world-readable, on the reasonable assumption that a key anyone can read is a key anyone can steal. A freshly downloaded key, or one stored on a Windows filesystem, often lands with permissive
0644bits.Fix: Restrict it to owner-read-only:
chmod 400 ~/m90-ec2.pem. On WSL, keep the key on the Linux filesystem (your home directory), not under/mnt/c/…, where Windows permissions can’t be tightened the same way.How you’d spot it in prod: “UNPROTECTED PRIVATE KEY FILE” in a CI runner or automation log means a key was checked out with loose permissions — usually pulled from an artifact or secret store without a
chmod 400afterward. Fix the permission step, don’t loosen SSH.
EC2 Interview Questions
Cover the answers below and say your own version out loud first — name what a security group is, and what terminate does that stop doesn’t, before you reveal each answer. Recalling before revealing is what makes these stick when an interviewer asks them cold. The four questions and answers render right after this note.
Go Deeper
Optional extras if you have ~30 more minutes today:
- 5 min — Try connecting with AWS Systems Manager Session Manager instead of SSH: it opens a shell through the AWS API with no inbound port 22 at all. Skim the “Session Manager” page in the SSM docs to see why teams prefer it to open SSH.
- 10 min — Read the EC2 security groups doc on stateful behaviour and default rules, then open your default VPC’s network ACL and compare — stateful per-instance vs stateless per-subnet.
- 10 min — Relaunch a t2.micro with a
--user-datascript (a bash script that runs on first boot) that installs and starts nginx, thencurlthe public IP to see the page. Terminate it the moment you’re done. - 5 min — Skim the EC2 instance-types page and the free-tier limits so you know which sizes are free and which quietly bill — the knowledge that stops a “just testing” instance becoming a surprise charge.
What is a security group and how does it differ from a network ACL? Both
A security group is a stateful, allow-only virtual firewall attached to an instance's network interface. You write rules for traffic to permit — there are no deny rules — and because it's stateful, the return traffic for an allowed connection is let back out automatically. A network ACL is different: it sits at the subnet boundary, is stateless (you must allow both directions), and supports explicit allow and deny rules evaluated in order. The rule I use: security groups are the per-instance guest list I reach for first; NACLs are a coarse, subnet-wide backstop, often used to block a bad IP range across everything at once. Most day-to-day access control lives in security groups.
Why is it dangerous to open port 22 to 0.0.0.0/0, and what should you do instead? Both
0.0.0.0/0 means the entire internet, so opening SSH that wide invites automated brute-force bots that scan public IPs constantly — a brand-new instance can be under attack within minutes. Instead I allow port 22 from my own IP as a /32, so only my address can reach it. Even better in real environments: don't expose 22 at all — use AWS Systems Manager Session Manager, which gives a shell through the AWS API with no inbound port open, or put instances in a private subnet behind a bastion or VPN. The principle is any firewall's: open the narrowest source range that still lets you do your job.
What's the difference between stopping and terminating an EC2 instance? Product
Stopping is like shutting the machine down: the instance halts, you stop paying for compute, but its EBS root volume persists, so the data survives and you can start it again later — though it usually gets a new public IP. Terminating deletes the instance for good; by default the root volume is deleted with it, the instance ID is gone, and you can't bring it back. I stop an instance I'll want tomorrow and terminate a throwaway I'm done with. The gotcha: a stopped instance still bills for its EBS storage and any Elastic IP, so 'stopped' isn't 'free' — only terminating and cleaning up the volumes truly stops the charges.
How do you connect to a new EC2 instance, and what do you check if SSH times out? Product
I SSH in with the key pair chosen at launch: ssh -i key.pem ubuntu@<public-ip> — the user is 'ubuntu' for Ubuntu AMIs, 'ec2-user' for Amazon Linux. If it times out rather than being refused, I treat it as a network problem, not authentication. First I check the security group actually allows port 22 from my current IP — home IPs change, so an old /32 rule may no longer match me. Then I confirm the instance has a public IP and sits in a public subnet with a route to an internet gateway. 'Permission denied (publickey)' is a different problem — wrong username or key. Long-term I'd prefer Session Manager over an open port 22.
Mark Day 48 complete
Tomorrow you play: AWS Bill Shock
Stuck on today’s lab? Ask in Mission 90 Q&A