Importing existing AWS IAM resources to Terraform

rinaxsumomo

rinaxsumomo

Posted on April 25, 2024

Importing existing AWS IAM resources to Terraform

Introduction

My project team created some AWS resources manually, but they wanted to bring this environment to another account. So here's Terraform. Using terraform import command, you can import existing resources to have Terraform manage.
In a nutshell, it's about updating tfstate file to reflect existing resources by this command, then reflecting the resources to main.tf (or other .tf files) to remove the differences between Terraform configuration and the real environment.

Environment

  • AWS
  • Terraform v1.7.5
  • Terraform Backend set to S3

Requirements

  • Basic knowledge of AWS
  • Basic knowledge of Terraform
  • IAM credentials that allow creating resources.

Terraform Directory design

Since we had different environment such as production, test, development, the Terraform directory is designed as below.
Also, we wanted to re-use codes, so we divided modules as below. VPC and IAM modules were developed.
There is iam.tf in /environment/test directory. This is the tf file to create IAM resource, developed in this post. This will be copied to the other environments as well.

.
├── environments
│ ├── dev
│ │ ├── main.tf
│ │ ├── terraform.tf
│ │ ├── terraform.tfvars
│ │ └── variables.tf
│ ├── test
│ │ ├── iam.tf
│ │ ├── main.tf
│ │ ├── output.tf
│ │ ├── terraform.tf
│ │ ├── terraform.tfvars
│ │ └── variables.tf
│ ├── prod
│ │ ├── main.tf
│ │ ├── terraform.tf
│ │ ├── terraform.tfvars
│ │ └── variables.tf
├── modules
├── vpc
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
└── iam
├── policy
│ ├── main.tf
│ ├── output.tf
│ ├── policies
│ │ └── xxx_allow_policy.json
│ ├── terraform.tf
│ └── variables.tf
└── role
├── main.tf
├── output.tf
├── roles
│ ├── ec2.json
│ ├── lambda.json
│ └── s3.json
├── terraform.tf
└── variables.tf

Import

Add resource block to .tf file

First, you need to add empty resource blocks for your imported resources in main.tf.
In this case, I've added a new file, iam.tf acting as main.tf when running terraform apply command, and main.tf for IAM module called from iam.tf.
iam.tf is separated from main.tf in the environment directory for some operational reasons.
Below are the snippets.

# iam.tf in environment directory
module "iam_policy" {
  source = "../../modules/iam/policy"
}

module "iam_role" {
  source = "../../modules/iam/role"
}
Enter fullscreen mode Exit fullscreen mode

You don't have to add arguments such as name or policy inside the block for now.

# main.tf in IAM Policy module directory
resource "aws_iam_policy" "test_policy" {
}
Enter fullscreen mode Exit fullscreen mode
# main.tf in IAM Role module directory
resource "aws_iam_role" "test_role" {
}
Enter fullscreen mode Exit fullscreen mode

Import IAM Policy

Here is the command and the result example.

$ terraform import module.iam_policy.aws_iam_policy.test_policy arn:aws:iam::1234567890:policy/test-policy
xxx...
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
Enter fullscreen mode Exit fullscreen mode

Import IAM Role

Here is the command and the result example.

$ terraform import module.iam_role.aws_iam_role.test_role Test-Role
xxx...
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
Enter fullscreen mode Exit fullscreen mode

Check the tfstate file

You should see that your imported resource is added in the tfstate file like below.

{
  "module": "module.iam_policy",
  "mode": "managed",
  "type": "aws_iam_policy",
  "name": "test_policy",
  "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
  "instances": [
    {
      "schema_version": 0,
      "attributes": {
        xxxx
      },
      "sensitive_attributes": [],
      "private": "xxx"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Update main.tf in IAM Policy and Role module directory.

Now using the information added in the tfstate file, you need to add arguments in the resource blocks like below.
If you use JSON file for policies, you need to place them accordingly.

# main.tf in IAM Policy module directory
resource "aws_iam_policy" "test_policy" {
  name = "test-policy"
  policy = file("${path.module}/policies/xxx_allow_policy.json")
}
Enter fullscreen mode Exit fullscreen mode
# main.tf in IAM Role module directory
resource "aws_iam_role" "Test_Role" {
  name   = "Test-Role"
  assume_role_policy = file("${path.module}/roles/s3.json")
  managed_policy_arns = ["arn:aws:iam::1234567890:policy/test-policy"]
}
Enter fullscreen mode Exit fullscreen mode

Plan to check differences

Let's check if there are differences between Terraform configuration (imported ones) and the real resources in AWS. If you find differences (a kind of conflicts you might say), you need to resolve them. In this case, there are 6 differences as below. If the change was subtle, such as adding tags, you might apply this configuration change to the real resources, or you might modify other settings to resolve.

$ terraform plan
xxx...
Plan: 0 to add, 6 to change, 0 to destroy.

Enter fullscreen mode Exit fullscreen mode

Note

  1. If you failed to add resource blocks before importing, you should see an error as below.
Error: resource address "aws_iam_policy.test_policy" does not exist in the configuration.

Before importing this resource, please create its configuration in the root module. For example:

resource "aws_iam_policy" "test_policy" {
  # (resource arguments)
}
Enter fullscreen mode Exit fullscreen mode
  1. The arguments for importing IAM Policy and IAM Role are different. The former is ARN and the latter is Name. I wonder why indeed. If you used ARN by mistake to import IAM Role, the command would return Invalid error as below, which can be confusing.
│ Error: reading IAM Role (arn:aws:iam::1234567890:role/test-role): operation error IAM: GetRole, https response error StatusCode: 400, RequestID: 12345678-aaaa-bb12-cc34-1234567890, api error ValidationError: The specified value for roleName is invalid. It must contain only alphanumeric characters and/or the following: +=,.@_-
Enter fullscreen mode Exit fullscreen mode

READ THE DOCUMENT!

  1. If you imported wrong resources, you can remove them from Terraform state quickly as below. (This won't delete the real resources in AWS, no worry.)

Let's check if this would remove as you intend with dry-run.

$ terraform state rm --dry-run module.wrongmodule.aws_iam_policy.wrong_policy
Would remove module.wrongmodule.aws_iam_policy.wrong_policy
Enter fullscreen mode Exit fullscreen mode

Then let's remove.

$ terraform state rm module.wrongmodule.aws_iam_policy.wrong_policy
Removed module.wrongmodule.aws_iam_policy.wrong_policy
Successfully removed 1 resource instance(s).
Enter fullscreen mode Exit fullscreen mode

Conclusion

OK, I've imported all the IAM resources to Terraform configuration with some try&error. It's really important to read Terraform official document in detail..
Hoping this post would help.

References

💖 💪 🙅 🚩
rinaxsumomo
rinaxsumomo

Posted on April 25, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related