AWS IAM User Management with Terraform
We will create a CSV file with multiple users’ data, then upload it to Terraform. Then Terraform will create users for us. Then we will create multiple IAM groups and assign them to particular groups dynamically. Then enable console access to IAM users. S3 remote backend.

Overview
Reads
users.csvfrom theCode/folder and decodes it withcsvdecode.Creates IAM users (
aws_iam_user) for each CSV row.Creates a login profile (
aws_iam_user_login_profile) for each user to allow console access (with forced password reset).Tags each user with metadata (DisplayName, Department, JobTitle) and additional attributes added in this iteration:
EmailandPhone.
Prerequisites:
- Create a CSV file with user data with the fields first_name, last_name, department, and job_title.
CSV to Map
To do this, we have to use a function in Terraform called csvdecode. csvdecode returns a list, but Terraform for_each requires a map or set. So we convert the list into a map using a for expression.
locals{
users = csvdecode(file(users.csv)) # users will be a map after operation
}
Create IAM Users
resource "aws_iam_user" "users" {
for_each = {for user in local.users: "${user.first_name} ${user.last_name}" => user} # this is a map
name = lower("${substr(each.value.first_name, 0, 1)}${each.value.last_name}")
path = "/users/"
tags = {
"DisplayName" = "${each.value.first_name} ${each.value.last_name}"
"Department" = each.value.department
"JobTitle" = each.value.job_title
"Email" = lookup(each.value, "email", "")
"Phone" = lookup(each.value, "phone", "")
}
}
The aws_iam_user_login_profile resource in Terraform sets up a password for an IAM user and forces them to reset it on first login. This is useful for onboarding new users securely, ensuring they create their own strong password upon initial access. The lifecycle block prevents Terraform from managing password changes, so manual resets outside Terraform are possible.
resource "aws_iam_user_login_profile" "users" {
for_each = aws_iam_user.users
user = each.value.name
password_reset_required = true
password_length = 16
lifecycle {
ignore_changes = [
password_length,
password_reset_required,
]
}
}
output "user_passwords" {
value = {
for user, profile in aws_iam_user_login_profile.users :
user => "Password created - user must reset on first login"
}
sensitive = true
}
This code creates login profiles for all IAM users defined in your configuration, sets a temporary password, and outputs a message indicating that each user must reset their password on first login. The sensitive = true flag hides the output in logs for security.
Creating an Output
We will be outputting the first name and last name of the created user.
output "user_names" {
value = [for user in local.users: "${user.first_name} ${user.last_name}"]
}
Groups creation
We will be creating three groups for the user account.
resource "aws_iam_group" "education" {
name = "Education"
path = "/groups/"
}
resource "aws_iam_group" "managers" {
name = "Managers"
path = "/groups/"
}
resource "aws_iam_group" "engineers" {
name = "Engineers"
path = "/groups/"
}
Then we will be dynamically adding the users to each group.
# Add users to the Education group
resource "aws_iam_group_membership" "education_members" {
name = "education-group-membership"
group = aws_iam_group.education.name
users = [
for user in aws_iam_user.users : user.name if user.tags.Department == "Education"
]
}
# Add users to the Managers group
resource "aws_iam_group_membership" "managers_members" {
name = "managers-group-membership"
group = aws_iam_group.managers.name
users = [
for user in aws_iam_user.users : user.name if contains(keys(user.tags), "JobTitle") && can(regex("Manager|CEO", user.tags.JobTitle))
]
}
# Add users to the Engineers group
resource "aws_iam_group_membership" "engineers_members" {
name = "engineers-group-membership"
group = aws_iam_group.engineers.name
users = [
for user in aws_iam_user.users : user.name if user.tags.Department == "Engineering"
]
}
Policy for MFA
We will create an IAM policy that enforces MFA by denying all actions for users who have not configured MFA. Terraform cannot manage the virtual MFA device itself, but it can enforce MFA by attaching this policy.
This policy will deny all actions until MFA is enabled. Apply with caution.
resource "aws_iam_policy" "mfa_required_policy" {
name = "mfa-required-policy"
path = "/"
description = "Require MFA for all actions except MFA setup"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "DenyAllExceptMFA"
Effect = "Deny"
Action = "*"
Resource = "*"
Condition = {
Bool = {
"aws:MultiFactorAuthPresent" = "false"
}
}
},
{
Sid = "AllowMFASetup"
Effect = "Allow"
Action = [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ResyncMFADevice",
"iam:DeactivateMFADevice",
"iam:DeleteVirtualMFADevice"
]
Resource = "*"
}
]
})
}
Attach this policy to users in each group.
resource "aws_iam_group_policy_attachment" "education_policy_attach" {
group = aws_iam_group.education.name
policy_arn = aws_iam_policy.mfa_required_policy.arn
}
resource "aws_iam_group_policy_attachment" "managers_policy_attach" {
group = aws_iam_group.managers.name
policy_arn = aws_iam_policy.mfa_required_policy.arn
}
resource "aws_iam_group_policy_attachment" "engineers_policy_attach" {
group = aws_iam_group.engineers.name
policy_arn = aws_iam_policy.mfa_required_policy.arn
}
That’s it for this project.
You can find the code here:
Arigato!