I'm a Systems Engineer transitioning into Cloud/DevOps. This post is a practical account of my IAM lab session — three hands-on labs covering Service Principals, Role Switching, and Instance Profiles. Not a tutorial. Just what I did, where things broke, and what I actually learned from it.
The Setup
IAM (Identity and Access Management) is the access control layer for everything in AWS. After spending time on the theory — AAA framework, policies, trust relationships — I sat down to actually build it. Three labs, one session.
Lab 1 — EC2 Accessing S3 Without Credentials
What I was trying to prove: An EC2 instance can access S3 without a single access key or secret key stored on it.
What I did:
Created an S3 bucket, launched an EC2 instance, then created an IAM Role (EC2-S3-FullAccess-Role) with ec2.amazonaws.com as the Service Principal and AmazonS3FullAccess attached. Attached the role to the EC2 instance via Modify IAM Role. SSH'd in and ran:
aws s3 ls
Output: tejas-shinkar-s3-bucket — listed immediately. No aws configure. No access key. No secret key.
The thing that clicked:
The EC2 instance called the AWS Metadata Service at 169.254.169.254, got temporary credentials from the Instance Profile, and used them transparently. The application never touches credentials. They rotate automatically. This is the correct way — not the "paste keys into your script" approach I'd seen before.
Lab 2 — Users, Groups, Roles, and Role Switching
This was the most involved lab. Two parts.
Part A — Group + User
Created a group DevOpsTeam with PowerUserAccess, created dev-user-1 and added them to the group. Logged in as dev-user-1 and confirmed inherited permissions.
The structure:
DevOpsTeam → PowerUserAccess → dev-user-1
Clean. Policy attached once to the group — user gets it by membership. This is how it should be done at scale. Direct policy attachment to users is a maintenance problem waiting to happen.
Part B — Role Switching
Created S3AccessRole with AmazonS3FullAccess, then edited the Trust Policy to allow dev-user-1 to assume it:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::484908301955:user/dev-user-1"
},
"Action": "sts:AssumeRole"
}]
}
Logged in as dev-user-1, switched to S3AccessRole via the Console.
What I observed: S3 access worked. EC2 access was suspended. The role's permissions completely replaced my user's permissions for that session.
Key learning: Role switching isn't additive — it's a full replacement. If you expect to retain your existing permissions while adding new ones, you're thinking about it wrong. The role is a temporary identity, not an extension.
Switching back restored dev-user-1's PowerUserAccess immediately.
Lab 3 — Instance Profile with Least Privilege
The goal this time: ReadOnly access, not full access. Explicitly test the boundary.
Created EC2-S3-ReadOnly-Role with AmazonS3ReadOnlyAccess, attached it to a new EC2 instance during launch via the IAM Instance Profile field in Advanced Details.
SSH'd in and ran the tests:
# List buckets — should work
aws s3 ls
# → bucket listed ✅
# Download a file
aws s3 cp s3://tejas-shinkar-s3-bucket/test.txt .
# → downloaded ✅
# Try to upload — should fail
aws s3 cp test.txt s3://tejas-shinkar-s3-bucket/write-test.txt
# → AccessDenied ❌
That AccessDenied on the write attempt was the point. It confirmed the policy boundary was real, not theoretical. The instance can read. It cannot write. The role defines the limit precisely.
Flow that makes this work:
EC2 → Instance Profile → IAM Role → Trust:
ec2.amazonaws.com→ Permission: S3 ReadOnly → Temporary credentials via Metadata Service → S3 access with defined limits
Practice Tasks — What Else I Worked Through
Beyond the three labs, I completed a few additional tasks:
Root MFA: Enabled on the Root account using Google Authenticator — the first thing anyone should do after creating an AWS account. Root has unrestricted access. A stolen password without MFA is total account compromise.
IAM User + Billing Access Test: Created an IAM user with AdministratorAccess. Verified EC2 and S3 access worked. Then checked Billing — got denied. This surprised me initially. IAM access to Billing is disabled by default even for Administrator users. Root must explicitly enable it. Good to know before you're in a situation where someone needs billing visibility and can't understand why they're blocked.
$5 Budget Alert: Set up a billing budget with alert thresholds. Non-negotiable for anyone learning on a Free Tier account. Unexpected charges from forgotten resources are a real trap.
What I'd Carry Forward
Three things from this session that I'll apply in anything I build going forward:
1. Never hardcode credentials. Instance Profile + IAM Role is the correct pattern for EC2 accessing other AWS services. There's no legitimate reason to put access keys on an EC2 instance.
2. Trust Policy before you try to switch. The role won't work until the Trust Policy explicitly names who can assume it. Missing this step causes a confusing failure — the role exists, but the switch fails silently until you realize the Principal wasn't set.
3. Least Privilege is testable. Don't assume the policy is working — test the boundary. The AccessDenied on the write attempt in Lab 3 was proof the ReadOnly constraint was actually enforced. Proving it works is part of building it correctly.
Documenting my AWS learning journey — Systems Engineer → Cloud/DevOps Engineer. Session 3 of an ongoing series.
Top comments (0)