DEV Community

Cover image for AWS IAM Deep Dive — Roles, Policies, Groups, EC2 & Everything in Between
Tejas Shinkar
Tejas Shinkar

Posted on • Edited on

AWS IAM Deep Dive — Roles, Policies, Groups, EC2 & Everything in Between

Part of my AWS learning journey — transitioning from Systems Engineer to Cloud/DevOps. This session goes deep into IAM — the access control system that governs who can do what across every AWS service.


📋 Topics Covered

# Topic Type
1 Why IAM — The AAA Framework Concept + Interview
2 Root User vs IAM User Concept + Interview
3 Multi-Factor Authentication Setup Concept + Lab
4 IAM Users Concept + Lab
5 IAM Groups Concept + Lab
6 IAM Roles & Service Principals Concept + Lab + Interview
7 Trust Policy & Role Switching Concept + Interview
8 IAM Permissions — Syntax & Policies Concept + Cert
9 Policy Types — Identity-Based & Resource-Based Concept + Cert
10 SCP — The Ceiling Above IAM Concept + Interview
11 ARN — Amazon Resource Name Concept + Lab
12 IAM Instance Profile Concept + Lab + Interview
13 IAM Identity Centre Concept + DevOps
14 EC2 Basics — Brief Introduction Concept + Lab
15 Lab 1 — Service Principal Creation, Access Between Services Lab
16 Lab 2 — Create User, Roles, Groups Lab
17 Lab 3 — IAM Instance Profile → EC2 Access S3 Lab

Why IAM Exists — The AAA Framework

When you first create an AWS account, you get a Root user — zero restrictions, can delete everything, rack up unlimited bills. Real teams have multiple people: developers, DevOps engineers, auditors, managers. You don't want everyone using the same all-powerful Root account. You need controlled, tracked, individual access. That's what IAM solves.

IAM answers one fundamental question: Who can do what, on which AWS resource — and is it being recorded?

That question maps to a universal security framework called AAA:

Pillar Question It Answers How AWS Implements It
Authentication Who are you? Username + Password + MFA
Authorization What can you do? IAM Policies and Roles
Accounting What did you do? AWS CloudTrail (full audit logs)

When you log into AWS and try to start an EC2 instance — AWS verifies your identity, checks if you're allowed to start EC2, and logs that you did it. Every single time, all three happen.


Root User vs IAM User

Root User IAM User
Created When you open AWS account By Root or an admin
Access Unrestricted — everything Only what policies allow
Can be restricted? ❌ No ✅ Yes
Use for Initial setup only, billing emergencies All daily tasks
MFA Enable immediately Strongly recommended

The rule is simple:

Root = lock it away with MFA, use only for account-level emergencies.

IAM User = what you actually log in with every day.


Multi-Factor Authentication (MFA)

A password alone isn't enough. If someone gets your password, they're in. MFA adds a second factor — a time-based OTP from your phone — so even a stolen password is useless without the physical device.

Type How It Works When to Use
Virtual MFA Google Authenticator / Authy app — TOTP on your phone ✅ Default choice for learning
Hardware MFA Physical YubiKey device ✅✅ Best for Root account
SMS/Voice OTP via text message ⚠️ Weakest — avoid if possible

Steps to enable MFA:

IAM Console → Security Credentials → Assign MFA Device → choose Authenticator app → scan QR code with Google Authenticator / Authy → enter two consecutive 6-digit codes → Add MFA ✅

Always enable MFA on Root first — then on your IAM user as well.


IAM Users

An IAM User is an identity for a specific person or application. Unlike Root, it starts with zero permissions — it can log in but cannot access any service until policies are attached.

Creating an IAM User:

IAM Console → Users → Create User → set username → enable Console access + set password → (Optional) add tags → attach policy directly or add to a group → Done ✅

One important note: an IAM user with permission to create users can create more IAM users. Permissions propagate — be deliberate about what you give.


IAM Groups — Managing at Scale

Imagine you have 10 developers. Attaching policies to each one individually is painful and error-prone. Groups solve this.

A Group is a collection of IAM users. Attach a policy to the group once — every user in it inherits those permissions automatically.

Group: DevOpsTeam → Policy: PowerUserAccess

User: tejas → gets PowerUserAccess ✅
User: ravi → gets PowerUserAccess ✅
User: priya → gets PowerUserAccess ✅

Add a new team member → add to group → done.
Remove someone → remove from group → done.

Key rules:

  • One user can belong to multiple groups
  • Groups cannot contain other groups (no nesting)
  • Default quota: 300 groups per account

Creating via Group — the cleaner approach:

Create IAM Group → Attach policy to group → Create IAM User → Add user to group → User inherits group's permissions automatically ✅


IAM Roles & Service Principals

An IAM Role is a temporary identity with permissions — no username, no password. It's meant to be assumed — picked up temporarily by a user or an AWS service, used, and then dropped.

Analogy: A Role is like a visitor badge at an office. A visitor picks up the badge at reception (assumes the role), gets access to specific floors (permissions), and returns the badge when done (session ends). No permanent access. No long-term credentials.

IAM User vs IAM Role

IAM User IAM Role
Has password? ✅ Yes ❌ No
Permanent? ✅ Yes ❌ Temporary (assumed)
For? Human beings Services or temporary access
Credentials Long-term Short-term, auto-rotated

Service Principal

A Service Principal is the identity of an AWS service — used in Trust Policies to allow services like EC2 or Lambda to assume a role.

Common service principals:

  • ec2.amazonaws.com — EC2 can assume this role
  • lambda.amazonaws.com — Lambda can assume this role
  • s3.amazonaws.com — S3 can assume this role

When you want EC2 to access S3 automatically (without hardcoding credentials), you create a role with ec2.amazonaws.com as the trusted service principal and attach S3 permissions to it. Then attach that role to EC2 — EC2 can now talk to S3 with zero credentials in your code.


Trust Policy & Role Switching

Every IAM Role has two parts:

  • Permission Policy — what the role is allowed to do
  • Trust Policy — who is allowed to assume this role

Trust Policy example — allowing a specific IAM user to assume a role:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::123456789012:user/tejas"
    },
    "Action": "sts:AssumeRole"
  }]
}
Enter fullscreen mode Exit fullscreen mode

Role switching — what actually happens:

Step 1: Create a Role with AmazonS3FullAccess permission

Step 2: By default, nobody can assume it — IAM user tries to switch → fails ❌

Step 3: Edit the Trust Policy → add your IAM user's ARN as a trusted principal

Step 4: Switch Role from Console top-right → enter Account ID + Role name → now operating AS the role

Step 5: You can now access S3 ✅ — but your original EC2 permissions are SUSPENDED for this session

Step 6: Switch back → back to your original IAM user permissions → S3 access gone

That EC2 access disappearing when you switched to the S3 role wasn't a bug — it's exactly how role switching works. The role's permissions replace your current ones for the duration of the session.


IAM Permissions — Syntax & Policies

A Policy is a JSON document that defines exactly what is allowed or denied. Every AWS request is checked against applicable policies to decide: allow or deny?

Basic policy structure:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:::my-bucket/*"
  }]
}
Enter fullscreen mode Exit fullscreen mode
Field What It Means
Effect Allow or Deny
Action Which API operation (e.g. s3:GetObject, ec2:*)
Resource Which specific resource (by ARN)
Condition Optional — e.g. "only if MFA is active"

The evaluation rule that matters most:

Explicit Deny → always wins, overrides everything

No Policy → denied by default (implicit deny)

Explicit Allow → access granted, unless a Deny exists somewhere

Combined permissions — how AWS evaluates multiple sources:

User has AmazonEC2FullAccess (direct policy) + Group has ReadOnlyAccess

Result: EC2 Full Access + ReadOnly on everything else (more permissive wins — unless there's an explicit Deny)


Policy Types — Identity-Based & Resource-Based

Identity-Based Policy

  • Attached to: IAM user, group, or role
  • Says: "This identity can perform these actions"
  • No Principal field — the identity it's attached to is the principal
  • Example: AmazonEC2FullAccess attached to an IAM user

Resource-Based Policy

  • Attached to: a resource (S3 bucket, SQS queue, etc.)
  • Says: "These identities are allowed to access me"
  • Has a Principal field — explicitly names who can access
  • Example: S3 bucket policy allowing a specific IAM user to read objects
{
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::123456789:user/tejas"
    },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-bucket/*"
  }]
}
Enter fullscreen mode Exit fullscreen mode

SCP — The Ceiling Above IAM

SCP = Service Control Policy — part of AWS Organizations, used when managing multiple AWS accounts together.

IAM Policy SCP
Attached to Users, Groups, Roles AWS Accounts / Organizational Units
Does it grant permissions? ✅ Yes ❌ No — only limits
Can it restrict Root? ❌ No ✅ Yes
Who uses it? Everyone Enterprises with multiple accounts

SCP defines the maximum permissions possible in an account. IAM policies still need to grant actual access — but they can never exceed what SCP allows.

Example: SCP says "no S3 access in this account." Even if an IAM policy gives a user S3FullAccess, they still can't access S3. SCP wins.

💡 SCP is enterprise-scale. Keep it in mind — it comes up in architecture interviews and the SAA-C03 exam.


ARN — Amazon Resource Name

ARN = Amazon Resource Name. Every single resource in AWS has a globally unique ARN.

Format:

arn:aws:SERVICE:REGION:ACCOUNT-ID:RESOURCE
Enter fullscreen mode Exit fullscreen mode

Examples:

arn:aws:iam::123456789012:user/tejas           → IAM User
arn:aws:iam::123456789012:role/S3AccessRole    → IAM Role
arn:aws:s3:::my-bucket                          → S3 Bucket
arn:aws:ec2:ap-south-1:123456789:instance/i-abc → EC2 Instance
Enter fullscreen mode Exit fullscreen mode

ARNs appear in trust policies, resource-based policies, CloudTrail logs, and anywhere a specific resource needs to be uniquely referenced.


IAM Instance Profile

An Instance Profile is a container that wraps an IAM Role and delivers it to an EC2 instance — giving EC2 automatic, temporary, rotating credentials through the AWS Metadata Service.

Why this matters — the right vs wrong approach:

Wrong approach ❌ — hardcoded keys in code:

aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE'
aws_secret_access_key = 'wJalrXUt...'

If this code goes to GitHub, your keys are exposed immediately. Bots scan GitHub 24/7.

Right approach ✅ — IAM Instance Profile:
IAM Role → Instance Profile → EC2 Instance → AWS Metadata Service (169.254.169.254) → Temporary credentials (auto-rotated every hour) → Access S3 ✅ — zero hardcoded keys, zero manual rotation

The Instance Profile is created automatically when you attach a role to EC2 via the Console — you don't create it separately. One profile holds exactly one role.


IAM Identity Centre

IAM Identity Centre (formerly AWS SSO — Single Sign-On) is AWS's centralized identity management service for organizations with multiple AWS accounts.

The problem it solves:

Without IAM Identity Centre: each developer needs a separate IAM user in every AWS account they work with — dev account, staging account, prod account. Managing credentials across 10+ accounts becomes a nightmare.

With IAM Identity Centre: one login, one set of credentials → access to multiple AWS accounts and applications based on assigned permission sets. Log in once via the Identity Centre portal, click the account you need, you're in.

Key concepts:

Concept What It Is
Permission Set A collection of policies defining access level (like an IAM role, but managed centrally)
Identity Source Where users come from — can be AWS's own directory, Active Directory, or an external IdP (Okta, Azure AD)
Assignment Linking a user/group to a specific AWS account with a specific Permission Set
SSO Portal URL The login page your organization's employees use to access all accounts

Who uses it: Any organization with multiple AWS accounts — and that's most serious organizations. A company might have separate accounts for dev, staging, prod, security, and logging. IAM Identity Centre manages access across all of them from one place.

🎯 DevOps relevance: In production environments, you'll rarely create individual IAM users anymore. Identity Centre + corporate directory (Active Directory/Okta) is the standard. Users log in with their company credentials and get federated access to AWS accounts — no AWS-specific passwords to manage.


EC2 Basics — Brief Introduction

This session introduced EC2 as part of the IAM Instance Profile lab. Full EC2 coverage is in Session 4 — here's what you need for the labs:

AMI (Amazon Machine Image) — the OS template used to launch EC2. Amazon Linux 2, Ubuntu 22.04, Windows Server are all AMIs. Region-specific — must copy to use in another Region.

Instance Type — CPU and RAM configuration. t2.micro = 1 vCPU, 1 GB RAM — Free Tier eligible.

Key Pair — used to SSH into EC2 without a password. AWS keeps the public key on EC2. You download the private .pem file. Download once — if lost, SSH access is gone permanently.

Security Group — virtual firewall. Port 22 for SSH, Port 80 for HTTP.

User Data — a script that runs once automatically on first boot:

#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from EC2!</h1>" > /var/www/html/index.html
Enter fullscreen mode Exit fullscreen mode

EC2 Launch Flow (used in labs):

EC2 Console → Launch Instance → Name → AMI (Amazon Linux 2) → Instance Type (t2.micro) → Key Pair (create + download .pem) → Security Group (SSH 22 + HTTP 80) → Storage (8 GB default) → User Data (optional script) → Launch → wait 2 min → Running ✅


🧪 Lab 1 — Service Principal Creation & Access Between Services

Objective: Create an IAM Role using a Service Principal so EC2 can access S3 without hardcoded credentials.

Step 1: IAM Console → Roles → Create Role
Trusted entity type: AWS Service → EC2
(This sets ec2.amazonaws.com as the Service Principal in the Trust Policy)

Step 2: Attach permission policy → AmazonS3FullAccess

Step 3: Name the role: EC2-S3-FullAccess-Role → Create Role

Step 4: Launch an EC2 instance → Advanced Details → IAM Instance Profile → select EC2-S3-FullAccess-Role
(Or on an existing instance: Actions → Security → Modify IAM Role → select the role → Update)

Step 5: SSH into the EC2 instance

Step 6: Run aws s3 ls
Expected output: list of your S3 buckets ✅ — zero credentials configured on the instance

What just happened: EC2 called the Metadata Service at 169.254.169.254, received temporary credentials from the Instance Profile, used them to authenticate with S3. All automatic. All secure.


🧪 Lab 2 — Create User, Roles, Groups

Objective: Create the full IAM structure — group with policy, user in group, role with trust policy, role switching.

Part A — Group + User:

IAM Console → User Groups → Create group
Name: DevOpsTeam
Attach policy: PowerUserAccess
Create group ✅

IAM Console → Users → Create user
Username: dev-user-1
Enable console access → set password
Add user to group: DevOpsTeam
Create user ✅ → download credentials CSV

Log in as dev-user-1 via IAM sign-in URL → verify PowerUserAccess is active

Part B — Role + Trust Policy + Switching:

IAM Console → Roles → Create role
Trusted entity type: AWS Account → This account
Attach policy: AmazonS3FullAccess
Name: S3AccessRole → Create role ✅

Open S3AccessRole → Trust relationships → Edit trust policy
Add your IAM user's ARN as a Principal:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::YOUR_ACCOUNT_ID:user/dev-user-1"
    },
    "Action": "sts:AssumeRole"
  }]
}
Enter fullscreen mode Exit fullscreen mode

Log in as dev-user-1 → top-right → Switch Role → enter Account ID + S3AccessRole
Verify: S3 access works ✅
Verify: EC2 access is suspended (role replaced your permissions) ✅
Switch back → original PowerUserAccess restored ✅


🧪 Lab 3 — IAM Instance Profile → EC2 Access S3

Objective: Create an EC2 instance with an Instance Profile that gives it read access to S3, then verify from inside the instance.

Step 1: Create S3 bucket with a test file
S3 Console → Create bucket → name: iam-lab-test-bucket-2026 → upload test.txt

Step 2: Create IAM Role for EC2
IAM → Roles → Create Role → AWS Service → EC2
Policy: AmazonS3ReadOnlyAccess
Name: EC2-S3-ReadOnly-Role

Step 3: Launch EC2 with Instance Profile
EC2 → Launch Instance → Amazon Linux 2 → t2.micro → Key Pair
Advanced Details → IAM instance profile → EC2-S3-ReadOnly-Role
Security Group: SSH (22) from My IP → Launch

Step 4: SSH into EC2 and verify S3 access

ssh -i your-key.pem ec2-user@<public-ip>

# List S3 buckets — should work with no credentials configured
aws s3 ls

# Download the test file
aws s3 cp s3://iam-lab-test-bucket-2026/test.txt .
cat test.txt

# Try to write — should fail (ReadOnly policy)
aws s3 cp test.txt s3://iam-lab-test-bucket-2026/write-test.txt
# Expected: AccessDenied ✅ (ReadOnly policy working correctly)
Enter fullscreen mode Exit fullscreen mode

⚡ Quick Revision

AAA Framework

  • Authentication → Who are you? (credentials + MFA)
  • Authorization → What can you do? (IAM Policies)
  • Accounting → What did you do? (CloudTrail)

Permission Hierarchy

  • SCP (Organizations) → ceiling, limits max permissions, can restrict Root
  • IAM Policies → actual grants within that ceiling
  • Explicit Deny → always wins over everything

IAM Building Blocks

  • User → permanent identity, has password, for humans
  • Group → collection of users, attach policy once, inherited by all members
  • Role → temporary identity, no password, assumed by users or services
  • Policy → JSON document defining Allow/Deny on specific resources
  • ARN → unique ID for every AWS resource

Role Switching

  • Add user ARN to role's Trust Policy → user can now switch
  • Switching: role permissions replace your current permissions for that session
  • Switch back → original permissions restored

Instance Profile (EC2 → S3)

  • Create Role with Service Principal ec2.amazonaws.com + S3 policy
  • Attach to EC2 via Instance Profile
  • EC2 gets auto-rotating temp credentials from Metadata Service
  • Zero hardcoded credentials in code or on disk

SCP vs IAM

  • IAM: grants permissions within an account
  • SCP: limits maximum permissions across accounts, overrides everything including Root

IAM Identity Centre

  • One login → multiple AWS accounts
  • Uses Permission Sets (managed role bundles) assigned per account per user/group
  • Integrates with corporate directories (Active Directory, Okta, Azure AD)
  • Standard for multi-account enterprise environments

💼 Interview Questions

Q1: What is the difference between an IAM User and an IAM Role?
An IAM User has permanent credentials (username + password) for a human being. An IAM Role has no password — it provides temporary credentials assumed by users or AWS services. Roles are the correct approach for service-to-service communication.

Q2: What is a Trust Policy and why is it needed?
A Trust Policy is a JSON document on an IAM Role that defines who is allowed to assume it. Without a Trust Policy, nobody can use the role — not even admins. You explicitly add trusted principals (IAM user ARNs or service principals like ec2.amazonaws.com).

Q3: What happens when you switch roles in AWS Console?
Your current IAM user permissions are temporarily suspended and fully replaced by the role's permissions for that session. When you switch back, your original permissions are restored. This is why EC2 access disappears when you switch to an S3-only role.

Q4: What is an IAM Instance Profile and why use it instead of access keys?
An Instance Profile is a container that delivers an IAM Role to an EC2 instance. EC2 automatically receives temporary, rotating credentials from the AWS Metadata Service — no hardcoded keys anywhere. This is far safer than embedding access keys in code, which can be accidentally exposed via Git repositories.

Q5: What is the difference between SCP and IAM policies?
IAM policies grant permissions to users, groups, or roles within an account. SCPs (Service Control Policies) sit above IAM at the Organizations level and define the maximum permissions allowed in an account — they restrict, not grant. Even the Root user is subject to SCPs.

Q6: An IAM user has EC2FullAccess directly and ReadOnlyAccess via their group. What's their effective permission?
They get EC2 Full Access combined with ReadOnly on everything else. AWS combines all Allow permissions from all sources. However, if an Explicit Deny exists anywhere — in any policy, including an SCP — it overrides all Allows.

Q7: What is IAM Identity Centre and when would an organization use it?
IAM Identity Centre is AWS's centralized identity management service. It lets users log in once with one set of credentials (optionally from a corporate directory like Active Directory or Okta) and access multiple AWS accounts via assigned Permission Sets — without separate IAM users per account. Organizations use it whenever they have more than one AWS account and want centralized, governed access management.

Q8: Your EC2 instance returns AccessDenied when calling DynamoDB. What do you check first?
Check the IAM Role attached to the EC2 instance via its Instance Profile. Verify the role has the required DynamoDB permissions (dynamodb:GetItem, dynamodb:PutItem, etc.) on the correct table ARN. Missing or incorrect permissions on the Execution Role is the most common cause of AccessDenied errors from EC2 or Lambda calling other AWS services.


🔬 Practice Tasks

  1. Create an IAM Group called DevOpsTeam, attach PowerUserAccess, create a user and add them to the group. Log in as that user and verify you can access EC2 but not billing (billing is Root-only by default).

  2. Create an IAM Role with AmazonS3ReadOnlyAccess. Edit its Trust Policy to add your IAM user's ARN. Switch to that role from the Console. Verify S3 access works — verify EC2 access is suspended while the role is active. Switch back and confirm EC2 access returns.

  3. Explore the AmazonEC2FullAccess policy JSON in IAM Console. Find what ec2:* means. Identify 3 specific actions it covers (e.g., ec2:StartInstances, ec2:DescribeInstances, ec2:TerminateInstances).

  4. Complete Lab 3 above — EC2 Instance Profile with S3 ReadOnly access. Verify you can list and download from S3, then try to upload and confirm you receive AccessDenied. This proves the Least Privilege principle is working.

  5. Create an IAM Role for EC2 with only AmazonS3FullAccess. Try to run aws ec2 describe-instances from inside the EC2 instance — confirm it returns AccessDenied. This demonstrates that the Instance Profile only gives what the role allows — nothing more.


AWS Session 3 — IAM Essentials | Cloud + DevOps learning journey — Systems Engineer → Cloud/DevOps Engineer

Top comments (0)