Alternative Way Provision AWS Resource via Terraform (Using IAM Assume Role Reference)
Tags: aws, terraform, iam
Published: 2023-03-01 14:55:43

When provisioning or creating Amazon Web Services (AWS) resources via Terraform, maybe the most common method used is using AWS credentials reference that includes AWS Access Key ID and AWS Secret Access Key.

In this blogpost, I will explain one of the alternative for provisioning AWS resource via Terraform. I reference IAM assumed role during provision.

For context, before we discussed the configuration in detail, below is the scenario or architecture that I used in this blogpost:

![topology](https://ludesdeveloper.files.wordpress.com/2023/02/topology-tf-with-iam-assumed-role.png)

- In this blogpost, I used Terraform with backend remote or Terraform Cloud
- In most common usage of Terraform, we reference AWS Access Key ID and AWS Secret Access Key for communication or interaction to AWS API. In this blogpost, I used IAM role that associated with IAM policy that needed for provision AWS resource
- But in this case, We still need IAM user or if I may call that intermediary user that act as middle man when Terraform interact with AWS API. And this IAM user still need AWS Access Key ID and AWS Secret Access Key, although this IAM user does not associated with IAM policy at all.
- IAM role will run assume role to IAM intermediary user before it can send API call that used for provision AWS resource
- The idea is we only concern about AWS keys that used by IAM intermediary user. And after that, We only need to create IAM role with privilege or IAM policy that needed, then IAM role will assume role to IAM intermediary user

## Prerequisites

As mentioned on scenario above, to provision from terraform via IAM assume role, we need some IAM resource. Here are some of them:

- IAM user that act as intermediary user
- IAM role
- Associate IAM policy to IAM role

In this blogpost, I will create IAM resource using terraform code. Here are the steps:

1. Create IAM user that act as intermediary user. This IAM user will be associated to IAM group. IAM user and IAM group will be provisioned using [public terraform aws module iam](https://registry.terraform.io/modules/terraform-aws-modules/iam/aws/latest). Here is example of code:

    ```
    #############
    # IAM Users #
    #############
    
    module "tf_assume_user" {
      source  = "terraform-aws-modules/iam/aws//modules/iam-user"
      version = "5.9.2"
    
      name                          = "tf-assume-user"
      force_destroy                 = true
      create_iam_access_key         = false
      create_iam_user_login_profile = false
    
      tags = {
        Name             = "tf-assume-user"
        "my:environment" = "my-environment"
      }
    }
    
    ##############
    # IAM Groups #
    ##############
    
    module "tf_assume_group" {
      source  = "terraform-aws-modules/iam/aws//modules/iam-group-with-policies"
      version = "5.9.2"
    
      name                              = "tf-assume-group"
      attach_iam_self_management_policy = false
    
      group_users = [
        module.tf_assume_user.iam_user_name
      ]
    
      custom_group_policy_arns = [
      ]
    
      tags = {
        Name             = "tf-assume-group"
        "my:environment" = "my-environment"
      }
    }
    ```

2. Here is the example of IAM group and IAM user when verified from AWS Web Console. Create new AWS credentials (AWS Access Key ID and AWS Secret Acces Key) that will be used by IAM intermediary user.

    ![tf-assume-group](https://ludesdeveloper.files.wordpress.com/2023/02/1.-tf-assume-group-1.png)

    ![tf-assume-user](https://ludesdeveloper.files.wordpress.com/2023/02/2.-tf-assume-user-1.png)

3. Create IAM role that will assign IAM intermediary user above as trusted entity and will run sts:AssumeRole. Then in this blgpost, I will associate that IAM role with customer managed policy that can provision Amazon Lightsail. Policy that used for provision Lightsail is just for demo purpose so that it is not least privileged, please consider to use least privileged policy in production environment. IAM role and IAM policy will be provisioned using public terraform module as well. Here is the example of code:

    ```
    #############
    # IAM Roles #
    #############
    
    module "compute_admin_role" {
      source  = "terraform-aws-modules/iam/aws//modules/iam-assumable-role"
      version = "5.11.1"
    
      trusted_role_arns = [
        "arn:aws:iam::12digitsawsid:user/tf-assume-user",
      ]
    
      trusted_role_services = [
        "lightsail.amazonaws.com"
      ]
    
      create_role       = true
      role_name_prefix  = "compute-admin-sbx"
      role_requires_mfa = false
    
      custom_role_policy_arns = [
        module.lightsail_full_access.arn
      ]
    }
    
    ################
    # IAM Policies #
    ################
    
    module "lightsail_full_access" {
      source  = "terraform-aws-modules/iam/aws//modules/iam-policy"
      version = "5.11.1"
    
      name        = "lightsail-full-access"
      path        = "/"
      description = "Provides full access to Amazon Lightsail"
      policy      = file("${path.module}/files/policies/lightsail/lightsail-full-access.json")
    
      tags = {
        Name             = "lightsail-full-access"
        "my:environment" = "my-environment"
      }
    }
    ```

4. Here is the example of IAM role when verified from AWS Web Console.

    ![iam-role-summary](https://ludesdeveloper.files.wordpress.com/2023/02/3.-tf-iam-assume-role.png)

    ![iam-role-trusted-entity](https://ludesdeveloper.files.wordpress.com/2023/02/3.1-tf-iam-assume-role-trust-relationship.png)

## Configuration Steps

In this section, I will explain configuration steps that needed for provisioning AWS resource. As mentioned above, I used remote backend or Terraform Cloud. So I will explained a little bit about configuration on Terraform Cloud as well. In this blogpost, I provisioned Amazon Lightsail Instance as example.

1. In Terraform Cloud workspace that used for provision AWS resource, on variables section I associated AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY that used by IAM intermediary user. I referenced those variables using variable sets. Variable sets configuration will not be explained more detail in this blogpost, please visit this [HashiCorp blog](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables/managing-variables#variable-sets) for more details.

    ![tfc-variables](https://ludesdeveloper.files.wordpress.com/2023/02/4.-set-tfc-workspace-varsets.png)

2. Next in Terraform Code, I created file **main.tf**. In this file, I declared terraform configuration that refers to terraform cloud (app.terraform.io) as a remote backend. I also declared the organization and workspace used by Terraform code. Next in this file, I declared the provider used by Terraform code which is Hashicorp/AWS, and versions related to it. Last, I declared the AWS region refer to the variable aws_region and IAM role arn refer to variable role_arn , which both are configured in file variables.tf which I will explain later.

    ```
    ###########################
    # Terraform Configuration #
    ###########################
    
    terraform {
      backend "remote" {
        hostname     = "app.terraform.io"
        organization = "my-organization"
    
        workspaces {
          name = "my-workspace"
        }
      }
    }
    
    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 4.53.0"
        }
      }
    
      required_version = ">= 1.3.0"
    }
    
    provider "aws" {
      region = var.aws_region
    
      assume_role {
        role_arn = var.role_arn
      }
    }
    ```

3. Next, I defined variables used by the Terraform in file **variables.tf**.

    ```
    variable "aws_region" {
      type        = string
      description = "AWS Region"
      default     = "ap-southeast-1"
    }
    
    variable "role_arn" {
      type        = string
      description = "IAM role ARN"
      default     = "arn:aws:iam::12digitsawsid:role/compute-admin-sbx"
    }
    
    variable "az" {
      type        = list(string)
      description = "AWS Availability Zone"
      default     = ["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1c"]
    }
    
    variable "key_pair_name" {
      type        = string
      description = "Name of Lightsail key pair"
      default     = "my-key-pair"
    }
    ```

4. Next I created example code for provisioning Amazon Lightsail Instance.

    ```
    ######################
    # Lightsail Instance #
    ######################
    
    resource "aws_lightsail_instance" "my_instance" {
      name              = "my-instance"
      availability_zone = var.az[0]
      blueprint_id      = "ubuntu_20_04"
      bundle_id         = "micro_2_0"
      key_pair_name     = var.key_pair_name
    
      tags = {
        "Name"           = "my-instance"
        "my:environment" = "my-environment"
        "my:owner"       = "my-owner"
      }
    }
    
    resource "aws_lightsail_instance_public_ports" "my_instance_ports" {
      instance_name = aws_lightsail_instance.my_instance.name
    
      port_info {
        protocol  = "tcp"
        from_port = 22
        to_port   = 22
      }
    
      port_info {
        protocol  = "tcp"
        from_port = 443
        to_port   = 443
      }
    }
    ```

5. Next commit and push terraform codes to the repository. After that run Terraform plan and Terraform apply from Terraform Cloud workspace so that infrastructures or resources can be provisioned. This activity will not be explained in detail and I will only show the simulation. If you are interested in how to configure Terraform Cloud workspace and how to run Terraform plans and apply for the provision of AWS resources, please check out my other blog post [here](https://aws.plainenglish.io/basic-configuration-how-to-provision-amazon-web-services-aws-resources-via-terraform-cl-5e1d6db8b22f).

    ![tfc-plan-apply](https://ludesdeveloper.files.wordpress.com/2023/02/5.-tfc-appy-and-run.png)
  
6. After resource provisioning via Terraform Cloud has been finished, then we can verify resources have been successfully created via the AWS web console. I switched to Lightsail service page and verified that instance has been provisioned.

    ![verify-lightsail-instance](https://ludesdeveloper.files.wordpress.com/2023/02/6.-verify-lightsail-instance.png)

## Conclusion

So We have reached the last section of this article. There are some key takeaways that I want to point out:

- Beside using most common method which is using IAM user that associated with AWS Credentials (AWS Access Key ID and AWS Secret Access Key) and IAM policy, we can provision AWS resource via Terraform using IAM role reference (IAM assume role)
- The idea is We only need to create IAM role with certain privilege and We don’t need create multiple IAM user that need AWS Credentials (AWS Access Key ID and AWS Secret Access Key)
- But by the time this blogpost is released, I found that there is still some limitation with this IAM assume role method. Because We still need IAM user that act as intermediary user and this IAM user need AWS Credentials (AWS Access Key ID and AWS Secret Access Key). Although this IAM user is not associated with any IAM policy at all and just IAM role that associated with IAM policy

Please check my GitHub repository to see source code example used in this blogpost. For IAM resource configuration, please check this [iam](https://github.com/lanandra/mock-aws-infrastructure/tree/master/environment-name/iam) directory and for example how to apply this Terraform assume role configuration, please refer to this [lightsail directory](https://github.com/lanandra/mock-aws-infrastructure/tree/master/environment-name/lightsail/region-name).

Please comment if you have any suggestions, critiques, or thoughts.

Hope this article will benefit you. Thank you!