Back to IAM: Users, Roles, Policies
Q2Question 2 of 5

IAM: User, Group, Role and Policy

Key Concepts
  • IAM (Identity and Access Management) controls who is authenticated and what they are authorized to do in an AWS account.
  • An IAM User is a permanent identity with long-lived credentials such as a password or access keys, meant for a human or legacy application.
  • An IAM Group is a collection of Users that share the same policies, used purely for convenience and never as a login identity.
  • An IAM Role is an identity with no permanent credentials that is assumed temporarily to receive short-lived credentials from STS.
  • An IAM Policy is a JSON document that lists allowed or denied actions on specific resources and is attached to Users, Groups, or Roles.
  • Effective permissions are the union of all attached policies, minus any explicit deny which always wins.
  • Roles are preferred over Users for machines because temporary credentials reduce blast radius if leaked.

Definition

User is a permanent person, Group bundles people for shared rules, Role is a temporary borrowed identity, and Policy is the rulebook that grants the actual permissions.
  • IAM (Identity and Access Management) – The AWS service that controls authentication (who are you) and authorization (what may you do) for every request to every AWS service.
  • IAM User – A permanent identity with long-lived credentials (password and/or access keys) for a human or legacy application.
  • IAM Group – A collection of Users that share the same policies, used only for convenience; you never log in as a Group.
  • IAM Role – An identity with no long-lived credentials, assumed temporarily through STS to obtain short-lived credentials that expire.
  • IAM Policy – A JSON document listing allowed or denied actions on specific resources, attached to Users, Groups, or Roles to grant permissions.

At National Bank of India, an IAM User is a permanent staff member like Rajesh with his own ID card and password. An IAM Group is a department like Tellers – everyone in it automatically gets the teller permissions. An IAM Role is a temporary visitor badge: a contractor or a machine borrows it for a few hours, does the job, and the badge expires. An IAM Policy is the printed rulebook that says exactly which rooms a badge can open – may open the cash vault, may not open the manager's office.

Understanding the Concepts

What is IAM?

IAM (Identity and Access Management) is the AWS service that answers two questions for every single API request: who are you (authentication), and what are you allowed to do (authorization). It is a global service, meaning you create Users, Groups, Roles, and Policies once and they work across all Regions. IAM itself is free; you pay for the AWS resources the identities access.

At National Bank of India, IAM is how we control which engineer can restart the settlement server, which service can read the customer-statements bucket, and which account can assume a cross-account analytics role.

JAVA
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.GetUserRequest;

public class WhoAmI {
    public static void main(String[] args) {
        IamClient iam = IamClient.create();
        var user = iam.getUser(GetUserRequest.builder().build()).user();
        System.out.println("Authenticated as: " + user.userName());
        System.out.println("ARN: " + user.arn());
    }
}

What is an IAM User?

An IAM User represents one identity with durable credentials. You give Rajesh a User with a console password for the web UI and optionally an access key ID and secret for the CLI or SDK. The credentials are long-lived: the password exists until changed, and access keys exist until rotated or deleted.

The problem with Users for applications is that those access keys are permanent secrets. If they leak through a Git commit, a Docker image, or a log file, an attacker can use them until someone manually rotates them. That is why we avoid Users for machines and prefer Roles.

JAVA
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.CreateUserRequest;
import software.amazon.awssdk.services.iam.model.CreateLoginProfileRequest;

public class CreateBankUser {
    public static void main(String[] args) {
        IamClient iam = IamClient.create();
        // Create a permanent user for engineer Rajesh
        iam.createUser(CreateUserRequest.builder()
                .userName("rajesh-teller").build());
        // Set console password
        iam.createLoginProfile(CreateLoginProfileRequest.builder()
                .userName("rajesh-teller")
                .password("TempP@ss123!")   // must change on first login
                .passwordResetRequired(true).build());
    }
}

What is an IAM Group?

An IAM Group exists purely for convenience. You never log in as a Group. You create a Group called Developers, attach the policies once, and add Users to it. When a new engineer joins, you add them to the Group and they instantly inherit the right permissions. Remove them from the Group and the permissions disappear. A User can belong to multiple Groups.

JAVA
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.*;

public class GroupManagement {
    public static void main(String[] args) {
        IamClient iam = IamClient.create();
        // Create the Developers group
        iam.createGroup(CreateGroupRequest.builder()
                .groupName("NBI-Developers").build());
        // Add Rajesh to the group
        iam.addUserToGroup(AddUserToGroupRequest.builder()
                .groupName("NBI-Developers")
                .userName("rajesh-teller").build());
        // Attach a policy to the group – all members get it
        iam.attachGroupPolicy(AttachGroupPolicyRequest.builder()
                .groupName("NBI-Developers")
                .policyArn("arn:aws:iam::123456789012:policy/ReadStatements")
                .build());
    }
}

What is an IAM Role?

An IAM Role is the modern, secure way to grant permissions. A Role has no password and no permanent access keys. Instead, a trusted principal assumes the Role through STS (Security Token Service) and receives temporary credentials that expire, typically in an hour. An EC2 instance, a Lambda function, or a user from another account can assume a Role.

The Role has two policies: a trust policy saying who is allowed to assume it, and permission policies saying what it can do once assumed.

JAVA
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;

public class AssumeAnalyticsRole {
    public static void main(String[] args) {
        StsClient sts = StsClient.create();
        var response = sts.assumeRole(AssumeRoleRequest.builder()
                .roleArn("arn:aws:iam::123456789012:role/StatementReaderRole")
                .roleSessionName("rajesh-analytics-session")
                .build());
        var creds = response.credentials();
        System.out.println("Temporary key: " + creds.accessKeyId());
        System.out.println("Expires: " + creds.expiration());
        // These credentials expire – far safer than embedded keys
    }
}

What is an IAM Policy?

An IAM Policy is the JSON document that actually decides allow or deny. It is attached to a User, Group, or Role. The policy has a Version, and one or more Statements, each with an Effect (Allow or Deny), an Action (the API calls), and a Resource (the ARNs). A minimal policy that lets the holder read one S3 bucket of statements looks like this:

JSON
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "ReadBankStatements",
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::nbi-customer-statements",
      "arn:aws:s3:::nbi-customer-statements/*"
    ]
  }]
}

How is a User different from a Role?

| Aspect | IAM User | IAM Role |
|---|---|---|
| Credentials | Permanent password and/or access keys | Temporary credentials from STS, expire in 1-12 hours |
| Intended for | Human operators, legacy apps | Machines (EC2, Lambda), cross-account, federation |
| If credentials leak | Attacker has access until manual rotation | Token expires automatically; damage is time-limited |
| Rotation | Manual; must update every app using the key | Automatic; STS issues new credentials each session |
| Best practice | Use for humans with MFA; avoid for code | Use for all programmatic access |

JAVA
// WRONG: User access key embedded in code – permanent secret
String accessKey = "AKIAIOSFODNN7EXAMPLE";    // leaked = breach until rotated
String secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";

// CORRECT: Role assumed by EC2 – temporary, auto-rotated
// The SDK picks up credentials from the instance metadata automatically
S3Client s3 = S3Client.create();  // no keys in code at all

What are common mistakes?

The most common mistake is giving every developer admin access. This violates least privilege and means a single compromised account can destroy the entire AWS setup. The fix is to create Groups with scoped policies.

The second mistake is embedding access keys in application code. The keys end up in Git history, Docker layers, and log files. The fix is to use Roles for all machine access.

The third mistake is not enabling MFA on User accounts, especially the root account. Without MFA, a leaked password is enough for full access.

JAVA
public class IAMMistakes {
    // WRONG: admin policy on every developer
    // { "Effect": "Allow", "Action": "*", "Resource": "*" }

    // CORRECT: scoped policy per team
    // Tellers: read/write transfers table only
    // Auditors: read-only on all tables
    // Admins: full access with MFA required
}

How do effective permissions work?

Permissions are the union of everything attached. If Rajesh is a User in the Tellers Group with one policy and also has an inline policy of his own, his effective permissions are all of them combined, minus any explicit deny, which always wins. The evaluation order is: if any policy says Deny, the request is denied; otherwise if any policy says Allow, it is permitted; otherwise it is implicitly denied.

DIAGRAM
Rajesh (User)
├── Inline policy: Allow s3:GetObject on statements bucket
├── Group: Tellers
│   └── Group policy: Allow dynamodb:GetItem on transfers table
└── Effective permissions:
    Allow s3:GetObject on statements bucket (from inline)
    Allow dynamodb:GetItem on transfers table (from group)
    Everything else: implicitly denied

What should a senior engineer know?

A senior engineer knows that IAM is the single most critical security service in AWS. They use Roles for all programmatic access, enforce MFA on all human Users, and use AWS Organizations service control policies to set guardrails across the entire account structure. They know that the root account should never be used for daily operations and should have MFA with a hardware key stored in a safe.

They also understand that IAM policies are additive, permission boundaries set a ceiling on what a Role can do even if its own policy allows more, and session policies further restrict permissions during an assumed-role session.

JSON
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PermissionBoundaryCeiling",
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:::nbi-*/*"
  }]
}

Visual Overview

DIAGRAM
┌─────────────────── AWS Account ───────────────────┐
│                                                    │
│  IAM User (Rajesh)                                 │
│  ├── Console password + MFA                        │
│  ├── Member of Group: NBI-Developers               │
│  │   └── Policy: ReadStatements                    │
│  └── Inline Policy: WriteTransfers                 │
│                                                    │
│  IAM Role (SettlementServiceRole)                  │
│  ├── Trust Policy: EC2 can assume this role        │
│  └── Permission Policy: ReadWriteStatements        │
│       │                                            │
│       ▼                                            │
│  EC2 Instance (Settlement Service)                 │
│  └── Instance Profile → Role                       │
│      └── SDK auto-fetches temporary credentials    │
│          from instance metadata (169.254.169.254)  │
│          └── Credentials expire in 1 hour          │
│              └── SDK auto-refreshes before expiry  │
│                                                    │
│  Policy Evaluation Flow:                           │
│  Request → Gather all policies                     │
│         → Explicit Deny? ──YES──→ DENIED           │
│         → Explicit Allow? ─YES──→ ALLOWED          │
│         → Neither? ─────────────→ DENIED (implicit)│
└────────────────────────────────────────────────────┘

Key Takeaway

IAM is the AWS service that answers two questions for every request: who are you, and what are you allowed to do. The four building blocks are User, Group, Role, and Policy, and the key is understanding which ones are identities and which one is the permission itself. A Policy is the permission – a JSON document that says allow or deny a set of actions on specific resources. It carries no identity on its own; you attach it to something.

A User is a permanent identity with long-lived credentials, meant for a human or a legacy application that needs its own password or access keys. A Group is not something you log in as; it is a bundle you attach policies to once and then add Users into, so twenty engineers who need the same access all inherit it from one place and a single edit updates them all. A Role is the one people misunderstand most – it is an identity with no permanent credentials that is assumed temporarily through STS, handing back short-lived credentials that expire in about an hour.

The reason Roles matter so much in banking is security. At National Bank of India our statement-generator service runs on EC2 and needs to read the customer-statements bucket. If we embedded an access key in that code and it leaked through a log or a repository, an attacker could use it until a human noticed and rotated it. Instead the EC2 instance assumes a Role, receives temporary credentials automatically, and those credentials rotate on their own, so there is simply no long-lived secret to steal.

The way I explain the effective permissions in an interview is this: a User's permissions are the union of its own inline policies plus every policy on every Group it belongs to, and then any explicit deny overrides everything. If nothing allows the action, the implicit deny applies. So in our setup engineers get read access by being in the Developers group, the service gets the same read access through an assumed Role with no keys in the code, and the policy itself is written once and reused everywhere, which is exactly how you keep a bank's access model both secure and maintainable.

Remember one sentence: User is a permanent person, Group bundles them, Role is a temporary badge for machines and cross-account access, and Policy is the JSON rulebook that actually says Allow or Deny.

---

Share
Use ← → arrow keys to navigate