HANDS-ON ASSESSMENT GUIDE
AWS Cloud
Practical Prep
Everything you need for the assessment — dependency chains, exact command sequences, checklists and common traps. Understand the why, not just the clicks.
14Services covered
3Exact scenarios
60+Checklist items
38Common traps
Before touching the console, map the architecture. Ask these six questions for every scenario:
1 — Where?
Region → AZ → VPC → Subnet
2 — Who?
IAM · Roles · Policies
3 — Traffic?
Routes · IGW · NAT · ALB · DNS
4 — Data?
S3 · EBS · EFS · RDS
5 — Scale?
ASG · Multi-AZ · Read Replicas
6 — Verify?
Browser · curl · Logs · Health checks
INTERNET
│
Route 53 / DNS
│
Load Balancer (ALB)
├──────────┐
EC2 EC2 ← Public Subnet
└────┬─────┘
│
RDS ← Private Subnet
+──────────────────+
│ S3 │
│ Files / Assets │
+──────────────────+
VPC 10.0.0.0/16
├── Public Subnets → IGW
├── Private Subnets → NAT → IGW
├── Route Tables
├── Security Groups (resource-level)
└── Network ACLs (subnet-level)
IAM → permissions | CloudWatch → monitoring | CloudTrail → API audit
Service Quick Reference
EC2Virtual server
S3Object storage
EBSBlock disk for EC2
EFSShared file system
RDSManaged relational DB
VPCPrivate network
IAMIdentity & permissions
ALBHTTP/HTTPS load balancer
ASGEC2 auto scaling
Route 53DNS service
LambdaServerless functions
CloudWatchMonitoring & alarms
CloudTrailAPI audit history
IGW / NATInternet gateways
IAM Entities
- User — individual identity
- Group — collection of users
- Role — assumable identity (temp creds)
- Policy — permission document
Policy Elements
- Effect — Allow / Deny
- Action — what operation?
- Resource — on what ARN?
- Condition — under what condition?
- Principal — who?
Least Privilege: Give only what is required. Never use AdministratorAccess just because it is convenient.
Verify EC2 Role
aws sts get-caller-identity
Do not put long-lived access keys in application code when an IAM Role can be attached to the EC2 instance instead.
📐 CIDR Layout
VPC: 10.0.0.0/16
├── Public-A 10.0.1.0/24
├── Public-B 10.0.2.0/24
├── Private-A 10.0.11.0/24
└── Private-B 10.0.12.0/24
Subnet CIDR must fit inside the VPC CIDR.
Traffic Paths
Public Subnet
EC2 / Resource
↓
Internet Gateway
↓
Internet
Route Table: 0.0.0.0/0 → IGW
Private Subnet (outbound)
Private Resource
↓
NAT Gateway
↓
Internet Gateway
↓
Internet
Key trap: Public subnet ≠ automatically public EC2. The instance also needs a public IP, SG open to port 80, and the route table must point to IGW.
Route Table Rules
Public Route Table
| Destination | Target |
| 10.0.0.0/16 | local |
| 0.0.0.0/0 | igw-xxxx |
Private Route Table
| Destination | Target |
| 10.0.0.0/16 | local |
⚠ No IGW route here!
Security Group vs NACL
| Feature | Security Group | Network ACL |
| Level |
Resource / Instance |
Subnet |
| State |
Stateful |
Stateless |
| Rules |
Allow only |
Allow + Deny |
| Return traffic |
Automatically tracked |
Must explicitly allow |
Layered Security Architecture
Internet
→
ALB-SG
→
EC2-SG
port 80 from ALB-SG
→
RDS-SG
3306 from EC2-SG
Prefer security-group-to-security-group rules between tiers. Never expose database ports to 0.0.0.0/0.
Secure VPC Build Order
1 Create VPC (CIDR 10.0.0.0/16)
2 Create Private Subnet (10.0.1.0/24)
3 Create Public Subnet (10.0.2.0/24)
4 Create private-rt + public-rt
5 Associate private-rt → private subnet
6 Associate public-rt → public subnet
7 Create Internet Gateway
8 Attach IGW to VPC
9 Add 0.0.0.0/0 → IGW to public-rt
✗ Do NOT add IGW route to private-rt
Launch Checklist
AMIUbuntu 22.04 / Amazon Linux
Instance Typet3.micro (lab default)
Key PairDownload .pem before launch
VPC + SubnetPick correct subnet
Public IPEnable auto-assign
Security Group80 + 22
StorageRoot volume size
User Data#!/bin/bash script
SSH Access
# Set permissions (required)
chmod 400 key.pem
# Ubuntu
ssh -i key.pem ubuntu@PUBLIC_IP
# Amazon Linux
ssh -i key.pem ec2-user@PUBLIC_IP
User Data — Nginx + HTML
#!/bin/bash
apt-get update -y
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
cat > /var/www/html/index.html <<'EOF'
<!DOCTYPE html>
<html>
<head><title>Portfolio</title></head>
<body>
<h1>Welcome to My Portfolio</h1>
<p>AWS Cloud Practical Assessment</p>
</body>
</html>
EOF
Nginx Commands
sudo systemctl status nginx
sudo systemctl start nginx
sudo systemctl restart nginx
sudo systemctl enable nginx
# Test locally
curl localhost
# Check port 80
sudo ss -tulpn | grep :80
Default web root: /var/www/html
EBS — Attach & Mount
Rule: EBS must be in the SAME Availability Zone as the EC2 instance.
# 1. See all block devices
lsblk
# 2. Format new disk (WARNING: destroys existing data)
sudo mkfs.ext4 /dev/nvme1n1
# 3. Create mount point
sudo mkdir /data
# 4. Mount
sudo mount /dev/nvme1n1 /data
# 5. Verify
df -h
# 6. Make persistent — get UUID
sudo blkid /dev/nvme1n1
# 7. Add to /etc/fstab
# UUID=YOUR-UUID /data ext4 defaults,nofail 0 2
# 8. Test fstab
sudo mount -a
Run lsblk first to confirm the device name before running mkfs. Formatting the wrong device destroys data.
EC2 Troubleshooting Chain
01 Instance running?
02 2/2 status checks passed?
03 Has a public IP?
04 In a public subnet?
05 Route table has 0.0.0.0/0 → IGW?
06 Security Group allows port 80?
07 Nginx running?
08 App listening on port 80?
09 OS firewall blocking?
If curl localhost works but browser fails → the problem is AWS networking/security, not the application.
Static Website Flow
Create Bucket
→
Enable Versioning
→
Create materials/
→
Upload index.html
→
Modify + Re-upload
→
Show Versions
→
Unblock Public Access
→
Bucket Policy
→
Enable Website Hosting
→
Open Endpoint ✓
Public Read Bucket Policy
New S3 buckets have Block Public Access ON by default. Disable it first, then apply this policy.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BUCKET-NAME/*"
}
]
}
S3 CLI
aws s3 ls
aws s3 ls s3://BUCKET-NAME/
aws s3 cp index.html s3://BUCKET-NAME/
aws s3 cp index.html s3://BUCKET-NAME/materials/
aws s3 cp s3://BUCKET-NAME/index.html .
Storage Classes
| Access Pattern | Class |
| Standard | Frequently accessed |
| S3-IA | Less frequent, rapid access |
| Glacier | Rare / archive (hours to retrieve) |
Index path trap: index.html and materials/index.html are different objects. If the assessment needs both, keep them at both paths.
Common Ports
- MySQL / MariaDB → 3306
- PostgreSQL → 5432
- MS SQL → 1433
Multi-AZ vs Read Replica
- Multi-AZ → Availability / failover
- Read Replica → Read scaling
"Primary fails?" → Multi-AZ
"Too many reads?" → Read Replica
RDS Security Group: allow the DB port from the EC2 Security Group, not from 0.0.0.0/0.
EC2 → RDS Architecture
Internet
→
ALB
→
EC2 (Public/Private)
→
RDS (Private)
ALB Three Words
Listener
HTTP :80
→
Target Group
EC2 instances
→
Health Check
GET /
ALB Troubleshooting
- EC2 instance running?
- Application running on correct port?
- Health check path correct?
- EC2 Security Group allows traffic from ALB Security Group?
- Correct protocol (HTTP vs HTTPS)?
Auto Scaling Group
Capacity Settings
| Minimum | Always running (e.g. 1) |
| Desired | Normal capacity (e.g. 2) |
| Maximum | Peak limit (e.g. 4) |
Launch Template
- AMI
- Instance type
- Security Group
- IAM Role
- Storage
- User Data
Route 53
| Record | Purpose |
| A | Maps to IPv4 address |
| AAAA | Maps to IPv6 address |
| CNAME | Maps to another hostname |
| Alias A | Points to ALB (preferred) |
| MX | Mail servers |
| TXT | Text / verification records |
Lambda Triggers
S3 upload → Lambda → Process object
API Gateway → Lambda → Response
CloudWatch → Lambda → Scheduled task
CloudWatch vs CloudTrail
CloudWatch
What is happening? Metrics, Logs, Alarms
CloudTrail
Who did what? API activity, Audit history
Files & Navigation
pwd ls ls -la cd
mkdir touch cp mv rm
cat less head tail
Disk & Network
lsblk df -h du -sh *
ip addr ip route
ss -tulpn curl ping
Useful Patterns
# Search for errors in log
grep "error" app.log
# Find HTML files
find /var/www -name "*.html"
# Fix web root ownership
sudo chown -R www-data:www-data /var/www/html
# Check what's on port 80
sudo ss -tulpn | grep :80
# Running processes
ps aux | grep nginx
The core skill: translate assessment words into AWS services.
| Static HTML files | S3 |
| Previous file versions | S3 Versioning |
| Public S3 website | S3 Website + Bucket Policy |
| Linux server | EC2 |
| Automatic server setup | User Data |
| Persistent EC2 disk | EBS |
| Private network | VPC |
| Public subnet Internet | IGW + Route |
| Private outbound Internet | NAT Gateway |
| HTTP/HTTPS distribution | ALB |
| Automatic EC2 scaling | Auto Scaling Group |
| Managed relational DB | RDS |
| Domain name / DNS | Route 53 |
| Serverless code | Lambda |
| Monitor CPU / logs | CloudWatch |
| API audit history | CloudTrail |
| Identity & permissions | IAM |
| Developers isolated, testers need Internet | VPC + Private & Public Subnets |
Resource Creation Orders
VPC + EC2
VPC → Subnet → Route Table → IGW → Route → Security Group → EC2 → Public IP → Application
EC2 + EBS
EC2 → EBS (same AZ!) → Attach → lsblk → Format → Mount → Use
ALB + EC2
VPC → Subnets → SGs → Launch Template → Target Group → ALB → Listener → Health Check
EC2 + RDS
VPC → Private subnets → EC2 → RDS subnet group → RDS → DB SG allows EC2 SG
Tick each item before submitting. These are the most common points assessors check.
🪣 S3 Static Website
0 / 11
🖥 EC2 + Nginx + EBS
0 / 16
🪣 S3 Traps
- Forgetting to enable Versioning before first upload
- Uploading index2.html instead of the same index.html again
- Forgetting to click Show Versions
- Wrong bucket ARN in policy (missing /*)
- Block Public Access still ON when making site public
- Confusing materials/index.html with root index.html
🖥 EC2 Traps
- Wrong Ubuntu version (use 22.04, not 20.04)
- Wrong instance type
- No public IP assigned at launch
- Port 80 missing in Security Group
- SSH port 22 opened to 0.0.0.0/0 unnecessarily
- User Data syntax error (script runs silently)
- EBS created in a different AZ than EC2
- EBS attached but NOT mounted
- Running mkfs on the wrong device
- Nginx installed but not started / not enabled
🌐 VPC Traps
- Creating Route Table but forgetting subnet association
- Creating IGW but not attaching it to the VPC
- Attaching IGW but forgetting to add 0.0.0.0/0 route
- Adding IGW route to the private Route Table
- Assuming a public subnet automatically gives EC2 Internet access
Rule: Change one thing → test → continue. Never randomly change five settings.
Resource
→
Region / AZ
→
VPC / Subnet
→
Route Table
→
IGW / NAT
→
SG / NACL
→
IAM / Policy
→
Service
→
Port
→
Application
→
Test ✓
Use least privilege — give only what is required.
SSH port 22 should be restricted to your IP in a lab (YOUR-IP/32).
Databases stay in private subnets; never expose DB ports to the Internet.
Prefer SG-to-SG rules between application tiers.
Do not make S3 buckets public unless the requirement explicitly needs it.
Never use root credentials for normal AWS work.
Never put long-lived access keys in application code — use an IAM Role.
Never open all ports just because troubleshooting is hard.
S3
Bucket → Versioning → Upload → Modify → Re-upload → Show versions → Unblock public → Bucket policy → Website hosting → index.html → Endpoint
EC2
AMI → Type → Key → VPC → Subnet → SG → User Data → Launch → EBS (same AZ) → Attach → Mount → Nginx → index.html → curl → Browser
VPC
VPC → Subnets → Route Tables → Associations → IGW → Public route (0.0.0.0/0) → Private stays without IGW route
PRIVATE
Private → NAT Gateway → IGW → Internet (outbound only)
SG
80 HTTP · 22 SSH (your IP) · DB port only from application SG
DEBUG
Resource → Network → Route → SG/NACL → Service → Application
Golden Rule: Translate every requirement → AWS service → resource dependency → configuration → security → verification. This pattern solves variations you haven't seen before.
Stopping an EC2 does not stop charges from every related resource. Delete everything.
EC2Terminate instance
ASGDelete if created
ALBDelete load balancer
Target GroupRemove if unused
NAT GatewayDelete (expensive!)
Elastic IPRelease if unused
RDSDelete instance
EBSDelete unattached volumes
SnapshotsRemove unnecessary
S3Empty + delete bucket
CloudWatchRemove alarms