DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Managing AWS IAM With Terraform: Part 1

Managing AWS IAM With Terraform: Part 1

Covering the basics of managing AWS IAM with Terraform and learn how to delete the Root/User Access key, enforce MFA, customize password policy, and more.

Tiexin Guo user avatar by
Tiexin Guo
·
Oct. 11, 22 · Tutorial
Like (1)
Save
Tweet
Share
4.13K Views

Join the DZone community and get the full member experience.

Join For Free

In my previous article, AWS IAM Security Best Practices, we covered a bunch of theoretical best practices on AWS IAM. In this tutorial, we will cover the basics of managing AWS IAM using Terraform.

Side note: this article assumes you already understand what Terraform is and know the basics of it. If not, start with a Terraform official tutorial.

We'll see: 

  • Why and how to delete the Root user access key
  • How to create an Admin Group/User
  • How to enforce MFA with Customer-Managed Policy and Policy Condition
  • How to customize password policy

1. Delete Access Keys of the Root User

Let's start by breaking the rules. How? By using the console. Why? Because we need to use the AWS Management Console to delete access keys for the root user.

  • Use your AWS account email address and password to sign in to the AWS Management Console as the AWS account root user
  • Choose your account name in the navigation bar, and then choose "Security Credentials"
  • Under the "Access keys for CLI, SDK, and API Access" section, find the access key, and then, under the "Actions" column, choose "Delete"
Delete Root User Access Key
The access key for your AWS account root user gives full access to all your resources for all AWS services. You cannot reduce the permissions associated with your AWS account root user access key.

For most day-to-day operations, you don't need to use the root user. Admin users are more than enough, which is why you should lock it away by deleting the Access key.  

We should try to avoid the usage of root users unless we absolutely have to. One such corner case would be if you had only one administrator user and that user accidentally removed admin permissions from themselves.  In that case, you'd have to log in with your root user and restore their permissions.

Now, let's see how to create admin users using Terraform.

2. Create Admin Group/User

Prepare a file admin_user_group.tf with the following content (you can get all the code from this tutorial).

 
resource "aws_iam_group" "administrators" {
  name = "Administrators"
  path = "/"
}

data "aws_iam_policy" "administrator_access" {
  name = "AdministratorAccess"
}

resource "aws_iam_group_policy_attachment" "administrators" {
  group      = aws_iam_group.administrators.name
  policy_arn = data.aws_iam_policy.administrator_access.arn
}

resource "aws_iam_user" "administrator" {
  name = "Administrator"
}

resource "aws_iam_user_group_membership" "devstream" {
  user   = aws_iam_user.administrator.name
  groups = [aws_iam_group.administrators.name]
}

resource "aws_iam_user_login_profile" "administrator" {
  user                    = aws_iam_user.administrator.name
  password_reset_required = true
}

output "password" {
  value     = aws_iam_user_login_profile.administrator.password
  sensitive = true
}


In the code block above, we:

  • Create a group intended for administrators
  • Read the ARN of the "AdministratorAccess," which is an AWS-managed policy
  • Attach the "AdministratorAccess" policy to the group
  • Create an admin user
  • Add that user to the admin group
  • Enable console login for that admin user
  • Add the initial password as a sensitive output

If we apply it:

 
terraform init
terraform apply
terraform output password


We will create all those resources and print out the initial password.

Do I Need to Use Groups?

Short answer: yes.

Well, technically, if you never need more than one admin user across your AWS account, you don't have to create an admin group and then put a single user into that group.

In the real world, you most likely would have a group of admins instead of one, so, the easier way to manage access for all admins is to create groups. Even if you have only one admin user at the moment, you need to bear in mind that your company, team, and project are subject to growth (maybe quicker than you'd imagine), and although using a group to manage merely one user at the moment can seem redundant, it's a small price to pay to be a bit more future-proof.

The same principle applies to managing non-admin users. With the same method, we can create job function/project/team/etc., dedicated groups that will share the same sets of permissions, which is more secure and easy than user-level permission management.

Sensitive Output

In the example above, we have an output marked as sensitive = true:

 
output "password" {
  value     = aws_iam_user_login_profile.administrator.password
  sensitive = true
}


In Terraform, an output can be marked as containing sensitive material using the optional sensitive argument. Terraform will hide values marked as sensitive in the messages from terraform plan and terraform apply.

In the above example, our admin user has an output which is their password. By declaring it as sensitive, we won't see the value when we execute terraform output. We'd have to specifically ask Terraform to output that variable to see the content (or use the -json or -raw command-line flag).

Here are two best practices for managing sensitive data with Terraform:

  1. If you manage any sensitive data with Terraform (like database passwords, user passwords, or private keys), treat the state itself as sensitive data because they are stored in the state. For resources such as databases, this may contain initial passwords
  2. Store the state remotely to avoid storing it as plain-text JSON files. If we use a remote state, Terraform does not persist state to the local disk. We can even use some backends that can be configured to encrypt the state data at rest.

3. Enforcing MFA: Customer-Managed Policy and Policy Condition Use Case

The way to enforce MFA in AWS isn't as straightforward as it can be, but it can be achieved with a single policy:

enforce_mfa.tf:

 
data "aws_iam_policy_document" "enforce_mfa" {
  statement {
    sid    = "DenyAllExceptListedIfNoMFA"
    effect = "Deny"
    not_actions = [
      "iam:CreateVirtualMFADevice",
      "iam:EnableMFADevice",
      "iam:GetUser",
      "iam:ListMFADevices",
      "iam:ListVirtualMFADevices",
      "iam:ResyncMFADevice",
      "sts:GetSessionToken"
    ]
    resources = ["*"]
    condition {
      test     = "BoolIfExists"
      variable = "aws:MultiFactorAuthPresent"
      values   = ["false", ]
    }
  }
}

resource "aws_iam_policy" "enforce_mfa" {
  name        = "enforce-to-use-mfa"
  path        = "/"
  description = "Policy to allow MFA management"
  policy      = data.aws_iam_policy_document.enforce_mfa.json
}


There are a couple of important things to notice here.

Customer-Managed Policy

Contrary to the AdministratorAccess policy we used in the previous section (which is an AWS-managed policy), here we have defined a "customer-managed policy."

Note: We should try to use customer-managed policies over inline policies. In most cases, you do not need the inline policy at all. If you are still interested, see this inline policy.

Multiple Ways to Create a Policy

There are multiple ways to create a policy, and the example above is only one of them. In this example, we created it using Terraform data.

We could also:

  • Create a policy using JSON strings
  • Convert a Terraform expression result to valid JSON syntax 

The benefit of using "aws_iam_policy_document" data is that the code looks nice and clean because they are Terraform/HashiCorp's HCL syntax. However, it isn't always as straightforward as it seems, and debugging it would be unbearable if you don't use it regularly.

Sometimes, writing a JSON string and using it to create a policy is easier. However, JSON strings in a Terraform source code file can look a bit weird and not clean (after all, they are multiline strings), especially when they are of great length.

There isn't a one-size-fits-all choice here; you'd have to decide on your own which is best for your use case.

There is, however, an interesting advantage of using JSON strings: you can validate JSON policy in the AWS IAM console.

Policy Condition

In this example above, we used a "policy condition," which only makes the policy effective when there isn't a multi-factor authentication.

This policy loosely translates to:

"Deny any operation that isn't MFA device-related if you don't have multi-factor authentication."

We can use aws_iam_group_policy_attachment to attach it to a group, then all the users in that group are affected. For example:

 
resource "aws_iam_group_policy_attachment" "enforce_mfa" {
  group      = aws_iam_group.administrators.name
  policy_arn = aws_iam_policy.enforce_mfa.arn
}


This makes sure the administrator group must enable MFA.

4. Strong Password Policy and Password Rotation

The AWS default password policy enforces the following:

  • Minimum password length of 8 characters and a maximum length of 128 characters
  • Minimum of three of the following mix of character types: uppercase, lowercase, numbers, and ! @ # $ % ^ & * ( ) _ + - = [ ] { } | ' symbols
  • Not be identical to your AWS account name or email address

We can; however, use a customized and strongerpassword_policy.tf:

 
resource "aws_iam_account_password_policy" "strict" {  minimum_password_length        = 10  
require_uppercase_characters   = true  
require_lowercase_characters   = true  
require_numbers                = true  
require_symbols                = true  
allow_users_to_change_password = true
}```


By using aws_iam_account_password_policy, we can also specify how often the users should change their password, and whether they could reuse their old passwords or not:

 
resource "aws_iam_account_password_policy" "strict" {  
# omitted  
max_password_age               = 90  
password_reuse_prevention      = 3
}


IAM Tutorial: Part 2

In the second part of the IAM tutorial, we will cover:

  • How to centralize IAM to reduce operational overhead with cross-account assume role
  • How to create an EC2 instance profile
  • How to implement Just-In-Time access management with the HashiCorp Vault AWS engine
AWS Terraform (software)

Published at DZone with permission of Tiexin Guo. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • mTLS Everywere
  • Public Key and Private Key Pairs: Know the Technical Difference
  • Unlocking the Power of Elasticsearch: A Comprehensive Guide to Complex Search Use Cases
  • A Gentle Introduction to Kubernetes

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: