interview-prep

Crisp answer: IAM (Identity and Access Management) controls who can do what in AWS. AWS evaluates permissions using a deny-by-default model: access is denied unless explicitly allowed, and an explicit deny always wins.

IAM entities:

Users:   Individual people or applications with long-term credentials
Groups:  Collections of users that share permissions
Roles:   Assumed by services, applications, or users — short-term credentials
         via STS (Security Token Service)

Policy types (in evaluation order):

1. Service Control Policies (SCPs)     — org-level ceiling, cannot grant above this
2. Resource-based policies             — attached to S3 buckets, KMS keys, etc.
3. Identity-based policies             — attached to users, groups, roles
4. Permission boundaries               — maximum permissions an entity can have
5. Session policies                    — passed when assuming a role

The evaluation logic:

1. Default deny — start with no access
2. Evaluate all applicable policies
3. If any policy has an explicit Deny → DENY (regardless of any Allow)
4. If any policy has an Allow → ALLOW
5. If no policy has an Allow → DENY (implicit deny)

Policy structure:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ReadOnly",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ],
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "eu-west-2"
        }
      }
    }
  ]
}

IAM Roles for Services:

EC2 instances, Lambda functions, EKS pods — they all assume IAM roles. The role's trust policy defines who can assume it:

{
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "lambda.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }]
}

IRSA — IAM Roles for Service Accounts (EKS):

Pods on EKS assume IAM roles via OIDC federation:

resource "aws_iam_role" "s3_reader" {
  assume_role_policy = jsonencode({
    Statement = [{
      Effect    = "Allow"
      Principal = { Federated = aws_iam_openid_connect_provider.eks.arn }
      Action    = "sts:AssumeRoleWithWebIdentity"
      Condition = {
        StringEquals = {
          "${aws_iam_openid_connect_provider.eks.url}:sub" =
            "system:serviceaccount:default:s3-reader"
        }
      }
    }]
  })
}

Useful IAM debugging commands:

# Simulate what a principal can do
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789:role/my-role \
  --action-names s3:GetObject \
  --resource-arns arn:aws:s3:::my-bucket/key

# See effective permissions (what an entity can do)
aws iam get-policy --policy-arn arn:aws:iam::123456789:policy/my-policy
aws iam list-attached-role-policies --role-name my-role
aws iam list-role-policies --role-name my-role  # inline policies

# Who has access to an S3 bucket?
aws s3api get-bucket-policy --bucket my-bucket

What to say in the interview:

"IAM is deny by default: you need an explicit allow and no explicit deny. The evaluation hierarchy matters: SCPs set the ceiling for the whole org, then identity-based policies, then resource-based policies. An explicit deny anywhere in the chain always wins. For EKS pods I use IRSA so each pod's service account assumes a specific IAM role with only the permissions that pod needs — no broad instance profile permissions shared across all pods on the node."


My notes