How to Create IAM Role using Terraform in AWS

How to Create IAM Role in AWS using Terraform

How to Create IAM Role using Terraform in AWS

Dear Reader, I hope you are doing great. A few days ago, I wrote a post on how to create an IAM role using Cloudformation. In this post, I will help you create IAM role using Terraform in AWS.

We will see Terraform example configuration file that you can use to create IAM role using Terraform in AWS.

So Are you ready?

Alright, let’s start.

Background

Similar to CloudFormation, Terraform is a also a very popular Infrastructure as Code(IaC) tool. An added feather in the cap of terraform is that, it supports multiple cloud provider like AWs, Azure, GCP etc.

Since an IAM role can have an inline policy, customer-managed policy and AWS-managed policy, We’ll see how to attach each one of policy to a role using Terraform.

We will also see how to attach multiple inline policies and managed policies to an IAM role using Terraform.

In case you have just started with AWS, here is a small section on IAM roles that will help you understand this tutorial better.

Don’t want to miss any posts from us? join us on our Facebook group, and follow us on Facebook, Twitter, LinkedIn, and Instagram. You can also subscribe to our newsletter below to not miss any updates from us.

What is an IAM Role?

role

An IAM role is an IAM identity that has a set of permissions to interact with AWS services. Those permissions decide what a role can or can not do.

Unlike other entities like users, a role can’t do anything on its own. However, other trusted entities such as a user, an application running on EC2, a lambda function or an AWS service such as CloudFormation can assume the role to perform actions specified in the role policy.

  • Roles are not associated with users or groups
  • You can attach inline as well as managed policy to a role
  • A maximum of 10 managed policies can be attached to a role

In the next section, let’s learn to create an IAM role in AWS using Terraform.

Related: 5 Ways to Create and Manage Resources in AWS

A bit of Background on Creating an IAM Role

When you are creating an IAM role, you need to configure two kinds of policy

  1. Trust Policy: A policy specifying who can assume this role
  2. Permission Policy: Specify what the entity can do with this role

As you might already know, permissions policy can be inline policy or managed policy. Moreover, managed policies can be AWS managed or customer-managed. Does this confuse you?

Okay worry not, we’ll work on all these use cases today.

Related: How to Create an IAM policy on AWS using Terraform

Prerequisite

Assumption: Before you can use this tutorial to create an IAM role resource, you should know how to create a resource on AWS using terraform. If you are a beginner I highly recommend you to read my previous post on Getting Started With Terraform on AWS In Right Way. Once you have read the post, you are ready to move ahead with this post further.

Related: 5 Ways to Create and Manage Resources in AWS

Steps to Create IAM Role using Terraform in AWS

  • Declare the Provider
  • Initialize Your Project Directory
  • Prepare the Configuration file(main.tf) to Create IAM Role in AWS using Terraform
  • Run Terraform Apply to Create IAM Role

Step 1: Declare the Provider

In this tutorial, we’ll create IAM Role using Terraform in AWS. The first thing we will do is create a configuration file and specify the resource we want(Role & Policies).

Create your project or folder and create a main.tf file.

Start with the provider declaration(AWS) and how Terraform will authenticate with AWS. As you can see I have specified a profile which is created by when you do the AWS CLI setup in your system.

This is how It looks like-

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.27"
    }
  }
}

provider "aws" {
  profile = "default"
  region  = "ap-south-1"
}

As you can see, we have specified that we’ll be working with the AWS provider. This is a necessary step and without this code, you won’t be able to work with AWS.

Step 2: Initialize Your Project Directory

You have specified provider declaration. It’s time for you to tell Terraform to download provider-specific code/plugins that will be needed while working with AWS.

If you try to run terraform plan or terraform apply command without doing terraform init, you will get the below error-

terraform init

As you can it says provider required and suggests running terraform init.

Let’s do that.

Terraform init done

Once you run terraform init, AWS-specific plugins get installed and you are ready to create your first resource using Terraform.

Important Note: You only need to do this once. Once the provider-specific code is downloaded, you need not run this command again and again unless you are changing the provider version or something that needs to pull extra code.

Step 3: Prepare the Configuration file(main.tf) to Create IAM Role in AWS using Terraform

As I said, while creating a role using Terraform on AWS, there are mainly two things that you need to specify.

  1. Trust Relationship(Who is allowed to assume the role and perform actions allowed by the role)
  2. Role Policy(What a role is allowed to do.)
    • Inline Policy
    • AWS managed policy
    • Customer managed policy

Let’s see all of these use cases in this section

3.1 Create a Trust Relationship for Role using Terraform

A trust relationship is mandatory for security reasons. Otherwise, anyone can create a role with admin privilege and do whatever they want. Therefore, a role can only be assumed by the entity specified in the trust relationship of the role. So it’s very important. You can create an IAM role with a trust relationship like this

resource "aws_iam_role" "demo_role" {
  name = "demo_role"
  assume_role_policy = <Policy JSON>
}

There are multiple ways in which to specify a policy JSON. However, as discussed in the previous post on policies in terraform , the preferred way is to use the aws_iam_policy_document data source shown below. That way it is easier to manage the policy.

So here is an example IAM role using terraform with trust relationship as lambda service-

data "aws_iam_policy_document" "trust_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "demo_role" {
  name               = "demo_role"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.trust_policy.json
}

Explanation:

Firstly we are creating a data source to create a trust policy specifies sts:AssumeRole action and the role can be assumed by an AWS service lambda.

Then we are creating an IAM role specifying using aws_iam_role resource. Here we have specified a name which is nothing but the role name we are creating. The path in which the role is created and finally assume_role_policy is specified which defines the trust relationship.

We have an IAM role with a trust policy. Time to attach permission policies to it.

3.2 Create an IAM Role with Inline Policy using Terraform

There are two ways in which you can attach an inline policy to an IAM role in Terraform.

  1. Using inline_policy argument of aws_iam_role resource
  2. Using terraform resource aws_iam_role_policy

Way 1: Using inline_policy attribute

You can specify an inline policy in a role by using inline_policy argument like below.

  inline_policy {
    name = "my_inline_policy"
    policy = <JSON POlicy>
  }

Just like any other policy you can use heredoc format, jsonencode or aws_iam_policy_document data source to define the policy. Heredoc I don’t recommend it at all. The other ones we will see in this section. You can specify multiple inline policies just by having multiple blocks.

In the below example, I am creating an IAM role with an inline policy using all the formats mentioned above. You can use what is more convenient for you. Here is a working example of creating an IAM role using Terraform.

main.tf

#Provider Declaraion
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.27"
    }
  }
}

#Profile to use for authentication
provider "aws" {
  profile = "default"
  region  = "ap-south-1"
}

#IAM role with Inline policy
resource "aws_iam_role" "demo_role" {
  name               = "demo_role"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.trust_policy.json

  #Inline Polcy using jsencode
  inline_policy {
    name = "demo_inline_1"

    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Action   = ["s3:GetObjectVersion"]
          Effect   = "Allow"
          Resource = "*"
        },
      ]
    })
  }

  #Inline policy using datasource
  inline_policy {
    name   = "demo_inline_2"
    policy = data.aws_iam_policy_document.inline_policy.json
  }


}

#Datasource defining an inline policy
data "aws_iam_policy_document" "inline_policy" {
  statement {
    actions   = ["s3:GetObject"]
    resources = ["*"]
  }
}

#Datasource defining Trust Relationship
data "aws_iam_policy_document" "trust_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}
3.2.1 Remove inline policy

We saw how we can add an inline policy. Here is how you can remove it. Just pass inline_policy {} empty block and inline policy gets removed.

resource "aws_iam_role" "demo_role" {
  name               = "demo_role"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.trust_policy.json

  #Remove Inline Policy
  inline_policy {
}
}

3.3 Create an IAM Role using Managed Policy using Terraform

This section will create an IAM role using managed IAM policy using Terraform. We will see both AWS-managed policy and customer-managed policy.

In the below example, we are attaching one existing AWS-managed policy and a customer-managed policy.

  • AWS Managed: arn:aws:iam::aws:policy/service-role/AmazonDMSRedshiftS3Role
  • Customer Managed: arn:aws:iam::672607396920:policy/S3-Bucket-Access-Policy

All you need to do is provide the ARN of any policy that you already have to the managed_policy_arns parameter of aws_iam_role resource in an array. And that’s it.

managed_policy_arns = ["RoleArn1", "RoleArn1"]

Example-

#IAM role with managed policy
resource "aws_iam_role" "demo_role" {
  name               = "demo_role"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.trust_policy.json

  #AWS Managed policy
  managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonDMSRedshiftS3Role, arn:aws:iam::672607396920:policy/S3-Bucket-Access-Policy"]

}

Note: You can even pass the ARN of the managed IAM policy that you create in the same resource for example-

resource "aws_iam_role" "demo_role" {
  name                = "demo_role"
  assume_role_policy  = data.aws_iam_policy_document.trust_policy.json
  managed_policy_arns = [aws_iam_policy.demo_policy.arn]
}

resource "aws_iam_policy" "demo_policy" {
  name = "policy1"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action   = ["s3:*"]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
3.3.3 Removing managed policy managed using managed_policy_arns

As you would expect, passing an empty array to the managed_policy_arns attribute removes all the managed policy attachments.

managed_policy_arns = []

This is what the role looks like-

#IAM role with Inline policy
resource "aws_iam_role" "demo_role" {
  name               = "demo_role"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.trust_policy.json

  #AWS Managed policy
  managed_policy_arns = []

}

3.3 Attach a Policy to a role using aws_iam_role_policy_attachment terraform resource

This resource is used to attach a managed IAM policy to an IAM role. Let’s understand this by an example. You can create a standalone IAM policy using the aws_iam_policy resource that can be reused across various use cases and then attach the created role using the aws_iam_role_policy_attachment resource.

This is what it looks like –

resource "aws_iam_role_policy_attachment" "policy_to_role" {
  role = "${aws_iam_role.demo_role.name}"
  policy_arn = "${aws_iam_policy.demo_policy.arn}"
}

Explanation: role is the role name to which you want to attach the policy. and policy_arn is of course the Arn of the policy you want to attach to this role.

Also, this resource allows to attach an IAM policy to an existing IAM role. You can create a new policy or attach an existing policy to the role using Terraform;

#Create a Standalone policy
resource "aws_iam_policy" "demo_policy" {
  name = "demo_policy"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action   = ["s3:*"]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}

resource "aws_iam_role_policy_attachment" "policy_to_role" {
  role = "${aws_iam_role.demo_role.name}"
  policy_arn = "${aws_iam_policy.demo_policy.arn}"
}

Important Note: When you are using this, you can’t use the managed_policy_arns attribute of role resource. Otherwise, both will try to manage the policy for the role creating unexpected results.

Attach Multiple policies while using aws_iam_role_policy_attachment

The simplest way as you might think is for example if you have two policies, you can simply have two aws_iam_role_policy_attachment resources.

#Attach policy-1 ro role
resource "aws_iam_role_policy_attachment" "policy1_to_role" {
  role = "${aws_iam_role.demo_role.name}"
  policy_arn = "${aws_iam_policy.policy2.arn}"
}

Attach policy-2 to role
resource "aws_iam_role_policy_attachment" "policy2_to_role" {
  role = "${aws_iam_role.demo_role.name}"
  policy_arn = "${aws_iam_policy.policy2.arn}"
}

However, this doesn’t look clean nor is efficient. When you want to attach many policies to your role. To keep your code efficient and easily maintainable, use a looping construct like for_each to achieve this.

resource "aws_iam_role_policy_attachment" "role-policy-attachment" {
  for_each = toset([
    "arn:aws:iam::aws:policy/AmazonEC2FullAccess", 
    "arn:aws:iam::aws:policy/AmazonS3FullAccess"
  ])

  role       = var.iam_role_name
  policy_arn = each.value
}

Step 4: Run Terraform Apply to Create IAM Role

We are ready with our configuration. Time to deploy this terraform configuration.

Open a terminal in the folder where you have your Terraform configuration file.

Initialize the directory with AWS necessary plugins by running terraform init

Run terraform apply

Our resources are successfully created as you can see.

Created IAM role using terraform

Clean Up

If you are creating this IAM role for learning purposes, you should delete or destroy your resource.

Simply run terraform destroy and it will delete all that you created using main.tf.

Happy Learning !!!

Note: Use terraform destroy with caution. As it deletes whatever you created. So make sure intend to do so.

Conclusion:

In this post, we learned How to Create IAM Role using Terraform in AWS.

  • We learn about mandatory parameters while creating a role
  • We also learned how we can add various types of policies such as inline or managed policies
  • The managed policy can be AWS-managed or customer-managed as per the requirement
  • You can have a maximum of 10 managed policies per role

I hope you found this post helpful.

Enjoyed the content?

Subscribe to our newsletter below to get awesome AWS learning materials delivered straight to your inbox.

Don’t forget to motivate me by-

  • Adding a comment below on what you liked and what can be improved.
  • Follow us on
  • Subscribe to our newsletter to get notified each time we post new content
  • Share this post with your friends

Suggested Read:

Leave a Reply

Your email address will not be published. Required fields are marked *