DEV Community

Tejas Shinkar
Tejas Shinkar

Posted on

AWS EC2 Hands-On Practicals

☁️ AWS EC2 Hands-On Practicals

EC2, IAM Roles, EBS, AMIs, Snapshots, CLI and Boto3

Part of my AWS hands-on practical journey. This post documents the EC2 labs and practical tasks I completed, including what I built, how I verified it, and the problems I encountered along the way.


📋 Practicals Covered

# Practical
1 EC2 Web Server with User Data
2 EC2 + S3 Access via IAM Role
3 EC2 Instance & EBS Volume Resize
4 Custom AMI Creation & Validation
5 EBS Snapshot Cross-Region Disaster Recovery
6 Elastic IP
7 EC2 Operations using AWS CLI
8 EC2 Cost Estimation
9 EC2 Automation with Boto3

🧪 Lab 1 — EC2 Web Server with User Data

What I Did

  • Launched an Amazon Linux 2023 EC2 instance using t2.micro.
  • Created a security group:
    • HTTP (80) → 0.0.0.0/0
    • SSH (22) → My IP
  • Added a User Data script during launch.
  • The script installed Apache (httpd), started and enabled the service, retrieved the Instance ID and Availability Zone from EC2 metadata, and created a custom index.html.
  • Opened the instance's public IP in a browser.

Verification

The instance automatically configured itself during first boot and served a custom webpage containing its Instance ID and Availability Zone.

Apache was verified with:

sudo systemctl status httpd
Enter fullscreen mode Exit fullscreen mode

The service was active, enabled, and listening on port 80.

What Failed

The first instance did not work because of setup issues, so it was recreated cleanly. SSH key-access problems were also encountered on Windows.

Troubleshooting Notes

  • User Data was added before launching because it normally runs during first boot.
  • The .pem file needed to be locally accessible and readable by SSH.
  • SSH keys stored in cloud-synced folders such as OneDrive can cause file-access or permission issues.
  • The correct Security Group needed to be attached.

The troubleshooting order that worked was:

Security Group → EC2 status → SSH → Apache → User Data


🧪 Lab 2 — EC2 + S3 Access via IAM Role

What I Did

The goal was to allow EC2 to access S3 without storing AWS access keys on the instance.

Created:

  • S3 bucket: lab-bucket-tejasshinkar
  • Test object: test.txt
  • IAM Role: EC2-S3-ReadOnly-Role
  • Trusted entity: AWS Service → EC2
  • Permission: AmazonS3ReadOnlyAccess

The role was attached to the EC2 instance through its IAM Instance Profile.

Verification

Listed the S3 bucket:

aws s3 ls
Enter fullscreen mode Exit fullscreen mode

Downloaded the test object:

aws s3 cp s3://lab-bucket-tejasshinkar/test.txt .
cat test.txt
Enter fullscreen mode Exit fullscreen mode

Both operations succeeded.

Then write access was tested:

aws s3 cp test.txt s3://lab-bucket-tejasshinkar/write-test.txt
Enter fullscreen mode Exit fullscreen mode

Result:

AccessDenied
Enter fullscreen mode Exit fullscreen mode

The error confirmed that s3:PutObject was not allowed.

What This Proved

EC2
 ↓
Instance Profile
 ↓
IAM Role
 ↓
AmazonS3ReadOnlyAccess
 ↓
S3
Enter fullscreen mode Exit fullscreen mode

The instance could list and read S3 objects, but could not write to S3.

No aws configure was used and no hardcoded AWS access keys were stored on the instance.


🧪 Lab 3 — EC2 Instance & EBS Volume Resize

Part A — EC2 Instance Resize

  • Stopped the running t3.micro instance.
  • Changed the instance type to t3.small.
  • Started the instance again.
  • SSH'd into the instance and verified it was operational.

The instance type had to be changed while the instance was stopped.

Part B — EBS Volume Resize

The root EBS volume was increased:

8 GiB → 16 GiB
Enter fullscreen mode Exit fullscreen mode

This was done through:

EC2 → Volumes → Modify Volume

The AWS Console showed 16 GiB, but the operating system initially reported only about 8 GiB.

Checked the disk layout:

lsblk
Enter fullscreen mode Exit fullscreen mode

The result showed:

nvme0n1 16G
└─nvme0n1p1 8G /
Enter fullscreen mode Exit fullscreen mode

The EBS volume had been expanded, but the partition/filesystem had not yet used the additional space.

Because Amazon Linux 2023 was using XFS:

sudo xfs_growfs /
Enter fullscreen mode Exit fullscreen mode

Finally:

df -h
Enter fullscreen mode Exit fullscreen mode

confirmed approximately 16 GB available to the root filesystem.

Important Troubleshooting

Before running disk commands, the actual device name and filesystem type were checked.

EBS Volume
     ↓
   16 GB
     ↓
Partition / Filesystem
     ↓
16 GB usable by OS
Enter fullscreen mode Exit fullscreen mode

The EBS volume expansion and filesystem expansion are separate operations.

For filesystem expansion:

XFS   → xfs_growfs
ext4  → resize2fs
Enter fullscreen mode Exit fullscreen mode

EBS volume expansion can be performed while the instance is running, unlike changing the EC2 instance type.


🧪 Lab 4 — Custom AMI Creation & Validation

What I Did

  • Launched an Amazon Linux 2023 EC2 instance.
  • Installed and configured Nginx and Git.
  • Created a custom webpage:
echo "<h1>Custom AMI - $(date)</h1>" | sudo tee /usr/share/nginx/html/index.html
Enter fullscreen mode Exit fullscreen mode
  • Verified Nginx locally:
curl localhost
Enter fullscreen mode Exit fullscreen mode
  • Created custom-nginx-ami.
  • Waited for the AMI to become Available.
  • Launched a new EC2 instance using the custom AMI.
  • SSH'd into the new instance.
  • Verified Nginx:
sudo systemctl status nginx
curl localhost
Enter fullscreen mode Exit fullscreen mode
  • Opened the new instance's public IP and confirmed the webpage was served.

What I Proved

The new EC2 inherited:

  • Nginx installation
  • Nginx configuration/service state
  • Custom index.html

No User Data script was required on the new instance.

Configured EC2
      ↓
   Create AMI
      ↓
Reusable EC2 Template
      ↓
    New EC2
      ↓
Same configuration
Enter fullscreen mode Exit fullscreen mode

Mistakes & Troubleshooting

1. Browser SSH failed

The Security Group allowed SSH only from my IP, so browser-based EC2 Instance Connect did not work. I used the SSH client from PowerShell instead.

2. Tried SSH as root

Amazon Linux required the default user:

ec2-user
Enter fullscreen mode Exit fullscreen mode

3. Website initially did not load

The problem was using:

https://<public-ip>
Enter fullscreen mode Exit fullscreen mode

instead of:

http://<public-ip>
Enter fullscreen mode Exit fullscreen mode

Nginx was serving HTTP on port 80, not HTTPS on port 443.


🧪 Lab 5 — EBS Snapshot Cross-Region Disaster Recovery

What I Did

The objective was to create an EBS snapshot in Mumbai, copy it to Virginia, restore it as a new EBS volume, and verify the original data.

Source Region:

ap-south-1
Enter fullscreen mode Exit fullscreen mode

Destination Region:

us-east-1
Enter fullscreen mode Exit fullscreen mode

Created test data:

echo "Mumbai DR test - original data" > test-dr.txt
Enter fullscreen mode Exit fullscreen mode

Then:

  1. Created an EBS Snapshot.
  2. Copied the snapshot from Mumbai to Virginia.
  3. Created a new EBS volume from the copied snapshot in us-east-1.
  4. Launched an EC2 instance in the destination Region.
  5. Attached the restored EBS volume as a secondary volume.
  6. Mounted the restored volume.
  7. Verified the original file:
ls /data
cat /data/test-dr.txt
Enter fullscreen mode Exit fullscreen mode

The original data was successfully restored.

Architecture

Mumbai (ap-south-1)
        │
       EC2
        │
   EBS Volume
        │
        ▼
     Snapshot
        │
        │ Cross-Region Copy
        ▼
Virginia (us-east-1)
        │
Copied Snapshot
        │
        ▼
 New EBS Volume
        │
        ▼
       EC2
        │
        ▼
 Original Data
Enter fullscreen mode Exit fullscreen mode

Mistakes & Troubleshooting

  • Initially created a snapshot before adding the test file, so another snapshot had to be created with the required data.
  • Multiple snapshots and volumes made identification confusing, so Snapshot IDs needed to be checked carefully.
  • The destination EC2 already had its own root EBS volume; the restored volume was attached separately as a secondary disk.
  • When attaching an EBS volume, the EC2 instance and volume must be in the same Availability Zone.

🔧 Additional Practical Tasks

Practical Task 1 — Elastic IP

What I Did

  • Launched an EC2 instance and recorded its initial dynamic public IP.
  • Stopped and started the instance.
  • Confirmed that the public IP changed.
  • Allocated an Elastic IP from EC2 → Elastic IPs.
  • Associated the Elastic IP with the instance.
  • Stopped and started the instance again.
  • Confirmed that the Elastic IP remained unchanged.
  • Released the Elastic IP after completing the practical.

Verification

Before Elastic IP
EC2 → Stop → Start → Different Public IP ❌

After Elastic IP
EC2 → Stop → Start → Same Public IP ✅
Enter fullscreen mode Exit fullscreen mode

Practical Task 2 — EC2 Operations Using AWS CLI

The objective was to manage EC2 using only AWS CLI rather than the AWS Console.

AWS CloudShell was used because it provides a pre-configured AWS CLI environment.

Find Running Instances

aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query "Reservations[].Instances[]. [InstanceId,InstanceType,State.Name,PublicIpAddress]" \
  --output table
Enter fullscreen mode Exit fullscreen mode

This returned the running instance:

Instance ID: i-0548517fe09f6eeeb
Type: t3.micro
State: running
Enter fullscreen mode Exit fullscreen mode

Stop the Instance

aws ec2 stop-instances --instance-ids i-0548517fe09f6eeeb
Enter fullscreen mode Exit fullscreen mode

The instance state was then verified using describe-instances.

The practical demonstrated the flow:

CloudShell
   ↓
AWS CLI
   ↓
EC2 API
   ↓
Describe / Stop / Start / Snapshot
Enter fullscreen mode Exit fullscreen mode

Practical Task 3 — EC2 Cost Estimation

Used AWS Pricing Calculator to estimate the monthly cost of:

  • 1 × t3.micro EC2
  • Linux
  • ap-south-1
  • Running 24/7
  • 20 GB gp3 EBS
  • 100 GB data transfer out
  • Comparison with a 1-year Reserved Instance, No Upfront

The configuration was compared as:

EC2 t3.micro
+ 20 GB gp3 EBS
+ 100 GB Data Transfer Out

        ↓

On-Demand Monthly Cost
        VS
1-Year Reserved Instance Monthly Cost
Enter fullscreen mode Exit fullscreen mode

The Reserved Instance estimate was cheaper for the continuous workload.

The EBS and data-transfer costs were considered separately rather than assuming the compute discount applies to the entire AWS bill.


Practical Task 4 — EC2 Automation with Python + Boto3

The task was to list EC2 instances in ap-south-1, display their Instance ID, Instance Type and State, and automatically stop running instances tagged:

Environment: dev
Enter fullscreen mode Exit fullscreen mode

Script

import boto3

ec2 = boto3.client("ec2", region_name="ap-south-1")

response = ec2.describe_instances()

for reservation in response["Reservations"]:
    for instance in reservation["Instances"]:
        instance_id = instance["InstanceId"]
        instance_type = instance["InstanceType"]
        state = instance["State"]["Name"]

        environment = None

        for tag in instance.get("Tags", []):
            if tag["Key"] == "Environment":
                environment = tag["Value"]

        print(
            f"ID: {instance_id} | "
            f"Type: {instance_type} | "
            f"State: {state}"
        )

        if state == "running" and environment == "dev":
            print(f"Stopping dev instance: {instance_id}")
            ec2.stop_instances(InstanceIds=[instance_id])
Enter fullscreen mode Exit fullscreen mode

Automation Logic

Boto3
  ↓
Connect to EC2 (ap-south-1)
  ↓
List instances
  ↓
Print ID + Type + State
  ↓
Check Environment tag
  ↓
Environment = dev?
  ↓
Running?
  ↓
STOP
Enter fullscreen mode Exit fullscreen mode

The important condition was:

if state == "running" and environment == "dev":
Enter fullscreen mode Exit fullscreen mode

So the script does not stop:

  • Production instances
  • Stopped dev instances
  • Testing instances

Only running instances tagged Environment=dev are stopped.


⚡ Practical Summary

EC2 + User Data: Automated first-boot configuration and Apache setup.

EC2 + IAM Role: Accessed S3 without storing AWS credentials on the instance and verified least-privilege read-only access.

EC2 + EBS: Resized compute and storage, including expanding the filesystem after increasing the EBS volume.

Custom AMI: Created a reusable configured EC2 template and launched another instance from it.

EBS Snapshot + Cross-Region Copy: Restored data in another AWS Region as a basic disaster-recovery workflow.

Elastic IP: Verified the difference between dynamic public IPs and a persistent Elastic IP.

AWS CLI: Performed EC2 operations from CloudShell without using the Console.

Boto3: Used Python to inspect EC2 instances and automate stopping only running development instances.


AWS Hands-On Practicals — EC2 | Cloud + DevOps learning journey

Top comments (0)