Skip to content

How to create IAM policy and IAM role in Terraform – 3 ways | 2022

3 ways to create AWS iam role in terraform 2022

In today’s tutorial, 3 ways to create a IAM policy and IAM role in the AWS cloud using Terraform. In the guide you will also find a simple way how you can add an IAM policy to an IAM Role. I’m assuming you’re already somewhat familiar with Terraform and the AWS cloud so I won’t go into explaining the basic concepts. If you want to learn more about policies and the IAM role, then you can take a look at the article How to automatically copy data from AWS S3 – Lambda events (lepczynski.it)

Method 1 – aws_iam_policy_document

The first way is – create an additional data element in Terraform aws_iam_policy_document. This way of implementation, I think I have seen most often in old TF version. Below I will describe what the whole file contains. It allows you to create an IAM policy, create an IAM role, and add policy to the role. I often group different types of resources into files so that I can find them more easily in the project. In this example, I show the contents of the iam.tf file.

data aws_iam_policy_document

In this method, I create a data element aws_iam_policy_document and in it I define what IAM policy should look like. This is a key element and worth spending more time on it. I recommend always granting only the necessary permissions. Unfortunately, this makes the role less flexible, but it is a price worth paying in my opinion. By doing this you significantly increase security.

For example, if we give full access to all S3s instead of just one and our function does not work as expected, this could have very serious consequences.

Good advice – don’t give everyone admin privileges 😉

In such a iam policy document, I can of course define several expressions. In the following example, I first allow:

  • GetObject and DeleteObject in the specified folder of the source S3 bucket,
  • ListBucket of the source S3 and its one folder contents,
  • PutObjects in only one folder of the destination S3 bucket.

resource aws_iam_policy

Then I create an aws_iam_policy and add the avs_iam_policy_document created earlier to it.

resource aws_iam_role

The next step is to create the IAM role aws_iam_role. The role can later be assigned to an EC2 machine, Lambda function, etc.

resource aws_iam_role_policy_attachment

The final step is to add IAM policy to the IAM role aws_iam_role_policy_attachment. If I wanted to add more IAM policy to one role, I create more avs_iam_role_policy_attachment resources. Inside them I specify the same role name and the next policy name.

data "aws_iam_policy_document" "S3_automation_move_objects" {
  statement {
    sid = "allowS3"
    actions = [
      "s3:GetObject",
      "s3:DeleteObject",
    ]
    resources = [
      "arn:aws:s3:::test-ndgegy4364gdu-source-bucket/images/*"
    ]
  }

  statement {
    sid = "allowListBucket"
    actions = [
      "s3:ListBucket",
    ]
    resources = [
      "arn:aws:s3:::test-ndgegy4364gdu-source-bucket",
      "arn:aws:s3:::test-ndgegy4364gdu-source-bucket/images/*"
    ]
  }

  statement {
    sid = "putObject"
    actions = [
      "s3:PutObject",
    ]
    resources = [
      "arn:aws:s3:::test-ndgegy4364gdu-destination-bucket/images/*"
    ]
  }

}

resource "aws_iam_policy" "S3_automation_move_objects" {
  name        = "S3_automation_move_objects"
  description = "test - access to source and destination S3 bucket"
  path        = "/"

  policy = data.aws_iam_policy_document.S3_automation_move_objects.json
}

resource "aws_iam_role" "S3_automation_move_objects" {
  name        = "S3_automation_move_objects"
  description = "test - access to source and destination S3 bucket"

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

resource "aws_iam_role_policy_attachment" "S3_automation_move_objects" {
  role       = aws_iam_role.S3_automation_move_objects.name
  policy_arn = aws_iam_policy.S3_automation_move_objects.arn
}

Method 2 – jsonencode function

This time, to create an IAM policy in Terraform, I don’t use additional elements. I define the permissions directly in aws_iam_policy. Here I use a special function jsonencode. This function maps Terraform language values to JSON values. In this case, I also didn’t take the easy way out and defined more expressions. This makes it more like a real life solution.

Creating an aws_iam_role and adding an IAM policy to an IAM Role is done in the same way as in the first method, i.e. using aws_iam_role_policy_attachment.

resource "aws_iam_policy" "S3_automation_move_objects" {
  name        = "S3_automation_move_objects"
  description = "test - access to source and destination S3 bucket"
  path        = "/"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
			"s3:GetObject",
			"s3:DeleteObject",
        ]
        Effect   = "Allow"
        Resource = "arn:aws:s3:::test-ndgegy4364gdu-source-bucket1/images/*"
      },
      {
        Action = [
			"s3:ListBucket",
        ]
        Effect   = "Allow"
        Resource = [
			"arn:aws:s3:::test-ndgegy4364gdu-source-bucket1",
			"arn:aws:s3:::test-ndgegy4364gdu-source-bucket1/images/*"
		]
      },
      {
        Action = [
			"s3:putObject",
        ]
        Effect   = "Allow"
        Resource = "arn:aws:s3:::test-ndgegy4364gdu-destination-bucket1/images/*"
      },
    ]
  })
}



resource "aws_iam_role" "S3_automation_move_objects" {
  name        = "S3_automation_move_objects"
  description = "test - access to source and destination S3 bucket"

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

resource "aws_iam_role_policy_attachment" "S3_automation_move_objects" {
  role       = aws_iam_role.S3_automation_move_objects.name
  policy_arn = aws_iam_policy.S3_automation_move_objects.arn
}

Mode 3 – EOF

You can also use the third way, which is very similar to point no. 2. In this method, we don’t add additional elements. However, instead of using a function we paste the contents of the JSON file.

If you have IAM policies defined in JSON format, you can add the contents of such a policy without any changes in the aws_iam_policy resource.

Creating an IAM Role in Terraform is done in the same way as in the previous two ways. This time I also connect the policy to the role using aws_iam_role_policy_attachment.

resource "aws_iam_policy" "S3_automation_move_objects" {
  name        = "S3_automation_move_objects"
  description = "test - access to source and destination S3 bucket"
  path        = "/"

  policy = <<EOF
{
	"Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::test-ndgegy4364gdu-source-bucket1/images/*"
        },
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::test-ndgegy4364gdu-source-bucket1",
                "arn:aws:s3:::test-ndgegy4364gdu-source-bucket1/images/*"
            ]
        },
        {
            "Action": [
                "s3:putObject"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::test-ndgegy4364gdu-destination-bucket1/images/*"
        }
    ],
    "Version": "2012-10-17"
}
EOF
}


resource "aws_iam_role" "S3_automation_move_objects" {
  name        = "S3_automation_move_objects"
  description = "test - access to source and destination S3 bucket"

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

resource "aws_iam_role_policy_attachment" "S3_automation_move_objects" {
  role       = aws_iam_role.S3_automation_move_objects.name
  policy_arn = aws_iam_policy.S3_automation_move_objects.arn
}

TEST

On the beginning, it is good to learn Terraform on code that works, improve it and adapt it to your needs.

To check that the 3 methods are working correctly, all you have to do is replace the names of the S3 buckets with the ones you have created:

  • test-ndgegy4364gdu-source-bucket – my source bucket from which I download and delete data
  • test-ndgegy4364gdu-destination-bucket – my destination bucket to which I move data
terraform apply 2022

The code above will create:

  • IAM policy with name ‘S3_automation_move_objects’,
  • IAM role named ‘S3_automation_move_objects’,
  • add IAM Policy to IAM Role

Summary

I have tested all three ways of creating an IAM policy in Terraform. On a day-to-day basis, you can choose the one that suits you best, or the one that someone else has used before to keep things consistent.

If you know of any other way to create an IAM policy in Terraform, please feel free to share it in the comments.

Maybe in the next Terraform article I will combine the IAM role with the EC2 or Lambda function, so that we have a complete solution and a concrete example of use.

For more articles related to Docker or Kubernetes, see the Kubernetes – Wojciech Lepczyński – DevOps Cloud Architect (lepczynski.it).

Leave a Reply

Your email address will not be published.