Least-privilege cloud IAM: policy scope, privilege-escalation paths, permissions boundaries, workload identity over static keys, MFA enforced by policy, cross-account trust, and separating deploy from runtime. Use when generating IAM policies, roles, or trust documents, wiring CI/CD service accounts or workload identities, delegating role creation, or designing cross-account access.
Installs into .claude/skills of the current project.
Are you the author of Iam Best Practices?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/shieldnet-360-iam-best-practices-secure-vibe)
---
name: iam-best-practices
description: "Least-privilege cloud IAM: policy scope, privilege-escalation paths, permissions boundaries, workload identity over static keys, MFA enforced by policy, cross-account trust, and separating deploy from runtime. Use when generating IAM policies, roles, or trust documents, wiring CI/CD service accounts or workload identities, delegating role creation, or designing cross-account access."
---
<!-- Native skill bundle for Claude Code. Generated by `secure-vibe dev regenerate`. -->
<!-- Do not edit by hand; the source of truth is skills/iam-best-practices/SKILL.md. -->
# Identity & Access Management Best Practices
Least-privilege cloud IAM: policy scope, privilege-escalation paths, permissions boundaries, workload identity over static keys, MFA enforced by policy, cross-account trust, and separating deploy from runtime. Use when generating IAM policies, roles, or trust documents, wiring CI/CD service accounts or workload identities, delegating role creation, or designing cross-account access.
## ALWAYS
- Grant the minimum permissions the workload's stated job requires: deny by default, then add concrete actions against concrete resources. A wildcard action on a wildcard resource — in a custom policy, in `AdministratorAccess`, or in `iam:*` attached to something that runs continuously — is the same finding wearing three different names.
- Read a policy for the **privileges it can reach**, not only the ones it names. A principal that can write policy (`iam:PutRolePolicy`, `iam:AttachRolePolicy`, `iam:CreatePolicyVersion`) can grant itself anything, and a principal that can pass a role into a compute service it may also create (`iam:PassRole` with `lambda:CreateFunction`, `ec2:RunInstances`, `glue:CreateJob`) inherits that role's privileges. These read as narrow grants and are administrative ones. `references/escalation-paths.md` lists the combinations worth failing a review over.
- Bound delegated permissions with a **permissions boundary** or an organization-level policy (SCP, GCP org policy, Azure management-group policy) when teams may create their own roles. The boundary caps what any role they create can do, which is what makes self-service role creation safe rather than a privilege-escalation primitive.
- Prefer **workload identity** — IRSA on EKS, GKE Workload Identity, Azure Managed Identity, OIDC federation from CI — over a static key. The goal is that no long-lived credential exists to leak. Where one is genuinely unavoidable, give it a documented rotation cadence and automatic disablement on inactivity, and treat that as a temporary state rather than the steady state.
- Enforce MFA for human principals through a **policy condition** (`aws:MultiFactorAuthPresent`), not only a directory setting, so a credential that bypasses the directory still cannot act.
- Separate the **deploy** identity from the **runtime** identity. The pipeline gets permission to create and change infrastructure; the running service gets permission to do its job and nothing that mutates IAM. `cicd-security` owns hardening the pipeline that holds the deploy role.
- Scope cross-account trust to the actual counterparty. Inside your own organization that means a specific principal ARN or `aws:PrincipalOrgID`; for a **third party** acting on your behalf it additionally means an `ExternalId` — a per-consumer value the third party supplies, which defeats the confused-deputy case where another of their customers can name your account. Treat `ExternalId` as an identifier, not a secret: it is not one, and relying on its confidentiality is a false assurance.
- Keep sessions short and make the length a deliberate choice. Human and CI roles should use the shortest session the workflow tolerates. A longer window on a break-glass role is a defensible operational trade — an incident should not expire mid-response — but it has to be stated as a decision, paired with alerting, and not copied onto ordinary roles.
- For Kubernetes RBAC, scope `Role` and `RoleBinding` to one namespace and reserve `ClusterRole` for genuinely cluster-wide objects. Review every `cluster-admin` binding at pull-request time, and remember that permission to create workloads in a namespace is permission to use every service account in it.
- Log IAM-mutating calls to a tamper-evident sink and alert on them: policy changes, `iam:PassRole`, access-key creation, and role assumption from an unexpected principal. `logging-security` owns the shape of the record.
- Require out-of-band approval for break-glass use (root, organization owner, `cluster-admin`) and alert on every invocation, not on a sample.
## NEVER
- Use the root or organization-owner account for day-to-day work. It gets a hardware MFA device, offline storage, and only the tasks that genuinely require it.
- Embed a long-lived access key in source, a container image, a machine image, or a CI environment variable where a workload identity or OIDC federation is available.
- Grant `iam:PassRole` with `Resource: "*"`. Pin the exact role ARNs the caller may pass, and read that grant together with what the caller can launch.
- Share one principal between multiple humans or multiple services. One identity per actor is what makes the audit log mean anything.
- Disable MFA for a principal to resolve a login problem. Rotate the device; the requirement stays.
- Persist an OIDC or SAML assertion beyond its stated lifetime. Refresh by re-assertion rather than storing the original token.
## KNOWN FALSE POSITIVES
- `Resource: "*"` on APIs that accept no resource ARN — `sts:GetCallerIdentity`, `ec2:DescribeRegions` and their equivalents. The wildcard is the only expressible value, so the finding is the *action* being unnecessary, not the resource scope.
- Provider-managed service-linked roles carry broader permissions than a role you would write. That breadth is the provider's contract and is not yours to narrow.
- A bootstrap operator in a fresh account often needs elevated permissions before the guardrails it installs exist. Gate it by tag or organization policy and revoke on completion — a bootstrap role that survives bootstrap is a finding again.
- Local development emulators accept any credential. That is a property of the emulator, not a grant in your account.
- Enumerating explicit resource ARNs is correct for a bounded set. Attribute-based scoping (tags, organization paths) is the answer when the set grows, not a security upgrade over naming resources you can name.
## Reference files
Read these only when the task calls for them.
- `references/escalation-paths.md`
- `references/verifying-findings.md`