Enforcing MFA for AWS IAM Users with CloudFormation/ Terraform

Varun Kumar Manik
5 min readJul 5, 2024

--

In today’s cloud-driven world, securing access to your AWS resources is more critical than ever. One effective way to enhance security is by enforcing Multi-Factor Authentication (MFA) for all IAM users. This additional layer of protection significantly reduces the risk of unauthorized access. In this guide, we will walk you through setting up MFA enforcement for IAM users using AWS CloudFormation and Terraform.

Objectives

  1. Create an IAM group that enforces MFA for all its members.
  2. Attach a policy to this group that requires users to enable MFA before they can access AWS resources.
  3. Allow users to manage their own IAM settings (passwords, access keys, signing certificates, SSH public keys, and MFA devices) while enforcing MFA for all other actions.
  4. Ensure seamless MFA setup so that users can easily enable MFA upon their next login.

Prerequisites

  • Basic knowledge of AWS IAM and infrastructure as code (IaC) tools.
  • AWS CLI installed and configured on your local machine.
  • Terraform installed on your local machine (for the Terraform section).
  • Necessary permissions to create IAM roles, policies, and groups in your AWS account.

Step-by-Step Guide

Step 1: Define the CloudFormation Template

First, we need to define our CloudFormation template. This template will create an IAM group and attach a policy that enforces MFA while allowing users to manage their own IAM settings.

GITHUB: https://github.com/manikcloud/aws-mfaEnforce-tutorial


AWSTemplateFormatVersion: '2010-09-09'
Description: Enforce MFA for all users in a specific IAM group and allow users to manage their own IAM settings.

Resources:
MFAEnforcedGroup:
Type: "AWS::IAM::Group"
Properties:
GroupName: "MFAEnforcedGroup"

EnforceMFAPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "EnforceMFA"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "AllowViewAccountInfo"
Effect: "Allow"
Action:
- "iam:GetAccountPasswordPolicy"
- "iam:ListVirtualMFADevices"
Resource: "*"
- Sid: "AllowManageOwnPasswords"
Effect: "Allow"
Action:
- "iam:ChangePassword"
- "iam:GetUser"
Resource: "arn:aws:iam::*:user/${aws:username}"
- Sid: "AllowManageOwnAccessKeys"
Effect: "Allow"
Action:
- "iam:CreateAccessKey"
- "iam:DeleteAccessKey"
- "iam:ListAccessKeys"
- "iam:UpdateAccessKey"
- "iam:GetAccessKeyLastUsed"
Resource: "arn:aws:iam::*:user/${aws:username}"
- Sid: "AllowManageOwnSigningCertificates"
Effect: "Allow"
Action:
- "iam:DeleteSigningCertificate"
- "iam:ListSigningCertificates"
- "iam:UpdateSigningCertificates"
- "iam:UploadSigningCertificates"
Resource: "arn:aws:iam::*:user/${aws:username}"
- Sid: "AllowManageOwnSSHPublicKeys"
Effect: "Allow"
Action:
- "iam:DeleteSSHPublicKey"
- "iam:GetSSHPublicKey"
- "iam:ListSSHPublicKeys"
- "iam:UpdateSSHPublicKey"
- "iam:UploadSSHPublicKey"
Resource: "arn:aws:iam::*:user/${aws:username}"
- Sid: "AllowManageOwnGitCredentials"
Effect: "Allow"
Action:
- "iam:CreateServiceSpecificCredential"
- "iam:DeleteServiceSpecificCredential"
- "iam:ListServiceSpecificCredentials"
- "iam:ResetServiceSpecificCredential"
- "iam:UpdateServiceSpecificCredential"
Resource: "arn:aws:iam::*:user/${aws:username}"
- Sid: "AllowManageOwnVirtualMFADevice"
Effect: "Allow"
Action:
- "iam:CreateVirtualMFADevice"
Resource: "arn:aws:iam::*:mfa/*"
- Sid: "AllowManageOwnUserMFA"
Effect: "Allow"
Action:
- "iam:DeactivateMFADevice"
- "iam:EnableMFADevice"
- "iam:ListMFADevices"
- "iam:ResyncMFADevice"
Resource: "arn:aws:iam::*:user/${aws:username}"
- Sid: "DenyAllExceptListedIfNoMFA"
Effect: "Deny"
NotAction:
- "iam:CreateVirtualMFADevice"
- "iam:EnableMFADevice"
- "iam:GetUser"
- "iam:GetMFADevice"
- "iam:ListMFADevices"
- "iam:ListVirtualMFADevices"
- "iam:ResyncMFADevice"
- "sts:GetSessionToken"
Resource: "*"
Condition:
BoolIfExists:
"aws:MultiFactorAuthPresent": "false"
Groups:
- !Ref MFAEnforcedGroup

Outputs:
MFAEnforcedGroupName:
Description: "The name of the IAM group where MFA is enforced"
Value: !Ref MFAEnforcedGroup

Step 2: Deploy the CloudFormation Stack

Save the above YAML content to a file named enforce-mfa.yaml.

Use the AWS CLI to deploy the CloudFormation stack:

aws cloudformation create-stack --stack-name EnforceMFAStack --template-body file://enforce-mfa.yaml --capabilities CAPABILITY_NAMED_IAM

Step 1: Define the Terraform Configuration

Create a file named main.tf with the following content:

GITHUB: https://github.com/manikcloud/aws-mfaEnforce-tutorial

provider "aws" {
region = "us-west-2" # Specify your AWS region
}

resource "aws_iam_group" "mfa_enforced_group" {
name = "MFAEnforcedGroup"
}

resource "aws_iam_policy" "enforce_mfa_policy" {
name = "EnforceMFA"
path = "/"
description = "Policy to enforce MFA"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowViewAccountInfo"
Effect = "Allow"
Action = [
"iam:GetAccountPasswordPolicy",
"iam:ListVirtualMFADevices"
]
Resource = "*"
},
{
Sid = "AllowManageOwnPasswords"
Effect = "Allow"
Action = [
"iam:ChangePassword",
"iam:GetUser"
]
Resource = "arn:aws:iam::*:user/\${aws:username}"
},
{
Sid = "AllowManageOwnAccessKeys"
Effect = "Allow"
Action = [
"iam:CreateAccessKey",
"iam:DeleteAccessKey",
"iam:ListAccessKeys",
"iam:UpdateAccessKey",
"iam:GetAccessKeyLastUsed"
]
Resource = "arn:aws:iam::*:user/\${aws:username}"
},
{
Sid = "AllowManageOwnSigningCertificates"
Effect = "Allow"
Action = [
"iam:DeleteSigningCertificate",
"iam:ListSigningCertificates",
"iam:UpdateSigningCertificate",
"iam:UploadSigningCertificate"
]
Resource = "arn:aws:iam::*:user/\${aws:username}"
},
{
Sid = "AllowManageOwnSSHPublicKeys"
Effect = "Allow"
Action = [
"iam:DeleteSSHPublicKey",
"iam:GetSSHPublicKey",
"iam:ListSSHPublicKeys",
"iam:UpdateSSHPublicKey",
"iam:UploadSSHPublicKey"
]
Resource = "arn:aws:iam::*:user/\${aws:username}"
},
{
Sid = "AllowManageOwnGitCredentials"
Effect = "Allow"
Action = [
"iam:CreateServiceSpecificCredential",
"iam:DeleteServiceSpecificCredential",
"iam:ListServiceSpecificCredentials",
"iam:ResetServiceSpecificCredential",
"iam:UpdateServiceSpecificCredential"
]
Resource = "arn:aws:iam::*:user/\${aws:username}"
},
{
Sid = "AllowManageOwnVirtualMFADevice"
Effect = "Allow"
Action = [
"iam:CreateVirtualMFADevice"
]
Resource = "arn:aws:iam::*:mfa/*"
},
{
Sid = "AllowManageOwnUserMFA"
Effect = "Allow"
Action = [
"iam:DeactivateMFADevice",
"iam:EnableMFADevice",
"iam:ListMFADevices",
"iam:ResyncMFADevice"
]
Resource = "arn:aws:iam::*:user/\${aws:username}"
},
{
Sid = "DenyAllExceptListedIfNoMFA"
Effect = "Deny"
NotAction = [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:GetUser",
"iam:GetMFADevice",
"iam:ListMFADevices",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice",
"sts:GetSessionToken"
]
Resource = "*"
Condition = {
BoolIfExists = {
"aws:MultiFactorAuthPresent" = "false"
}
}
}
]
})
}

resource "aws_iam_group_policy_attachment" "attach_mfa_policy" {
group = aws_iam_group.mfa_enforced_group.name
policy_arn = aws_iam_policy.enforce_mfa_policy.arn
}

Step 2: Initialize Terraform

Run the following commands to initialize Terraform and apply the configuration:

terraform init
terraform apply

Step 3: Add Users to the MFAEnforcedGroup

After deploying the stack, add users to the MFAEnforcedGroup using the AWS CLI:

aws iam add-user-to-group --group-name MFAEnforcedGroup --user-name <user1>
aws iam add-user-to-group --group-name MFAEnforcedGroup --user-name <user2>
# Repeat for other users

Step 4: Verify MFA Enforcement

Users in the MFAEnforcedGroup will need to enable MFA to access AWS resources. Here's how users can set up MFA:

  1. Log in to the AWS Management Console.
  2. Navigate to the IAM section.
  3. Go to the “Users” section and select your username.
  4. Click on the “Security credentials” tab.
  5. Scroll down to the “Assigned MFA device” section and click “Manage”.
  6. Follow the instructions to assign a virtual MFA device (e.g., using Google Authenticator).

Step 5: Test After Enabling MFA

Once MFA is enabled, users can log out and log back in to verify they can access AWS resources as usual.

Conclusion

By following this guide, you will enhance the security of your AWS environment by enforcing MFA for all IAM users. Using AWS CloudFormation and Terraform, you can automate this process, ensuring consistency and ease of management.

This approach not only protects your AWS resources with an additional layer of security but also empowers users to manage their own IAM settings without compromising security. Implementing MFA is a best practice that helps safeguard your infrastructure against unauthorized access and potential security breaches.

Further Reading

By implementing these steps, you can help ensure your AWS environment is more secure and compliant with best practices.

--

--

Varun Kumar Manik
Varun Kumar Manik

Written by Varun Kumar Manik

AWS APN Ambassador | SME of DevOps DevSecOps | Cloud Architect & Trainer | Blogger | Youtuber |Chef

No responses yet