AWS for DevOps — MFA & Best Practices

3 minute read

2.4 — MFA & Best Practices

Multi-Factor Authentication (MFA) adds a second layer of security beyond a password. IAM best practices form the foundation of a secure AWS environment.

What Is MFA?

MFA requires two pieces of evidence to authenticate:

  1. Something you know — password
  2. Something you have — a time-based one-time password (TOTP) from a device
  Login Attempt
       │
       ▼
  Enter password ──► Correct? ──► Enter MFA code ──► Valid? ──► Access Granted
       │                   │              │                │
       NO                  │              NO               │
       ▼                   │              ▼                │
    DENIED                 │           DENIED              │
                           │                               │
                    Wrong password                   Wrong MFA code

MFA Device Types

Type Example Best For
Virtual MFA Google Authenticator, Authy Most users
Hardware MFA YubiKey, Gemalto token Root account, high-security
SMS MFA Text message code Not recommended (SIM swap attacks)

Enabling MFA for the Root Account (Console)

  1. Sign in as root → My Security Credentials
  2. Multi-factor authentication → Activate MFA
  3. Choose Virtual MFA device
  4. Scan the QR code with your authenticator app
  5. Enter two consecutive codes to verify
  6. Done — root account now requires MFA

Enabling MFA for an IAM User (CLI)

# Step 1 — Create a virtual MFA device
$ aws iam create-virtual-mfa-device \
    --virtual-mfa-device-name alice-mfa \
    --outfile /tmp/QRCode.png \
    --bootstrap-method QRCodePNG

# Step 2 — Scan the QR code, then enable with two consecutive codes
$ aws iam enable-mfa-device \
    --user-name alice \
    --serial-number arn:aws:iam::123456789012:mfa/alice-mfa \
    --authentication-code1 123456 \
    --authentication-code2 789012

Requiring MFA for Sensitive Actions

You can write a policy that denies all actions unless MFA is present:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyAllExceptListedIfNoMFA",
      "Effect": "Deny",
      "NotAction": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:ListMFADevices",
        "iam:ResyncMFADevice",
        "sts:GetSessionToken"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

This says: “Deny everything (except MFA setup actions) if MFA is not present on the session.”

The Principle of Least Privilege

Grant only the permissions required to perform a task — nothing more.

  ✗ Bad:   "Action": "*",   "Resource": "*"
  ✓ Good:  "Action": "s3:GetObject",  "Resource": "arn:aws:s3:::my-bucket/*"

How to implement it:

  1. Start with zero permissions — new users/roles have no access
  2. Add permissions as needed — one service at a time
  3. Use Access Advisor — check which permissions are actually used
  4. Review quarterly — remove unused permissions

Checking Last Accessed Information

# Generate a report
$ aws iam generate-service-last-accessed-details \
    --arn arn:aws:iam::123456789012:user/alice
{
    "JobId": "abc123"
}

# Get the report
$ aws iam get-service-last-accessed-details --job-id abc123
{
    "ServicesLastAccessed": [
        {
            "ServiceName": "Amazon S3",
            "LastAuthenticated": "2026-03-25T10:00:00Z",
            "TotalAuthenticatedEntities": 1
        },
        {
            "ServiceName": "Amazon EC2",
            "LastAuthenticated": null,
            "TotalAuthenticatedEntities": 0
        }
    ]
}

If Alice has EC2 permissions but has never used them → remove them.

IAM Best Practices Checklist

# Practice Priority
1 Enable MFA on root account Critical
2 Never use root for daily tasks Critical
3 Delete root access keys Critical
4 One IAM user per person High
5 Use groups for permissions High
6 Use roles for applications / services High
7 Apply least privilege High
8 Enable CloudTrail for audit High
9 Rotate access keys every 90 days Medium
10 Use IAM Access Analyzer Medium
11 Set a strong password policy Medium
12 Use permission boundaries for delegation Medium

Setting a Password Policy

$ aws iam update-account-password-policy \
    --minimum-password-length 14 \
    --require-symbols \
    --require-numbers \
    --require-uppercase-characters \
    --require-lowercase-characters \
    --max-password-age 90 \
    --password-reuse-prevention 12 \
    --allow-users-to-change-password

Access Keys Best Practices

# List access keys and their age
$ aws iam list-access-keys --user-name deploy-bot
{
    "AccessKeyMetadata": [
        {
            "UserName": "deploy-bot",
            "AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
            "Status": "Active",
            "CreateDate": "2025-12-01T00:00:00Z"
        }
    ]
}

# Create a new key, update your applications, then deactivate the old one
$ aws iam create-access-key --user-name deploy-bot
$ aws iam update-access-key \
    --user-name deploy-bot \
    --access-key-id AKIAIOSFODNN7EXAMPLE \
    --status Inactive

# After confirming everything works, delete the old key
$ aws iam delete-access-key \
    --user-name deploy-bot \
    --access-key-id AKIAIOSFODNN7EXAMPLE

IAM Credential Report

Generate a CSV report of all users and their credential status:

$ aws iam generate-credential-report
$ aws iam get-credential-report --query 'Content' --output text | base64 --decode > report.csv
$ head -1 report.csv
user,arn,user_creation_time,password_enabled,password_last_used,mfa_active,...

This report is essential for audits — it shows who has MFA, when passwords were last rotated, and which access keys are active.

Quick Reference

Task Command
Enable MFA aws iam enable-mfa-device --user-name NAME --serial-number ARN --authentication-code1 CODE --authentication-code2 CODE
List MFA devices aws iam list-mfa-devices --user-name NAME
Set password policy aws iam update-account-password-policy --minimum-password-length 14 ...
Generate credential report aws iam generate-credential-report
Check last accessed aws iam generate-service-last-accessed-details --arn ARN
Rotate access key Create new → update apps → deactivate old → delete old

Next → AWS Organizations

Updated: