Advanced IAM & Security
IAM is arguably the most complex and critical service in AWS. These advanced topics appear frequently on the DVA-C02 exam.
IAM Policy Evaluation Logic (Complete Flow)
When AWS evaluates an API request, it follows a strict order:
"Effect": "Deny" in any applicable policy immediately halts evaluation. To succeed, an explicit "Effect": "Allow" must be present, and no boundary or SCP may restrict it.Key rule: For an action to be allowed, ALL applicable policy types must allow it. ONE explicit Deny anywhere = denied.
Identity Policies vs Resource Policies
| Characteristic | Identity Policy | Resource Policy |
|---|---|---|
| Attached to | IAM User, Group, or Role | S3 Bucket, KMS Key, SQS Queue, Lambda |
| Defines | What this identity can do | Who can access this resource |
| Principal element | β Cannot have | β MUST have |
| Cross-account | Grants outbound access | Grants inbound access |
Same-Account Access
Either identity policy OR resource policy can grant access.
You don't need both β just one Allow is sufficient.
Cross-Account Access
Account A's identity policy must allow the action
AND
Account B's resource policy must allow Account A's principal
EXCEPTION: If the resource policy specifies the exact role ARN
(not just the account), the identity policy is not strictly needed.
Resource Policies That Support Cross-Account
| Service | Resource Policy Name |
|---|---|
| S3 | Bucket Policy |
| SQS | Queue Policy |
| SNS | Topic Policy |
| Lambda | Function Policy |
| KMS | Key Policy |
| ECR | Repository Policy |
| API Gateway | Resource Policy |
| Secrets Manager | Resource Policy |
Permissions Boundaries
A Permissions Boundary sets the maximum permissions. It does NOT grant permissions on its own.
Effective Permissions = Intersection
| Policy Type | Declared Allowed Actions | Set Logic | Effective Runtime Result |
|---|---|---|---|
| Identity-Based Policy | s3:*, dynamodb:*, lambda:* | Requested Privileges | β |
| Permissions Boundary | s3:Get*, dynamodb:* | Maximum Allow Ceiling | β |
| Effective Permissions | s3:Get*, dynamodb:* | lambda:* and s3:Put* are strictly blocked! |
Delegated Administration Use Case
Problem: Senior admin wants to let junior dev create Lambda execution roles, but prevent privilege escalation.
// Step 1: Create Permission Boundary policy
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject", "dynamodb:GetItem", "dynamodb:PutItem", "logs:*"],
"Resource": "*"
}]
}
// Step 2: Allow junior dev to create roles WITH boundary attached
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["iam:CreateRole", "iam:PutRolePolicy", "iam:AttachRolePolicy"],
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::123456789012:policy/LambdaBoundary"
}
}
}]
}
Now the junior dev can create roles, but every role MUST have the boundary β preventing them from creating overly permissive roles.
Service Control Policies (SCPs)
SCPs are guardrails for AWS Organizations:
AWS Organization
βββ Root
β βββ SCP: "DenyDeleteS3" (applied to all accounts)
β βββ OU: Production
β β βββ SCP: "DenyNonApprovedRegions"
β β βββ Account: prod-us-east-1
β β βββ Account: prod-eu-west-1
β βββ OU: Development
β βββ No additional SCP (inherits root)
β βββ Account: dev-sandbox
Key rules:
- SCPs do NOT grant permissions β they only restrict
- SCPs apply to all users/roles in the account (including root!)
- Management account is NOT affected by SCPs
- SCPs must explicitly Allow actions (deny-by-default)
Common SCP Patterns
// Deny all regions except approved ones
{
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["us-east-1", "eu-west-1"]
}
}
}
// Prevent disabling CloudTrail
{
"Effect": "Deny",
"Action": ["cloudtrail:StopLogging", "cloudtrail:DeleteTrail"],
"Resource": "*"
}
Cross-Account Role Assumption (STS) Deep Dive
Step-by-Step
1. Account B (target) creates a role: CrossAccountRole
2. Account B's Trust Policy allows Account A:
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111111111111:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "shared-secret-123" }
}
}
3. Account A's identity policy allows sts:AssumeRole on Account B's role ARN
4. Account A calls sts:AssumeRole with ExternalId
5. STS returns temporary credentials valid for 1-12 hours
Confused Deputy Prevention
Without ExternalId:
Attacker (Account C) tricks Service into assuming role in Account B
β Service uses its own credentials to assume the role
β Attacker gains access to Account B's resources
With ExternalId:
Role requires ExternalId that only legitimate caller knows
β Attacker can't provide the correct ExternalId
β AssumeRole fails
Web Identity Federation
OIDC Federation (Mobile/Web Apps)
User β Login with Google/Apple β JWT Token
β Cognito Identity Pool (or direct AssumeRoleWithWebIdentity)
β STS returns temporary AWS credentials
β App calls S3/DynamoDB directly
SAML 2.0 Federation (Corporate)
Employee β Login via AD/Okta β SAML Assertion
β App calls sts:AssumeRoleWithSAML
β STS returns temporary AWS credentials
β Employee accesses AWS Console or APIs
IAM Identity Center (SSO)
- Modern replacement for manual SAML federation
- Centralized access management for all AWS accounts
- Integrates with AD, Okta, Azure AD
- Provides temporary credentials via SSO portal
Attribute-Based Access Control (ABAC)
Instead of creating separate policies per team, use tags as policy conditions:
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::company-data/*",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/department": "${aws:PrincipalTag/department}"
}
}
}
Benefits: One policy works for all teams. New team? Just tag the user β no policy changes.
DVA-C02 Exam Tips
- Explicit Deny always wins β no exceptions
- SCP doesn't grant β only restricts (and doesn't affect management account)
- Permission Boundary = max permissions ceiling (intersection with identity policy)
- Cross-account = identity policy + resource policy (both needed)
- Same-account = either identity OR resource policy sufficient
- ExternalId = confused deputy prevention for third-party access
- Resource policies with specific principal can grant cross-account independently
- ABAC = tag-based policies scale better than role-per-team
- AssumeRole default 1h, max 12h (configured on the role)
- IAM Identity Center = modern SSO for organizations
Practice Questions
Q1. IAM user has s3:* policy. SCP denies s3:DeleteBucket. User tries to delete. Result?
A) Allowed β identity policy permits
B) Denied β SCP explicit deny wins
C) Allowed β SCP doesn't affect users
D) Depends on bucket policy
β Answer & Explanation
B β SCPs restrict ALL principals in the account. Explicit Deny in SCP overrides any Allow.
Q2. Cross-account: Account A needs S3 access in Account B. Most secure approach?
A) Share access keys
B) VPC Peering
C) IAM Role in Account B + AssumeRole from Account A
D) Public bucket
β Answer & Explanation
C β AssumeRole provides temporary credentials. VPC Peering is for network connectivity, not S3 access control.
Q3. Junior dev creates a Lambda role with AdministratorAccess. How to prevent this?
A) Remove iam:CreateRole from the dev
B) Require a Permission Boundary on all created roles
C) SCP denying iam:CreateRole
D) Enable MFA
β Answer & Explanation
B β Permission Boundaries ensure any role the dev creates is capped at the boundary's permissions, preventing privilege escalation.
Q4. Role in Account B has trust policy allowing Account A. Account A's user has NO identity policy for AssumeRole. Can the user assume the role?
A) No β identity policy must also allow sts:AssumeRole
B) Yes β trust policy is sufficient
C) Yes β if ExternalId matches
D) Depends on SCP
β Answer & Explanation
A β Cross-account access requires BOTH the resource policy (trust policy) AND the identity policy. The user needs sts:AssumeRole permission on the target role ARN.
