Identity & Access Management Best Practices

SkillDocs & knowledge

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.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Identity & Access Management Best Practices skill

What this skill tells your AI

The instructions your AI receives, as published by shieldnet-360/secure-vibe in skills/iam-best-practices/SKILL.md and read by ahel’s review.

Rules (for AI agents)

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.

Context (for humans)

Cloud IAM has one property that makes reviewing it different from reviewing other access control: the effective permission set is not the stated one. A role that can attach a policy can attach AdministratorAccess to itself. A role that can pass another role into a service it can also create runs as that role. Neither policy contains a wildcard, and both are administrative. Reading a policy for what it says, rather than for what it can reach, is the failure that produces most over-permissioned estates.

The second property is that the scalable answer is rarely a better-written policy. Permissions boundaries and organization policies are what let a platform team hand role creation to product teams without handing over the account, and attribute-based scoping is what keeps a policy correct as the resource set grows. Enumerating ARNs more carefully does not scale past the point where someone starts pasting * to make the deploy pass.

Capital One (2019) and Uber (2022) are the canonical illustrations of the two halves: an over-permissioned role reached through a server-side request, and a static credential found in a script after an MFA-fatigue login.

References

Signals

GitHub stars
22
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
iam-best-practices
Source
github.com/shieldnet-360/secure-vibe