IAM Advanced via SDK Best Practices
Policy review cadence and least-privilege enforcement patterns for advanced IAM setups - permission boundaries, cross-account trust, and org-wide guardrails.
Search across all documentation pages
Policy review cadence and least-privilege enforcement patterns for advanced IAM setups - permission boundaries, cross-account trust, and org-wide guardrails.
Each rule is stated positively with a one-line reason and how to enforce it via the SDK.
Allow in a boundary or SCP does nothing without a matching Allow in an identity-based or resource-based policy.SimulatePrincipalPolicy catches a missing Allow or an unexpected Deny before it reaches production.Resource: "*" is genuinely necessary, note why so a reviewer doesn't mistake it for laziness.{
"Sid": "RequireEncryptedRead",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::acme-reports/*",
"Condition": {
"StringEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
}
}iam:CreateRole without a boundary condition. Require iam:PermissionsBoundary to match your approved boundary ARN, or a delegated admin can create an unbounded role.iam:DeleteRolePermissionsBoundary for delegated roles. Without this, an admin can create a properly-bounded role and strip the boundary off right after.iam:CreatePolicyVersion on the boundary's ARN, or they can widen their own ceiling.# --- Python (boto3) ---
# Good: CreateRole call includes the required boundary ARN.
iam.create_role(RoleName="team-service-role",
AssumeRolePolicyDocument=trust_doc,
PermissionsBoundary=boundary_arn)// --- TypeScript (AWS SDK v3) ---
// Good: CreateRoleCommand includes the required boundary ARN.
await iam.send(new CreateRoleCommand({
RoleName: "team-service-role",
AssumeRolePolicyDocument: trustDoc,
PermissionsBoundary: boundaryArn,
}));sts:ExternalId for third-party trust relationships. It defends against the confused deputy problem when a vendor's role ARN could otherwise be reused by an unrelated party.Principal to a specific role ARN when you know the caller. An account-root principal is looser than necessary when only one role in that account should assume yours.FullAWSAccess without an equivalent replacement. SCPs never grant access, so removing the default without a substitute drops the account to zero effective permissions.NotAction to keep iam:*, sts:*, and organizations:* working when restricting a service or region elsewhere.ListFindingsV2 for accounts with many roles. A single call may not return every finding - use the boto3 paginator or loop on nextToken.unusedAccessAge to match your real review cadence. Too short a window flags normal infrequent access as noise; start around 90 days.Because every other category assumes you already know that boundaries and SCPs are ceilings, not grants, and that explicit Deny always wins. Getting this wrong makes every downstream control confusing to debug.
Granting iam:CreateRole without also requiring the iam:PermissionsBoundary condition. Without it, a delegated admin can simply create an unbounded role and skip the safety cap entirely.
It protects against the confused deputy problem - a scenario where an intermediary service with its own trusted access could be tricked into assuming your role on behalf of an unrelated party, not the vendor being untrustworthy per se.
For one-directional, resource-scoped sharing, usually yes - there's no session lifecycle to manage. Role assumption is still preferable when you need a distinct, auditable permission set that differs from the caller's own identity.
Every account under that OU drops to zero effective permissions, since SCPs never grant access on their own - everything falls to implicit Deny until a replacement Allow is attached.
No. The management account is exempt from all SCPs by design, so other controls (IAM policies, monitoring, alerting) need to cover it.
On a recurring cadence tied to your security review cycle, not once. The analyzer runs continuously, so new findings appear as access ages past the configured window.
Review with the resource owner first. A permission unused within the lookback window can still back a legitimate quarterly or annual task that the window didn't capture.
Because an SCP takes effect immediately once attached to a live OU, and a misconfigured Deny can block legitimate operations account-wide with no warning period.
Run SimulatePrincipalPolicy against the specific actions and resources your workload needs, before and after the change, and compare the EvalDecision values.
Not necessarily - a boundary caps one role or user, while an SCP caps an entire account or OU. They're often layered together but solve different scope problems.
Wire the checkable ones into CI (policy linting, boundary-condition checks) or a recurring Lambda that re-runs Access Analyzer and posts findings somewhere visible, rather than relying on memory.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 25, 2026