Free — no signup required

Row-Level Security: IAM Policy Variables

2 min read

The Scaling Problem

You have one million users. Each user should only be able to read and write their own data. You cannot create one million IAM roles — IAM has a hard limit of 5,000 roles per account, and even if it didn't, managing them would be operationally impossible.

The solution is a single IAM role whose policy contains variables that resolve to the current user's identity at the moment the credentials are issued. AWS evaluates these variables when checking each API call, so the same policy document enforces different boundaries for every user.

The Policy Variable

The key variable is ${cognito-identity.amazonaws.com:sub}, which resolves to the user's Cognito Identity ID — a stable, unique identifier like us-east-1:a1b2c3d4-e5f6-7890-abcd-ef1234567890. This ID is consistent across sessions for the same user.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPersonalS3Folder",
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
      "Resource": [
        "arn:aws:s3:::my-app-bucket/users/${cognito-identity.amazonaws.com:sub}/*"
      ]
    },
    {
      "Sid": "AllowPersonalDynamoDBRow",
      "Effect": "Allow",
      "Action": ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:UpdateItem"],
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/UserProfiles",
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
        }
      }
    }
  ]
}

What This Achieves

When Alice's credentials are issued, AWS substitutes her Identity ID into the policy at evaluation time. Her s3:PutObject call to /users/alice-id/photo.jpg succeeds. Her attempt to call s3:PutObject on /users/bob-id/photo.jpg fails with AccessDenied — not because of application logic, but because IAM itself enforces the boundary. Even if your application has a bug that constructs the wrong path, AWS rejects the call.

The DynamoDB condition dynamodb:LeadingKeys enforces the same pattern at the database row level. Alice can only GetItem or PutItem on rows where the partition key equals her Identity ID. This is true row-level security enforced by the IAM control plane, not your application code.

Interview Tip

A common interview question: "How do you implement per-user data isolation in DynamoDB for a mobile app without a backend?" The answer is Cognito Identity Pools + IAM policy variables + the dynamodb:LeadingKeys condition key. This combination enforces isolation at the AWS API layer, meaning no amount of client-side tampering can access another user's data. Follow-up: "What's the partition key design?" — each user's records use their Cognito Identity ID as the partition key.

This is one of 18 chapters

Get every chapter — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

See pricing