# AWS Policy Roles MFA

A common and excellent security practice! We'll create an IAM Group with two policies:

1. **`ReadOnlyAccess`**: An AWS managed policy providing read-only access to most AWS services.
2. **`ForceMFA`**: A custom policy that denies most actions unless MFA is enabled, but crucially allows users to manage their own credentials (password, MFA devices) initially.

When a new IAM user is created, they will be:

* Added to this group.
* Required to change their initial password upon first login.
* Forced by the `ForceMFA` policy to set up MFA before they can do much else (beyond managing their own credentials).

Here's how to set this up step-by-step:

**Step 1: Create the Custom MFA Enforcement Policy (`ForceMFA`)**

This policy is the most critical part. It needs to:

* Allow users to manage their own password and MFA devices.
* Allow users to view basic account information necessary for initial setup.
* Deny all other actions if MFA is not present.

1. Navigate to the IAM console in AWS.
2. Go to **Policies** and click **Create policy**.
3. Select the **JSON** tab and paste the following policy. Replace `<YOUR_ACCOUNT_ID>` with your actual AWS Account ID.

````json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "AllowUserToManageTheirOwnCredentialsAndMFA",
			"Effect": "Allow",
			"Action": [
				"iam:GetAccountEmailAddress",
				"iam:ListAccessKeys",
				"iam:ListSigningCertificates",
				"iam:GetLoginProfile",
				"iam:UpdateAccountEmailAddress",
				"iam:ChangePassword",
				"iam:CreateVirtualMFADevice",
				"iam:DeleteVirtualMFADevice",
				"iam:EnableMFADevice",
				"iam:DeactivateMFADevice",
				"iam:GetUser",
				"iam:ListMFADevices",
				"iam:ListVirtualMFADevices",
				"iam:CreateVirtualMFADevice",
				"iam:ResyncMFADevice",
				"iam:GetAccountPasswordPolicy"
			],
			"Resource": [
				"arn:aws-cn:iam::*:user/${aws:username}",
				"arn:aws-cn:iam::*:mfa/*",
				"arn:aws-cn:iam::*:sms-mfa/*"
			]
		},
		{
			"Sid": "AllowListUsersForConsoleExperience",
			"Effect": "Allow",
			"Action": "iam:ListUsers",
			"Resource": "*"
		},
		{
			"Sid": "DenyAllOtherActionsIfNoMFA",
			"Effect": "Deny",
			"NotAction": [
				"iam:ChangePassword",
				"iam:CreateVirtualMFADevice",
				"iam:DeleteVirtualMFADevice",
				"iam:EnableMFADevice",
				"iam:DeactivateMFADevice",
				"iam:GetUser",
				"iam:ListMFADevices",
				"iam:ResyncMFADevice",
				"iam:GetAccountPasswordPolicy",
				"iam:ListUsers",
				"sts:AssumeRole"
			],
			"Resource": "*",
			"Condition": {
				"BoolIfExists": {
					"aws:MultiFactorAuthPresent": "false"
				}
			}
		}
	]
}
    ```

    **Explanation of the `ForceMFA` policy:**
    *   **`AllowUserToManageTheirOwnCredentialsAndMFA`**: Allows users to manage their *own* password and MFA devices using `aws:username` path variable.
    *   **`AllowListUsersForConsoleExperience`**: Allows `iam:ListUsers` which is sometimes needed for the AWS Management Console to render correctly after login, even before MFA is set up. This is a broad permission but necessary for a smooth console experience.
    *   **`DenyAllOtherActionsIfNoMFA`**: This is the core enforcement.
        *   `Effect: Deny`: Explicitly denies actions.
        *   `NotAction`: Excludes the actions listed (which are the self-management actions from the first statement and `sts:AssumeRole` which is often needed and has its own MFA controls).
        *   `Resource: "*"`: Applies to all resources.
        *   `Condition: { "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" } }`: This is the key. It denies the action if the `aws:MultiFactorAuthPresent` key in the request context is `false` (meaning MFA was not used) or if the key doesn't exist (which can happen for some API calls).

4.  Click **Next: Tags**. Add any tags if you wish (optional).
5.  Click **Next: Review**.
6.  Give the policy a **Name**, e.g., `ForceMFA`.
7.  Add a **Description**, e.g., "Enforces MFA for users, allowing self-management of credentials and MFA devices."
8.  Click **Create policy**.

**Step 2: Create the IAM Group**

1.  In the IAM console, go to **User groups** and click **Create group**.
2.  Enter a **User group name**, e.g., `ReadOnlyUsersWithMFA`.
3.  In the "Add users to the group" section, you can skip adding users for now if you're just setting up the group.
4.  In the "Attach permissions policies" section:
    *   Search for and select the AWS managed policy: `ReadOnlyAccess`.
    *   Search for and select the custom policy you just created: `ForceMFA`.
5.  Click **Create group**.

**Step 3: Create a New IAM User (and add to the group)**

1.  In the IAM console, go to **Users** and click **Add users**.
2.  Enter a **User name**, e.g., `test-readonly-user`.
3.  Select **AWS credential type**: Choose **Password - AWS Management Console access**.
4.  **Console password**: Select **Autogenerated password** or **Custom password**.
5.  **IMPORTANT**: Check the box for **User must create a new password at next sign-in**.
6.  Click **Next: Permissions**.
7.  Select **Add user to group**.
8.  Check the box next to the group you created: `ReadOnlyUsersWithMFA`.
9.  Click **Next: Tags**. Add tags if desired (optional).
10. Click **Next: Review**.
11. Review the details and click **Create user**.
12. **Crucially, download the .csv file or copy the login credentials (especially the one-time password and login link).**

**Step 4: User's First Login Experience**

1.  The user receives their one-time password and a special login link (e.g., `https://<YOUR_ACCOUNT_ID>.signin.aws.amazon.com/console`).
2.  They navigate to the link, enter their IAM user name and the one-time password.
3.  They will be immediately prompted to change their password. They must set a new, strong password.
4.  After successfully changing the password, they will be logged into the AWS Management Console.
5.  Now, if they try to access any AWS service (e.g., go to EC2 dashboard), the `ForceMFA` policy will kick in. Most actions will be denied with a message similar to "You are not authorized to perform this operation."
6.  They need to set up MFA. They can do this by:
    *   Clicking their username in the top right corner.
    *   Selecting **Security credentials**.
    *   In the "Multi-factor authentication (MFA)" section, clicking **Assign MFA device**.
    *   Following the prompts to set up a virtual MFA device (like Google Authenticator or Authy) or a hardware MFA key.
7.  Once MFA is successfully configured, they must log out and log back in.
8.  When logging back in, after entering their username and new password, they will be prompted for their MFA code.
9.  After successful MFA authentication, they will have `ReadOnlyAccess` to AWS services as defined by the group's policies.

**How to do this with AWS CLI:**

```bash
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# 1. Create the ForceMFA policy
POLICY_DOCUMENT='{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowUserToManageTheirOwnCredentialsAndMFA",
            "Effect": "Allow",
            "Action": [
                "iam:ChangePassword",
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:DeactivateMFADevice",
                "iam:GetUser",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice",
                "iam:GetAccountPasswordPolicy"
            ],
            "Resource": [
                "arn:aws:iam::'${AWS_ACCOUNT_ID}':user/${aws:username}",
                "arn:aws:iam::'${AWS_ACCOUNT_ID}':mfa/${aws:username}/*",
                "arn:aws:iam::'${AWS_ACCOUNT_ID}':sms-mfa/${aws:username}"
            ]
        },
        {
            "Sid": "AllowListUsersForConsoleExperience",
            "Effect": "Allow",
            "Action": "iam:ListUsers",
            "Resource": "*"
        },
        {
            "Sid": "DenyAllOtherActionsIfNoMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:ChangePassword",
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:DeactivateMFADevice",
                "iam:GetUser",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice",
                "iam:GetAccountPasswordPolicy",
                "iam:ListUsers",
                "sts:AssumeRole"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}'

MFA_POLICY_ARN=$(aws iam create-policy \
    --policy-name ForceMFA \
    --policy-document "$POLICY_DOCUMENT" \
    --description "Enforces MFA for users, allowing self-management of credentials and MFA devices." \
    --query 'Policy.Arn' --output text)

echo "ForceMFA Policy ARN: $MFA_POLICY_ARN"

# 2. Create the IAM Group
GROUP_NAME="ReadOnlyUsersWithMFA"
aws iam create-group --group-name "$GROUP_NAME"

# Attach ReadOnlyAccess (AWS Managed Policy)
aws iam attach-group-policy \
    --group-name "$GROUP_NAME" \
    --policy-arn "arn:aws:iam::aws:policy/ReadOnlyAccess"

# Attach ForceMFA (Customer Managed Policy)
aws iam attach-group-policy \
    --group-name "$GROUP_NAME" \
    --policy-arn "$MFA_POLICY_ARN"

echo "Group $GROUP_NAME created and policies attached."

# 3. Create a new IAM user and add to the group
USER_NAME="cli-readonly-user"
INITIAL_PASSWORD="YourSecureP@sswOrd123!" # Change this!

aws iam create-user --user-name "$USER_NAME"

# Create login profile with password reset required
aws iam create-login-profile \
    --user-name "$USER_NAME" \
    --password "$INITIAL_PASSWORD" \
    --password-reset-required

aws iam add-user-to-group \
    --user-name "$USER_NAME" \
    --group-name "$GROUP_NAME"

echo "User $USER_NAME created, added to group $GROUP_NAME, and requires password reset."
echo "Login URL: https://${AWS_ACCOUNT_ID}.signin.aws.amazon.com/console"
echo "Username: $USER_NAME"
echo "Initial Password: $INITIAL_PASSWORD"
````

**Resources and Further Reading:**

* **IAM Policies:** <https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html>
* **AWS Managed Policies:** <https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html> (Specifically `ReadOnlyAccess`)
* **IAM Policy Elements: `Condition`:** <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html>
* **IAM Condition Keys for MFA:** `aws:MultiFactorAuthPresent` <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-multifactorauthpresent>
* **Creating an IAM User:** <https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html>
* **Requiring Users to Change Password:** Look for "Requiring an IAM user to change their password on first sign-in" in AWS docs.
* **Policy Variable `aws:username`**: <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#policy-vars-infotouse>

This setup ensures that any user added to the `ReadOnlyUsersWithMFA` group will have a consistent, secure onboarding process, enforcing both initial password change and subsequent MFA usage for read-only access. Remember to test thoroughly!
