Most AWS accounts start with a handful of IAM users. Then “real life” happens: people join, phones get replaced, tokens get lost, and suddenly MFA becomes the thing everyone needs help with—right when they’re locked out.
The goal of this setup is simple:
- Users can enroll, view, resync, and replace their own MFA device
- Users are blocked from doing anything else until MFA is present (except the minimum actions needed to enroll/authenticate)
- Admins keep a safe “break-glass” path for recovery
This guide shows a clean, least-privilege way to do that for IAM users.
Before you start: choose the right identity model
If you’re still using IAM users for humans, the steps below work well.
If you’re starting fresh (or can modernize), consider IAM Identity Center (AWS SSO). It supports centrally managed MFA when using supported identity sources, and it’s usually the preferred long-term direction.
This article focuses on IAM users because that’s where “self-manage MFA” policies matter most.
Step 0: Protect the root user
Enable MFA for the root user and lock down how it’s used.
Root should not be used for daily operations—treat it as break-glass only.
Step 1: Create an IAM group for human users
In the IAM console:
- Go to User groups → Create group
- Name it something like
HumanUsersorMFARequiredUsers - Don’t attach policies yet
Any human IAM user goes into this group and inherits the MFA onboarding + enforcement behavior.
Step 2: Attach a “self-manage MFA” policy (customer managed)
Create a customer managed policy that lets users create and manage their own MFA, but not anyone else’s.
Use this as a starting point:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowListVirtualMfaDevices", "Effect": "Allow", "Action": "iam:ListVirtualMFADevices", "Resource": "*" }, { "Sid": "AllowCreateVirtualMfaDevice", "Effect": "Allow", "Action": "iam:CreateVirtualMFADevice", "Resource": "arn:aws:iam::*:mfa/*" }, { "Sid": "AllowManageOwnMfaForOwnUser", "Effect": "Allow", "Action": [ "iam:DeactivateMFADevice", "iam:EnableMFADevice", "iam:GetUser", "iam:ListMFADevices", "iam:ResyncMFADevice" ], "Resource": "arn:aws:iam::*:user/${aws:username}" } ] } |
Attach it to your HumanUsers group.
Why it’s safe: the Resource scoping with ${aws:username} prevents users from touching anyone else’s MFA devices.
Step 3: Enforce MFA with an explicit deny (but still allow enrollment)
Now add the enforcement layer: deny all actions unless MFA is present, except the handful of actions required to set up MFA and obtain MFA-backed sessions.
Attach a second customer managed policy to the same group:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyAllExceptMfaSetupIfNoMfa", "Effect": "Deny", "NotAction": [ "iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "iam:GetUser", "iam:ListMFADevices", "iam:ListVirtualMFADevices", "iam:ResyncMFADevice", "sts:GetSessionToken" ], "Resource": "*", "Condition": { "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" } } } ] } |
Why sts:GetSessionToken is in the allowlist
For the AWS CLI, users must call sts:GetSessionToken with an MFA code to get temporary credentials that are “MFA-authenticated.” Then they use those credentials for actions protected by aws:MultiFactorAuthPresent.
Step 4: Create users and test the flow
For each new IAM user:
- Create the user (console or CLI)
- Add them to the
HumanUsersgroup - Give them a login method (console password and/or access keys)
What the user experience looks like
- Without MFA, the user can only access the minimum needed to enroll MFA (and get session tokens)
- After enrolling MFA, normal access works (subject to their other permissions)
Step 5: Document a simple CLI workflow for users
If your users use the CLI, give them a short workflow:
- List MFA devices:
|
1 2 |
aws iam list-mfa-devices |
- Get a session token (replace
SERIAL+CODE):
|
1 2 3 4 |
aws sts get-session-token \ --serial-number arn:aws:iam::123456789012:mfa/USERNAME \ --token-code 123456 |
- Use the returned temporary credentials (env vars or a profile)
Step 6: Add a break-glass plan
Even with self-management, you need a recovery path:
- Keep one or two admin identities that are tightly controlled
- Store backup MFA devices for admins using a secure process
- Audit and alert on usage of break-glass identities
Common pitfalls
- Locking users out before enrollment: If you enforce MFA but forget to allow
iam:CreateVirtualMFADevice/iam:EnableMFADevice/sts:GetSessionToken, users can’t bootstrap. - CLI confusion: MFA conditions check whether the request is made with MFA-backed session credentials, not whether the user simply has an MFA device configured.
- Over-permissioning: Keep self-management scoped to
arn:aws:iam::*:user/${aws:username}to avoid cross-user tampering.
Wrap-up
With two small policies—one for self-management and one for enforcement—you can let users handle their own MFA lifecycle while keeping the account protected.
If you’re enforcing MFA alongside role assumption (recommended), permission boundaries, or Organizations SCPs, tailor the allowlist so onboarding stays smooth without opening unintended access.




