Managing AWS IAM using Terraform

Terraform IAM Tutorial – Easy AWS automation

AWS IAM (Identity and Access Management) is an Amazon Web Service that controls users’ and services’ access to AWS resources. With AWS IAM, you can configure authentication and authorization of identities to AWS resources and services by managing users, groups, roles, policies, and identity providers. This article contains Terraform IAM resource usage examples to automate users, groups, policies, and roles management in AWS IAM service.

Prerequisites

To start managing any AWS services, including AWS IAM using Terraform, you need to install Terraform on your machine and set up access to your AWS account using the AWS access key.

Alternatively, you can set up and launch a Cloud9 IDE Instance.

Project Structure

This article constitutes four sections that include managing AWS users, groups, policies and, roles using Terraform. Each section of this article has an example that you can execute independently. Every example can be put in a separate Terraform *.tf file to achieve the results shown in this article.

All Terraform files are in the same folder and belong to the same Terraform state file:

Terraform files

Make sure to use commands to avoid unnecessary errors while following the article:

  • terraform validate to verify and test your Terraform HCL file
  • terraform plan to check out the desired changes on every Terraform file creation
  • terraform apply to create the resources in AWS

AWS Terraform provider

To start managing the AWS IAM service, you need to declare the AWS Terraform provider in a providers.tf file:

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "3.69.0"
    }
  }
}
provider "aws" {
  profile = "default"
  region  = "us-east-2"
}

Run the terraform init command to initialize the Terraform working directory with the AWS plugins for the terraform configuration.

Here is the execution output.

Terraform IAM project initialization

Manage AWS IAM users using Terraform

This article section will cover how to manage AWS users using Terraform.

Create user

An IAM user is a specific user and an identity with an inline policy that is user created and used to interact with AWS resources. To create an IAM User, use the aws_iam_user Terraform resource and assign the required argument (name), which specifies the user’s identity name (iam_user.tf):

resource "aws_iam_user" "user" {
  name = "luzze.john"
  path = "/"
}

Create an AWS Access Key for the user

To create a user with an AWS Access Key and AWS Secret Access Key, you can use the aws_iam_access_key resource and assign the required argument, such as user, which is the identity of the user to associate with the access key (iam_access_key.tf) and assign permissions to it.

The pgp_key argument provides encryption and decryption of the user’s AWS Secret Access Key. PGP (Pretty Good Privacy) is a data encryption method that transforms plain text into an encrypted text block that can be shared and transmitted securely over the network. The recipient can use the PGP key to convert the message into a readable format. In the following example, we use keybase.io for end-to-end encryption.

The aws_iam_user_policy resource defines the new user’s access level to the AWS resources. In this case, the user has permission to read all the information about the EC2 resources.

resource "aws_iam_user" "nsamba_robert" {
  name = "nsamba.robert"
  path = "/"
}
resource "aws_iam_access_key" "nsamba_robert_key" {
  user    = aws_iam_user.nsamba_robert.name
  pgp_key = "keybase:kelvingalabuzi"
}
resource "aws_iam_user_policy" "ec2_policy" {
  name = "ec2_policy"
  user = aws_iam_user.nsamba_robert.name
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "ec2:Describe*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}
output "Access_key_id" {
  value = aws_iam_access_key.nsamba_robert_key.id
}

output "Access_key_secret" {
  value = aws_iam_access_key.nsamba_robert_key.encrypted_secret
}

Note: the IAM Policy Simulator Console https://policysim.aws.amazon.com/ allows you to test policy.

Note: every AWS service has APIs that define what actions AWS users or roles can perform with the service. You always need to provide the required permissions explicitly in the aws_iam_user_policy resource found in the iam policy section. To find out what Actions you can specify in the IAM policy statement, google the API reference for the specific service. For example, for EC2 service, it might be “AWS EC2 API reference“. Open the official AWS API Reference documentation for the specific service in the search results to review all available API actions.

AWS API reference documentation

Create a user login profile

By default, when you create a user, it doesn’t automatically allow that user to login into the AWS web console. The purpose of an IAM login profile is to control an AWS user’s permissions to login into the AWS web console.

To create an IAM user with a login profile, you can use the aws_iam_user_login_profile resource and assign the required arguments, such as the user and pgp_key (iam_user_login_profile.tf):

resource "aws_iam_user" "nicholas" {
  name          = "nicholas.kapenge"
  path          = "/"
  force_destroy = true
}
resource "aws_iam_user_login_profile" "nicholas_profile" {
  user    = aws_iam_user.nicholas.name
  pgp_key = "keybase:kelvingalabuzi"
}
output "password" {
  value = aws_iam_user_login_profile.nicholas_profile.encrypted_password
}

Create a user using Terraform’s IAM Module

Alternatively, you can create AWS IAM users using the AWS Terraform IAM module. This module allows you to create a new user with an AWS Access Key, AWS Secret Access Key, and a login profile with less Terraform code (iam_user_module.tf):

module "iam_user" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-user"
  version = "~> 4.3"
  name          = "kelvin.galabuzi"
  force_destroy = true
  pgp_key = "keybase:kelvingalabuzi"
  password_reset_required = false
}

Note: the purpose of every Terraform module is to hide and encapsulate the implementation logic of your Terraform code into a reusable resource. Terraform modules can be reused in multiple places of your Terraform project without duplicating lots of Terraform code over and over again.

Note: every open-source Terraform module code can be found at GitHub. For example, here’s a source code of the terraform-aws-modules/iam/aws module. // in the source attribute of theiam_user module allows specifying the module’s nested folder in the project structure.

Add an IAM policy to a user

AWS IAM policies are rules that define the level of access that Users have to AWS resources. To add an IAM policy to a user, use the aws_iam_user_policy resource and assign the required arguments, such as the policy, which requires a JSON formatted string. The user argument defines the user to attach the policy to (iam_user_policy.tf):

resource "aws_iam_user_policy" "s3_policy" {
  name = "s3_policy"
  user = aws_iam_user.linda.name
  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "s3:*",
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
resource "aws_iam_user" "linda" {
  name = "linda.lillian"
  path = "/"
}
resource "aws_iam_access_key" "linda_key" {
  user = aws_iam_user.linda.name
}

Alternatively, you can add an IAM policy to a User using the aws_iam_user_policy_attachment resource and assign the required arguments, such as the user and policy_arn (Amazon Resource Number). Here’s the content of the iam_user_policy_attachement.tf file:

resource "aws_iam_user" "mwanjje" {
  name = "mwanjje.joshua"
}
resource "aws_iam_policy" "s3_policy" {
  name        = "s3_policy"
  description = "Allow all S3 actions"
  policy      = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "s3:*",
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
resource "aws_iam_user_policy_attachment" "attach_s3_policy" {
  user       = aws_iam_user.mwanjje.name
  policy_arn = aws_iam_policy.s3_policy.arn
}

Create user SSH keys

IAM User SSH Keys can authenticate access and enable MFA to AWS resources such as AWS CodeCommit repositories. To upload an SSH public key and associate it with a user, use the aws_iam_user_ssh_key resource and assign the required arguments such as username, encoding, and public_key.

Here’s the content of the iam_user_ssh_key.tf file:

resource "aws_iam_user" "walter" {
  name = "walter.king"
  path = "/"
}
resource "aws_iam_user_ssh_key" "walter_ssh_key" {
  username   = aws_iam_user.walter.name
  encoding   = "SSH"
  public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCBz3n69Rbf32ILGsFwpc/N6XH7xgyHWtig6TvrgAuLaJyb53EBUuuG1DirICOXIoBQq1yGtsoVhMQVVBUKp41VbHDaNLcMC3tt+nj5VMv8RE/nLuDkiI+1iJYPgZbKRF1/Qzyei77B5k+BznaRUPqjnJSzSunjvuEqCjcH2fPQjlTT68+apzq0SIhuedXSkd9svrWENjvw37HvDU4MOUshZJyLvKAXAuTZA7yZOSrPkyAfO6o5bBICGjxOCeoQcgphk3u4FvnfD+k7ToHnWj28/LqPWguFywTjzZiZ+pmDJ6CzWuwJ0r8l2VrQJ4uP5QFntkr10PzpTYqe+iB2LPsN"
}

Create an AWS account password policy

An AWS account password policy defines the rules to follow when creating passwords to have strong passwords. To create a custom password policy for your AWS account users, you can use the aws_iam_account_password_policy resource and assign the supported arguments (iam_account_password_policy.tf).

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

Manage AWS IAM Groups using Terraform

This article section will cover how to manage AWS Groups users using Terraform.

Create an AWS IAM Group

AWS IAM Groups are collections of IAM Users in your AWS Account. To create an AWS IAM group using Terraform, you can use the aws_iam_group resource and assign the name as the required argument (iam_group.tf).

resource "aws_iam_group" "cloudadmins" {
  name = "Cloud-Admins"
  path = "/"
}

Create an AWS IAM Group Membership

AWS IAM Group memberships allow you to organize multiple users according to their functionality in the AWS Account. This enables you to restrict specific resources only if the user is part of a specified group. To create a new user and add a group membership, you can use the aws_iam_user_group_membership resource and assign the required arguments, such as user and groups (iam_user_group_membership.tf).

resource "aws_iam_user_group_membership" "IT_Membership" {
 user = aws_iam_user.matovu.name
 groups = [
   aws_iam_group.IT.name
 ]
}
resource "aws_iam_user" "matovu" {
 name = "matovu.peter"
}
resource "aws_iam_group" "IT" {
  name = "IT-Department"
}

Create an AWS IAM Group Policy

AWS IAM Group policies allow you to define a set of permissions that Users associated with a specific group can perform. To create IAM policies and attach them to a group, use the aws_iam_group_policy resource and pass the required arguments, such as the policy and group (iam_group_policy.tf).

resource "aws_iam_group_policy" "s3_policy" {
  name  = "s3_policy"
  group = aws_iam_group.sys_analysts.name
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "s3:*",
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
resource "aws_iam_group" "sys_analysts" {
  name = "System-Analysts"
  path = "/"
}

Alternatively, you can add an IAM group policy to a Group using the aws_iam_group_policy_attachment resource and assign the required arguments, such as the group and policy_arn (Amazon Resource Number).

Here’s the content of the iam_group_policy_attachment.tf file:

resource "aws_iam_group" "clouddevs" {
  name = "Cloud-Developers"
}
resource "aws_iam_policy" "clouddevs_s3_policy" {
  name        = "clouddevs_s3_policy"
  description = "Allow all S3 actions"
  policy      = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "s3:*",
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
resource "aws_iam_group_policy_attachment" "s3_policy_attachment" {
  group      = aws_iam_group.clouddevs.name
  policy_arn = aws_iam_policy.s3_policy.arn
}

Manage AWS IAM Policies using Terraform

This article section will cover how to manage AWS IAM Policies using Terraform.

Create AWS IAM Policy

AWS IAM Policies are objects in AWS that define permissions to all AWS resources. To create an AWS IAM Policy, use the aws_iam_policy resource and assign the required argument, policy which is a defined JSON formatted string (iam_policy.tf).

resource "aws_iam_policy" "beanstalk_policy" {
  name        = "beanstalk_policy"
  path        = "/"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
        {
            "Effect": "Allow",
            "Action": "elasticbeanstalk:*",
            "Resource": "*"
        }
    ]
  })
}

The policy argument should contain a valid IAM policy document.

Instead of using the jsonencode() function and defining a policy using JSON syntax, it is also convenient to use the aws_iam_policy_document data source. This data source allows you to define the policy using Terraform HCL. Here’s the exact policy definition:

data "aws_iam_policy_document" "beanstalk_policy" {
  statement {
    effect    = "Allow"
    actions = [
      "elasticbeanstalk:*"
    ]
    resources = [
      "*"
    ]
  }
}
resource "aws_iam_policy" "beanstalk_policy" {
  name        = "beanstalk_policy"
  path        = "/"
  policy = data.aws_iam_policy_document.beanstalk_policy.json
}

The beanstalk_policy contains the statement, actions, and resources required for access management.

Create an IAM Policy attachment

You can use an IAM Policy attachment to attach a policy to AWS Users, Roles, and Groups. To create an IAM Policy attachment, you can use the aws_iam_policy_attachment resource and assign the policy_arn, a required argument (iam_policy_attachment.tf).

resource "aws_iam_user" "victor" {
  name = "victor.manuel"
}
resource "aws_iam_role" "msp_access" {
  name = "msp_access"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}
resource "aws_iam_group" "Developer_Support" {
  name = "Developer_Support"
}
resource "aws_iam_policy" "elasticbeanstalk_policy" {
  name        = "Developer_Support_BeanStalk_FullAccess"
  description = "Full Access to Elastic BeanStalk Environments"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
        {
            "Effect": "Allow",
            "Action": "elasticbeanstalk:*",
            "Resource": "*"
        }
    ]
}
EOF
}
resource "aws_iam_policy_attachment" "elasticbeanstalk_policy_attachment" {
  name       = "elasticbeanstalk_policy_attachment"
  users      = [aws_iam_user.victor.name]
  roles      = [aws_iam_role.msp_access.name]
  groups     = [aws_iam_group.Developer_Support.name]
  policy_arn = aws_iam_policy.elasticbeanstalk_policy.arn
}

Manage AWS IAM Roles using Terraform

This article section will cover how to manage AWS IAM Roles using Terraform.

Create an AWS IAM Role

AWS IAM Roles are identities you can create with specific permissions with valid credentials or access keys for short durations. Roles can be assumed by entities that you trust. To create an AWS IAM Role, you can use the aws_iam_role resource and pass the required arguments, such as the role’s name and the assume_role_policy, which defines the entity to utilize the role (iam_role.tf).

resource "aws_iam_role" "s3_role" {
  name = "s3_role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = ""
        Principal = {
          Service = "s3.amazonaws.com"
        }
      },
    ]
  })
}

Create an AWS IAM role and assign a policy

As AWS IAM Users and Groups, you can assign policies to Roles. To create an AWS IAM Role with an access policy, you can use the aws_iam_role_policy resource and define the required arguments, such as role to attach the policy and the policy document configured in JSON format. In the following example, we attach a permission policy assigning this single policy in the AWS Lamba service full access to the AWS Elastic Beanstalk environments (iam_role_policy.tf).

resource "aws_iam_role" "lambda-beanstalk" {
  name = "lambda-beanstalk"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = ""
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      },
    ]
  })
}
resource "aws_iam_role_policy" "elasticbeanstalk_policy" {
  name = "elasticbeanstalk_policy"
  role = aws_iam_role.lambda-beanstalk.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
        {
            "Effect": "Allow",
            "Action": "elasticbeanstalk:*",
            "Resource": "*"
        }
    ]
  })
}

Alternatively, you can use the aws_iam_role_policy_attachment resource to attach an IAM Policy to an IAM role, then use the required arguments such as the role and the policy_arn of the policy that you want to apply (iam_role_policy_attachment.tf).

resource "aws_iam_role" "s3_beanstalk" {
  name = "s3_beanstalk"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "s3.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}
resource "aws_iam_policy" "s3_beanstalk_policy" {
  name        = "S3_BeanStalk_Policy"
  description = "S3 Access to Elastic BeanStalk"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
        {
            "Effect": "Allow",
            "Action": "elasticbeanstalk:*",
            "Resource": "*"
        }
    ]
}
EOF
}
resource "aws_iam_role_policy_attachment" "s3_beanstalk_policy_attachment" {
  role       = aws_iam_role.s3_beanstalk.name
  policy_arn = aws_iam_policy.s3_beanstalk_policy.arn
}

Create an AWS IAM Instance Profile

An Instance profile is used to pass an IAM role to an AWS EC2 Instance. To create an AWS IAM Instance profile, you can use the aws_iam_instance_profile resource (iam_instance_profile.tf).

resource "aws_iam_instance_profile" "ec2_instance_profile" {
  name = "ec2_instance_profile"
  role = aws_iam_role.ec2_web_instances.name
}
resource "aws_iam_role" "ec2_web_instances" {
  name = "ec2_web_instances"
  path = "/"
  assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Principal": {
               "Service": "ec2.amazonaws.com"
            },
            "Effect": "Allow",
            "Sid": ""
        }
    ]
}
EOF
}
data "aws_iam_policy_document" "s3_policy_document" {
  statement {
    sid = "1"
    actions = [
      "s3:ListAllMyBuckets",
      "s3:GetBucketLocation",
    ]
    resources = [
      "arn:aws:s3:::*",
    ]
  }
 
}
 
resource "aws_iam_policy" "ec2_instance_profile_policy" {
  name   = "ec2_instance_profile_policy"
  path   = "/"
  policy = data.aws_iam_policy_document.s3_policy_document.json
}
resource "aws_iam_policy_attachment" "ec2_instance_profile_attachment" {
  name       = "ec2_instance_profile_attachment"
  roles      = [aws_iam_role.ec2_web_instances.name]
  policy_arn = aws_iam_policy.ec2_instance_profile_policy.arn
}

Summary

This article covered using Terraform IAM resources to perform everyday management tasks on AWS IAM.

Similar Posts