Back to blog
Article

IAM permissions a three-person team can actually maintain

IAM permissions a three-person team can actually maintain
S

StriveBit

4 min readCloud Solutions

IAM permissions a three-person team can actually maintain

A client in Greater Noida has a Django API, a Postgres RDS instance, an S3 bucket for uploads, and a CloudFront distribution. Six developers touch the AWS account. Nobody owns the IAM configuration full-time. The previous setup had one admin user shared across the team and a handful of policies that granted `s3:*` on `*` because someone needed to upload to one bucket and the shortest path was a broad statement.

This is the normal state of IAM in small teams. The fix is not a permissions review committee. The fix is a small set of role-based policies, named clearly, scoped to specific ARNs, and checked into the same repository as the application code.

We start by listing what each workload actually does. The Django API reads and writes to one RDS instance, puts objects in one S3 bucket, and writes logs to one CloudWatch log group. That is the complete list. The policy for the API's instance role is about fifteen lines.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:AbortMultipartUpload"
      ],
      "Resource": "arn:aws:s3:::client-uploads-prod/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:ap-south-1:*:log-group:/ecs/client-api-prod/*"
    }
  ]
}

The RDS credentials come from Secrets Manager, so the role also gets `secretsmanager:GetSecretValue` on one specific ARN. Nothing else. No `s3:ListBucket` on all buckets, no `logs:CreateLogGroup` because the log group already exists and is managed by Terraform.

The tradeoff here is that someone has to add a new permission when the application needs one. That is the point. A developer who needs to add a new S3 bucket for exports writes a one-line Terraform change, opens a PR, and merges it after someone else looks at it. This takes about four minutes. The alternative is a policy that says `s3:*` on `*`, which takes zero minutes to add and approximately forever to audit when something goes wrong.

For human users, we use three roles. `dev-read-only` can log in, view resources, read logs, and download objects from S3. `dev-deployer` can additionally update ECS services, invalidate CloudFront, and restart EC2 instances. `admin` can do everything, and only two people have it. The roles are assumed via SSO, not via long-lived access keys. Access keys are the single biggest source of IAM problems in small teams because they get committed to Slack, pasted into `.env` files, and left on developer laptops.

We define the roles in Terraform, in the same repository as the infrastructure. When someone joins the team, we add them to the right SSO permission set. When they leave, we remove them. There is no separate IAM review process because the IAM configuration is code and goes through the same review as everything else.

The part that takes discipline is refusing to grant temporary broad permissions. A developer says they need `s3:*` on all buckets because they are debugging an upload issue. What they actually need is `s3:GetObject` and `s3:ListBucket` on one bucket, or read access to the CloudFront distribution, or the ability to check a specific log group. We write the narrow policy, deploy it, and remove it when the debugging is done. This is slower in the moment. It is faster than discovering six months later that a contractor had `AdministratorAccess` for a week in February.

We run `aws iam simulate-principal-policy` against each role once a week as part of the CI pipeline. It checks that the dev-read-only role cannot write to S3, that the deployer role cannot create new IAM users, and that nothing has drifted. If someone manually edits a policy in the console, the Terraform plan fails on the next run and we see it. The console is not where IAM changes should happen, but the pipeline is what catches it when they do.

The result is an IAM setup that six people can understand by reading one Terraform file. No wiki page documenting who has what. No quarterly access review meeting. The permissions are the documentation.

Back to all articles

Ready to build something great?

We help ambitious teams build software that lasts. If you're interested in working with us or want to discuss your project, let's connect.

Get in touch