Use it with IAM Identity Center¶
The problem¶
Your humans sign in through IAM Identity Center, and you want the reader role assumable from an SSO session — without weakening the certifier role, which should keep a hard MFA gate.
The trap, first¶
[!WARNING] Pointing
reader_principal_arnsat an Identity Center permission-set role while leavingreader_require_mfa = trueproduces a role no human can assume.
AWS documents aws:MultiFactorAuthPresent as:
not present for federated identities or requests made using access keys to sign AWS CLI, AWS API, or AWS SDK requests
An Identity Center session is a federated identity, so the key is absent, not
false. A trust policy Allow carrying Bool: aws:MultiFactorAuthPresent =
true never matches, and every assume attempt is denied — with no clue as to
why.
Setting it false is not a weakening. Identity Center enforces MFA at
sign-in; it just isn't expressible as an IAM condition. See
MFA and federated sessions.
Steps¶
1. Create a dedicated permission set¶
Don't reuse an admin permission set. The reader list should be as short as the
job allows, and reusing admin makes it "everyone with AdministratorAccess".
Create a permission set with no AWS managed policies attached — the reader role gets its capability from the KMS key policy, not from the permission set.
2. Find the permission-set role ARN¶
Identity Center provisions a role into each assigned account under a reserved path:
aws iam list-roles \
--path-prefix /aws-reserved/sso.amazonaws.com/ \
--query 'Roles[?starts_with(RoleName, `AWSReservedSSO_ConfidentialIntakeReader`)].Arn' \
--output text
arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/eu-west-2/AWSReservedSSO_ConfidentialIntakeReader_a1b2c3d4e5f6a7b8
The trailing hash is stable across ordinary permission-set updates. It changes if the permission set is deleted and recreated, which is the one case where you'll need to re-apply.
3. Configure the module asymmetrically¶
module "identity" {
source = "gitlab.com/phpboyscout/encryption-kms/aws"
version = "~> 0.1"
name = "confidential-intake-v1"
# Federated: MFA enforced at sign-in, not observable in IAM.
reader_require_mfa = false
reader_principal_arns = [
"arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/eu-west-2/AWSReservedSSO_ConfidentialIntakeReader_a1b2c3d4e5f6a7b8",
]
# Ordinary IAM principal: MFA is observable, so require it.
certifier_require_mfa = true
certifier_principal_arns = [
"arn:aws:iam::111122223333:role/break-glass",
]
key_administrator_arns = ["arn:aws:iam::111122223333:role/platform-admin"]
automation_role_arn = "arn:aws:iam::111122223333:role/tofu-automation"
}
The two flags are separate precisely so this shape is possible. A single module-wide flag would force the certifier down to whatever the reader needed.
4. Confirm a real session can assume the role¶
From an aws sso login session using that permission set:
aws sts assume-role \
--role-arn "$(tofu output -raw reader_role_arn)" \
--role-session-name check \
--query 'AssumedRoleUser.Arn' --output text
An AccessDenied here almost always means reader_require_mfa is still true.
If you are not using Identity Center¶
Leave both flags at their default true and use ordinary IAM role or user
ARNs. That path is the minimal example,
and it gets a hard IAM-level MFA condition on both roles.
See also¶
- Roles and trust — why the certifier is the higher privilege of the two.
- IAM permissions reference — exactly what each role is granted.