There are hundreds of posts online about what is EC2 IAM role so I’m not going to discuss that here. Here we will develop the code to create an EC2 IAM role with Terraform and deploy with Terragrunt. I’m assuming you know what both of these tools are… if not checkout my introduction posts on how to setup your development environment. Or if you need a introduction to Terraform modules and how to use them click here. Do note that an EC2 IAM role can only be used for EC2 instances. The IAM policies can be shared with other resources or services though.
This Terraform module creates AWS IAM policy then creates IAM role specifically designed to be used by EC2 instances. After that it attaches the IAM role to the EC2 instance profile. Lastly attaches the IAM policy to the EC2 IAM role. Remember every IAM role needs a set of policies (permissions).
Terraform EC2 IAM role module

Here’s the main.tf file of the module.
# Create the AWS IAM role.
resource "aws_iam_role" "this" {
name = var.ec2_iam_role_name
path = "/"
assume_role_policy = var.assume_role_policy
}
# Create AWS IAM instance profile
# Attach the role to the instance profile
resource "aws_iam_instance_profile" "this" {
name = var.ec2_iam_role_name
role = aws_iam_role.this.name
}
# Create a policy for the role
resource "aws_iam_policy" "this" {
name = var.ec2_iam_role_name
path = "/"
description = var.policy_description
policy = var.policy
}
# Attaches the policy to the IAM role
resource "aws_iam_policy_attachment" "this" {
name = var.ec2_iam_role_name
roles = [aws_iam_role.this.name]
policy_arn = aws_iam_policy.this.arn
}
As always there’s a vars.tf
variable ec2_iam_role_name {
type = string
validation {
condition = length(var.ec2_iam_role_name) > 4 && substr(var.ec2_iam_role_name, 0, 4) == "svc-"
error_message = "The ec2_iam_role_name value must be a valid IAM role name, starting with \"svc-\"."
}
}
variable policy_description {
type = string
validation {
condition = length(var.policy_description) > 4
error_message = "The policy_description value must contain more than 4 characters."
}
}
variable assume_role_policy {}
variable policy {}
The module full code: https://github.com/masterwali/tf-module-iam-ec2-role
Terraform variable type constraints
If you noticed the “type” keyword in the variable declaration, that’s to ensure the kind of answer matches exactly what the module expects. This way someone doesn’t give an integer to a string type variable, it may break the Terraform apply but it may not error during Terraform plan. So we want to catch problems like that as soon as possible with type constraints. Now all the validations are done during the Terraform plan.
Terraform variable validation
Terraform starting with version 0.13.x released this new capability to apply validation on the answers provided to the variables. If you noticed the validation blocks above; the ‘ec2_iam_role_name’ var checks for both the length and what should be the starting characters. This is excellent way to ensure everyone’s following the naming conventions your organizations have created. The second validation on the ‘policy_description’ is just ensuring the provided value is more than 4 characters. Learn more about variable conditions.
How to use the module
In another git project we’ll have this setup.


The main.tf that will call the Terraform module. Be sure to update your git code. Now we create many, many EC2 IAM roles with the same naming convention! By default the source variable will use the master branch!
provider "aws" {
region = var.aws_region
profile = var.aws_cli_profile
}
terraform {
backend "s3" {}
}
module "web_server" {
source = "git@gitlab.com:cloudly-engineer/aws/tf-modules/iam-ec2-role.git"
ec2_iam_role_name = "svc-web-server-role"
policy_description = "IAM ec2 instance profile for the web servers."
assume_role_policy = file("assumption-policy.json")
policy = data.aws_iam_policy_document.web_server.json
}
Here’s the sample “web server” IAM policy that’s attached to this role.
data "aws_iam_policy_document" "web_server" {
statement {
sid = "GetS3Stuff"
effect = "Allow"
actions = [
"s3:List*",
"s3:Get*"
]
resources = ["*"]
}
}
Then do a terragrunt init, plan and apply!
Here’s what the error message looks like when the validation fails during the plan.

The calling terraform/terragrunt code: https://github.com/masterwali/ec2-iam-role
Subscribe to get notified when more AWS and Terraform code is published!