# IAM Least Privilege Patterns

> Implement least privilege IAM policies with scoped permissions, permission boundaries, and IRSA for Kubernetes

- Skill: `nicolasmosquerar/iam-least-privilege-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nicolasmosquerar/iam-least-privilege-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nicolasmosquerar/iam-least-privilege-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: nicolasmosquerar (https://skillmd.com/u/nicolasmosquerar)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/nicolasmosquerar/iam-least-privilege-patterns

---


# IAM Least Privilege Patterns

## Overview

The principle of **least privilege** ensures that IAM entities (users, roles, services) have only the permissions necessary to perform their tasks—nothing more. This reduces the blast radius of security incidents and meets compliance requirements.

```mermaid
flowchart TB
    subgraph "Permission Layers"
        PB[Permission Boundary]
        RP[Role Policy]
        RP2[Resource Policy]
    end
    
    subgraph "Effective Permissions"
        EP[Intersection of All Layers]
    end
    
    PB --> EP
    RP --> EP
    RP2 --> EP
    
    style EP fill:#90EE90
```

## Key Concepts

### Permission Boundaries
A **permission boundary** sets the maximum permissions a role can have, regardless of its attached policies. Think of it as a "ceiling" on permissions.

### Service-Linked Roles
AWS-managed roles with predefined permissions for specific services. Use these when available instead of creating custom roles.

### IRSA (IAM Roles for Service Accounts)
For Kubernetes workloads on EKS, IRSA allows pods to assume IAM roles without embedding credentials. This is the recommended approach for EKS.

## Best Practices

1. **Start with zero permissions** - Add only what's needed
2. **Use conditions** - Restrict by IP, time, tags, or resource attributes
3. **Scope to resources** - Avoid `Resource: "*"` when possible
4. **Use permission boundaries** - Prevent privilege escalation
5. **Prefer managed policies** - For common use cases
6. **Implement IRSA** - For Kubernetes workloads on EKS
7. **Regular access reviews** - Use IAM Access Analyzer
8. **Use service control policies (SCPs)** - For organization-wide guardrails

## Anti-Patterns to Avoid

❌ Using `AdministratorAccess` for applications  
❌ Hardcoding credentials in code  
❌ Overly permissive `Resource: "*"` statements  
❌ Sharing IAM roles across unrelated services  
❌ Long-lived access keys instead of temporary credentials  

---

## Example 1: Terraform - Lambda with Scoped Permissions + Permission Boundary

This example creates a Lambda function with:
- A scoped IAM role limited to specific S3 bucket and DynamoDB table
- A permission boundary to prevent privilege escalation
- CloudWatch Logs permissions for the function's log group only

📁 **Location**: [terraform/examples/iam-least-privilege/](file:///home/nmosquerar/skills-repo/terraform/examples/iam-least-privilege/)

### Key Features

```hcl
# Permission boundary - the ceiling for this role
resource "aws_iam_policy" "permission_boundary" {
  name = "${local.name_prefix}-boundary"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["s3:*", "dynamodb:*", "logs:*"]
        Resource = "*"
        Condition = {
          StringEquals = {
            "aws:RequestedRegion" = var.aws_region
          }
        }
      },
      {
        Effect   = "Deny"
        Action   = ["iam:*", "organizations:*", "sts:AssumeRole"]
        Resource = "*"
      }
    ]
  })
}

# Role with scoped policy
resource "aws_iam_role_policy" "lambda_policy" {
  role = aws_iam_role.lambda.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["s3:GetObject", "s3:PutObject"]
        Resource = "${aws_s3_bucket.data.arn}/*"  # Scoped to specific bucket
      }
    ]
  })
}
```

---

## Example 2: CDK - IRSA for EKS Pod with Scoped Permissions

This example creates:
- An EKS service account with IRSA
- A scoped IAM role for S3 and Secrets Manager access
- Permission boundary attached to the role

📁 **Location**: [cdk/examples/iam-least-privilege/](file:///home/nmosquerar/skills-repo/cdk/examples/iam-least-privilege/)

### Key Features

```typescript
// Create IRSA role with scoped permissions
const serviceAccountRole = new iam.Role(this, 'PodRole', {
  assumedBy: new iam.FederatedPrincipal(
    cluster.openIdConnectProvider.openIdConnectProviderArn,
    {
      StringEquals: {
        [`${cluster.clusterOpenIdConnectIssuer}:sub`]: 
          `system:serviceaccount:${namespace}:${serviceAccountName}`,
        [`${cluster.clusterOpenIdConnectIssuer}:aud`]: 'sts.amazonaws.com'
      }
    },
    'sts:AssumeRoleWithWebIdentity'
  ),
  permissionsBoundary: permissionBoundary,
});

// Add scoped S3 policy
serviceAccountRole.addToPolicy(new iam.PolicyStatement({
  effect: iam.Effect.ALLOW,
  actions: ['s3:GetObject', 's3:PutObject'],
  resources: [`${dataBucket.bucketArn}/*`],
  conditions: {
    StringEquals: {
      's3:x-amz-acl': 'bucket-owner-full-control'
    }
  }
}));
```

---

## Validation Checklist

- [ ] No `Resource: "*"` without conditions
- [ ] Permission boundary attached to all custom roles
- [ ] IRSA used for EKS workloads (no static credentials)
- [ ] IAM Access Analyzer enabled
- [ ] No inline policies with `*` actions
- [ ] All roles have meaningful descriptions
- [ ] Tags applied for ownership tracking

## Related Skills

- [OIDC & IRSA Patterns](../oidc-irsa-patterns/SKILL.md) - Deeper dive into IRSA
- [Policy as Code](../policy-as-code/SKILL.md) - Enforce IAM policies with OPA
- [Secrets Externalization](../secrets-externalization/SKILL.md) - Never hardcode credentials

