Skip to content

Keyboard shortcuts

Go

  • Scope — the control cataloggm
  • Plan — your last certification classgp
  • Collect — the recipe indexgc
  • The control you are workinggw
  • Startgh

Move

  • Next rowj
  • Previous rowk
  • Previous in this run[
  • Next in this run]
  • Filter this page's list/
  • Search everythingK

Act

  • Copy this page's permalinky
  • Toggle dark moded
  • This sheet?

Rows are whatever the current page lists — controls on Scope, recipes on Plan and Collect.

IAM — Identity and Access Management

5 indicators, 31 controls in scope for class B; 13 of them have an authored recipe.

Class B vulnerability-response floor

VDR floor7 days

tightest MUST
7 days VDR-TFR-MVX Persistent Machine Verification and Validation for 20x
A response floor, not a collection interval — why

This is a class-widefloor on responding to a vulnerability, quoted in the dataset’s own units and never converted. It is not a per-check collection interval — the rules author none. Schedule against the MUST; the tightest entry may be a SHOULD.

  • automatable
  • partial — needs judgement
  • narrative — no API proves this
partialAccount-wide credential report proving MFA is active per principal and that passwords/access keys are rotated within policy
monthlycli
$ aws iam generate-credential-report
$ aws iam get-credential-report --query GeneratedTime --output text
$ aws iam get-credential-report --query Content --output text | base64 --decode
partialFull snapshot of every IAM user, group, role, and attached/inline policy with their relationships, used to review that granted permissions match least-privilege intent
quarterlycli
$ aws iam get-account-authorization-details --query 'UserDetailList[].{User:UserName,Groups:GroupList,Attached:AttachedManagedPolicies[].PolicyName,Inline:UserPolicyList[].PolicyName}'
$ aws iam get-account-authorization-details --filter Role --query 'RoleDetailList[].{Role:RoleName,Trust:AssumeRolePolicyDocument,Attached:AttachedManagedPolicies[].PolicyName}'
partialActive unused-access findings identifying IAM roles, access keys, console passwords, and service/action-level permissions that have not been used within the configured age, driving right-sizing and removal
continuouscli
$ aws accessanalyzer list-analyzers --type ACCOUNT_UNUSED_ACCESS --query 'analyzers[].{arn:arn,status:status,age:configuration.unusedAccess.unusedAccessAge}'
$ aws accessanalyzer list-analyzers --type ORGANIZATION_UNUSED_ACCESS --query 'analyzers[].{arn:arn,status:status,age:configuration.unusedAccess.unusedAccessAge}'
$ aws accessanalyzer list-findings-v2 --analyzer-arn <UNUSED_ACCESS_ANALYZER_ARN> --filter '{"status":{"eq":["ACTIVE"]}}'
partialAWS Config compliance result for the managed rule proving every IAM user with a console password has MFA enabled
continuousconfig-rule
$ aws configservice get-compliance-details-by-config-rule --config-rule-name mfa-enabled-for-iam-console-access --compliance-types NON_COMPLIANT
$ aws configservice describe-configuration-recorder-status
$ aws configservice describe-config-rule-evaluation-status --config-rule-names mfa-enabled-for-iam-console-access
partialAWS Config compliance result proving long-lived IAM access keys (including those used by service/non-user identities) are rotated within the maximum age
continuousconfig-rule
$ aws configservice get-compliance-details-by-config-rule --config-rule-name access-keys-rotated --compliance-types NON_COMPLIANT
$ aws configservice describe-configuration-recorder-status
$ aws configservice describe-config-rule-evaluation-status --config-rule-names access-keys-rotated
partialAWS Config compliance result proving no customer-managed IAM policy grants full administrative access (Allow Action:* on Resource:*)
continuousconfig-rule
$ aws configservice get-compliance-details-by-config-rule --config-rule-name iam-policy-no-statements-with-admin-access --compliance-types NON_COMPLIANT
$ aws configservice describe-configuration-recorder-status
$ aws configservice describe-config-rule-evaluation-status --config-rule-names iam-policy-no-statements-with-admin-access
narrativeDocumented just-in-time / break-glass privilege-elevation process backed by IAM Identity Center permission sets and account assignments, showing privileged access is role/attribute-based, time-bound, and approval-gated rather than standing
quarterlycli
# aws sso-admin list-instances --query 'Instances[0].InstanceArn' --output text
# aws sso-admin list-permission-sets --instance-arn <INSTANCE_ARN>
# aws sso-admin describe-permission-set --instance-arn <INSTANCE_ARN> --permission-set-arn <PERMISSION_SET_ARN> --query 'PermissionSet.{Name:Name,SessionDuration:SessionDuration}'
# aws sso-admin list-account-assignments --instance-arn <INSTANCE_ARN> --account-id <ACCOUNT_ID> --permission-set-arn <PERMISSION_SET_ARN>

The API proves RBAC structure and bounded session duration, but it cannot by itself prove that each elevation was requested, approved, and time-limited just-in-time. The approval/break-glass workflow and its evidence (tickets, approvals, deprovisioning records) are a documented process; attach the runbook and sampled approval records. Do not present the permission-set listing alone as proof of JIT.

partialGuardDuty IAM/credential-abuse findings (detection) paired with CloudTrail records of the responsive action taken to disable or secure the affected privileged principal
continuouscli
$ aws guardduty list-detectors --query 'DetectorIds[0]' --output text
$ aws guardduty list-findings --detector-id <DETECTOR_ID> --finding-criteria '{"Criterion":{"type":{"Eq":["UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS"]}}}'
$ aws guardduty get-findings --detector-id <DETECTOR_ID> --finding-ids <FINDING_ID>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteLoginProfile --max-results 10
partialHow operators actually reach the environment from outside it: the managed access paths that exist, the logging and encryption configured on them, the session-by-session record of who used them, and the negative check that no instance is directly reachable instead
weeklycli
$ aws ssm get-document --name SSM-SessionManagerRunShell --document-version '$LATEST' --query Content --output text
$ aws ssm describe-sessions --state History --query 'Sessions[].{owner:Owner,target:Target,start:StartDate,end:EndDate,document:DocumentName,accessType:AccessType,maxDuration:MaxSessionDuration}'
$ aws ec2 describe-client-vpn-endpoints --query 'ClientVpnEndpoints[].{id:ClientVpnEndpointId,transport:TransportProtocol,auth:AuthenticationOptions[].Type,connectionLog:ConnectionLogOptions,splitTunnel:SplitTunnel,sessionTimeoutHours:SessionTimeoutHours,serverCert:ServerCertificateArn,selfServicePortal:SelfServicePortalUrl}'
$ aws ec2 describe-client-vpn-connections --client-vpn-endpoint-id <CLIENT_VPN_ENDPOINT_ID> --query 'Connections[].{user:Username,commonName:CommonName,clientIp:ClientIp,established:ConnectionEstablishedTime,ended:ConnectionEndTime,status:Status,posture:PostureComplianceStatuses}'
$ aws configservice get-compliance-details-by-config-rule --config-rule-name ec2-instance-no-public-ip --compliance-types NON_COMPLIANT
partialEvery way a workforce user can authenticate into the account, counted and named in one pass — how many IAM users and federated trusts exist, which SAML and OIDC providers are registered, whether an IAM Identity Center instance is the workforce entry path — together with the state of the two credentials that belong to no person: the root user's access key and the account's X.509 signing certificate
monthlycli
$ aws iam get-account-summary --query 'SummaryMap.{Users:Users,Providers:Providers,MFADevices:MFADevices,MFADevicesInUse:MFADevicesInUse,AccountAccessKeysPresent:AccountAccessKeysPresent,AccountSigningCertificatesPresent:AccountSigningCertificatesPresent}'
$ aws iam list-saml-providers --query 'SAMLProviderList[].{Arn:Arn,ValidUntil:ValidUntil,CreateDate:CreateDate}'
$ aws iam list-open-id-connect-providers
$ aws sso-admin list-instances --query 'Instances[].{InstanceArn:InstanceArn,IdentityStoreId:IdentityStoreId,Status:Status}'
$ aws configservice get-compliance-details-by-config-rule --config-rule-name iam-user-mfa-enabled --compliance-types NON_COMPLIANT
$ aws configservice get-compliance-details-by-config-rule --config-rule-name iam-root-access-key-check --compliance-types NON_COMPLIANT
partialFor every Amazon Cognito directory that fronts non-organizational users: the user pool's multi-factor configuration, and every identity pool's guest-access flag together with the named external providers it will exchange a token for
weeklycli
$ aws cognito-idp list-user-pools --max-results 60 --query 'UserPools[].{Id:Id,Name:Name,Status:Status}'
$ aws cognito-idp get-user-pool-mfa-config --user-pool-id <USER_POOL_ID>
$ aws cognito-identity list-identity-pools --max-results 60 --query 'IdentityPools[].{Id:IdentityPoolId,Name:IdentityPoolName}'
$ aws cognito-identity describe-identity-pool --identity-pool-id <IDENTITY_POOL_ID> --query '{Id:IdentityPoolId,AllowUnauthenticatedIdentities:AllowUnauthenticatedIdentities,AllowClassicFlow:AllowClassicFlow,Cognito:CognitoIdentityProviders,Login:SupportedLoginProviders,Saml:SamlProviderARNs,Oidc:OpenIdConnectProviderARNs}'
partialThe configured ceiling on how long any credential stays valid before its holder must present an authenticator again — MaxSessionDuration on every IAM role, SessionDuration on every IAM Identity Center permission set, and the aws:MultiFactorAuthAge conditions in policy that expire an MFA-backed session independently of the session itself
quarterlycli
$ aws iam list-roles --query 'Roles[].{Role:RoleName,MaxSessionDuration:MaxSessionDuration}'
$ aws sso-admin list-instances --query 'Instances[].InstanceArn'
$ aws sso-admin list-permission-sets --instance-arn <INSTANCE_ARN>
$ aws sso-admin describe-permission-set --instance-arn <INSTANCE_ARN> --permission-set-arn <PERMISSION_SET_ARN> --query 'PermissionSet.{Name:Name,SessionDuration:SessionDuration}'
$ aws iam get-account-authorization-details --query '{Managed:Policies[].PolicyVersionList[?IsDefaultVersion].Document,UserInline:UserDetailList[].UserPolicyList[].PolicyDocument,RoleInline:RoleDetailList[].RolePolicyList[].PolicyDocument,GroupInline:GroupDetailList[].GroupPolicyList[].PolicyDocument}'
partialThe mechanism that ends a temporary or emergency account without anyone deciding to: the AWS Config rule that measures how long an IAM credential has gone unused, the period it is configured with, the remediation configuration proving the revocation fires automatically, and an empty non-compliant set showing nothing has outlived the period
continuousconfig-rule
$ aws configservice describe-config-rules --config-rule-names iam-user-unused-credentials-check
$ aws configservice get-compliance-details-by-config-rule --config-rule-name iam-user-unused-credentials-check --compliance-types NON_COMPLIANT
$ aws configservice describe-remediation-configurations --config-rule-names iam-user-unused-credentials-check
$ aws iam get-account-authorization-details --filter User
partialThe machine-generated inventory of every resource an external entity can reach — IAM Access Analyzer's active ExternalAccess findings — read against the declared zone of trust, so the terms-and-conditions review has a list to work from rather than a memory
quarterlycli
$ aws accessanalyzer list-analyzers --query 'analyzers[].{Name:name,Type:type,Status:status,Arn:arn,LastAnalyzedAt:lastResourceAnalyzedAt}'
$ aws accessanalyzer list-findings-v2 --analyzer-arn <ANALYZER_ARN> --filter '{"status":{"eq":["ACTIVE"]},"findingType":{"eq":["ExternalAccess"]}}'
$ aws organizations describe-organization --query 'Organization.{Id:Id,FeatureSet:FeatureSet,ManagementAccount:MasterAccountId}'
$ aws configservice get-compliance-details-by-config-rule --config-rule-name iam-external-access-analyzer-enabled --compliance-types NON_COMPLIANT
partialThe enforced half of who may change what: the service control policy type actually enabled in the organization root, the customer-authored SCPs and the roots, OUs and accounts each one is attached to, and the permissions boundary carried by every principal your own tagging marks as a change authority
continuouscli
$ aws organizations describe-organization
$ aws organizations list-roots
$ aws organizations list-policies --filter SERVICE_CONTROL_POLICY
$ aws organizations describe-policy --policy-id <CUSTOMER_AUTHORED_SCP_ID>
$ aws organizations list-targets-for-policy --policy-id <CUSTOMER_AUTHORED_SCP_ID>
$ aws iam get-account-authorization-details --filter User Role
partialEvery identifier the account has issued, with the date it was assigned and the AWS-generated unique id behind it; the workforce identifiers issued through IAM Identity Center and the external issuer each one came from; and the CloudTrail record of identifiers being deleted, which is the only dated evidence of a name becoming free to reuse
continuouscli
$ aws iam get-account-authorization-details --filter User Role Group
$ aws sso-admin list-instances
$ aws identitystore list-users --identity-store-id <IDENTITY_STORE_ID>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteUser --start-time <T0> --end-time <T1>
partialThe state of every credential in the account as of a stated moment, the enabled-or-disabled status of every workforce identity in the identity store, and the CloudTrail record of the revocations themselves — the five API calls that actually revoke standing access, each with the time it happened and the administrator who did it
continuouscli
$ aws iam generate-credential-report
$ aws iam get-credential-report
$ aws identitystore list-users --identity-store-id <IDENTITY_STORE_ID>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteUser --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteLoginProfile --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteAccessKey --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateAccessKey --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeactivateMFADevice --start-time <T0> --end-time <T1>
partialWhat a transferred individual can still reach, and what they have actually used: the reassignment events themselves from CloudTrail (group membership, attached policy, and Identity Center account-assignment changes, each with its time and the administrator who made it), the current Identity Center assignments per permission set, and IAM's service-last-accessed report for the identities involved — a per-principal view of which services the identity is permitted to reach and which of those it has never authenticated to. `iam-access-analyzer-unused-access` answers the same question estate-wide from findings; this answers it for the named principal a transfer is about.
continuouscli
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AddUserToGroup --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=RemoveUserFromGroup --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AttachUserPolicy --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DetachUserPolicy --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=CreateAccountAssignment --start-time <T0> --end-time <T1>
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteAccountAssignment --start-time <T0> --end-time <T1>
$ aws sso-admin list-permission-sets --instance-arn <INSTANCE_ARN>
$ aws sso-admin list-account-assignments --instance-arn <INSTANCE_ARN> --account-id <ACCOUNT_ID> --permission-set-arn <PERMISSION_SET_ARN>
$ aws iam generate-service-last-accessed-details --arn <PRINCIPAL_ARN> --granularity ACTION_LEVEL
$ aws iam get-service-last-accessed-details --job-id <JOB_ID>

Indicators in this theme

  • KSI-IAM-APM13 controls
    Adopting Passwordless Methods

    Secure passwordless methods are used for user authentication and authorization when feasible, otherwise strong passwords with phishing-resistant MFA is used.

  • KSI-IAM-ELP34 controls
    Ensuring Least Privilege

    Identity and access management measures are used and persistently reviewed to ensure each user or device can only access the resources they need.

  • KSI-IAM-JIT38 controls
    Authorizing Just-in-Time

    A least-privileged, role and attribute-based, and just-in-time security authorization model is used and persistently reviewed for all user and non-user accounts and services.

  • KSI-IAM-SNU7 controls
    Securing Non-User Authentication

    Appropriately secure authentication methods are used and persistently reviewed for non-user accounts and services.

  • KSI-IAM-SUS7 controls
    Responding to Suspicious Activity

    Accounts with privileged access are disabled or otherwise secured in response to suspicious activity.

No accounts — your progress ticks never leave this browser.