Import existing resources from the root account into Terraform state
Before applying the account-baseline-root
module to the root account, we need to import existing resources—including
the IAM user you created manually earlier—into Terraform state, so that Terraform manages those resources instead of
trying to create totally new ones. You can do this using the
import
command, which uses the format:
terraform import <ADDRESS> <ID>
Where <ADDRESS>
is the address of the Terraform
resource you’re importing and <ID>
is a resource-specific identifier (e.g., for aws_instance
, it’s the instance ID,
whereas for aws_lb
, it’s the load balancer’s name—check the docs for the resource to find out what to use).
Let’s import the IAM user you created manually in the root account. IAM users are managed using the
aws_iam_user
resource, and the
documentation for that
resource tells us to use the user’s name
as the <ID>
; we’ll assume for this example that your IAM user’s name was
alice
, who is already one of the entries in the users
variable in terragrunt.hcl
. So now we need the <ADDRESS>
.
An easy way to get it is to run plan
:
cd infrastructure-live/root/_global/account-baseline
aws-vault exec root-iam-user -- terragrunt plan
You should get a whole bunch of log output, including something that looks like this:
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
<= read (data resources)
Terraform will perform the following actions:
# ... (omitting lots of log output for simplicity) ...
# module.root_baseline.module.iam_users.aws_iam_user.user["alice"] will be created
+ resource "aws_iam_user" "user" {
+ arn = (known after apply)
+ force_destroy = true
+ id = (known after apply)
+ name = "alice"
+ path = "/"
+ unique_id = (known after apply)
}
# ... (omitting lots of log output for simplicity) ...
Plan: 160 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
This plan
output is telling you that Terraform will create a bunch of resources, including the aws_iam_user
named
alice
. Of course, this user already exists, so we want to import
the user rather than create it again. The text
next to the #
gives you the <ADDRESS>
to use:
# module.root_baseline.module.iam_users.aws_iam_user.user["alice"] will be created
So the <ADDRESS>
you want is module.root_baseline.module.iam_users.aws_iam_user.user["alice"]
. Now, normally, you’d
run import
right away, but due two Terraform bugs, #13018 and
#26211, import
doesn’t work on certain types of modules—namely,
those with nested provider
blocks that use dynamic data—and will produce an error like unknown variable accessed: var.region in:
. One of these bugs has been open for over 3 years, so we built a workaround for it in Terragrunt: the
aws-provider-patch
command.
The idea behind the workaround is to temporarily hard-code the dynamic data in nested provider
blocks. In particular,
we need to temporarily hard-code some of the region
and role_arn
parameters of the nested provider
blocks used by
account-baseline-root
as follows:
terragrunt aws-provider-patch \
--terragrunt-override-attr 'region="eu-west-1"' \
--terragrunt-override-attr 'assume_role.role_arn=""'
Note: You can use any region you want for the region
parameter. It’s just temporary. However, role_arn
must be set
to an empty string or Terraform will complain.
After running this command, you can finally import your IAM user:
aws-vault exec root-iam-user -- terragrunt import \
'module.root_baseline.module.iam_users.aws_iam_user.user["alice"]' \
'alice'
You should see log output that looks something like this:
[terragrunt] 2020/10/13 14:19:16 Running command: terraform import module.root_baseline.module.iam_users.aws_iam_user.user["alice"] alice
module.root_baseline.module.iam_users.aws_iam_user.user["alice"]: Importing from ID "alice"...
module.root_baseline.module.iam_users.aws_iam_user.user["alice"]: Import prepared!
Prepared aws_iam_user for import
module.root_baseline.module.iam_users.aws_iam_user.user["alice"]: Refreshing state... [id=alice]
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.
You’ll now be able to manage that IAM user as code going forward!
If you created other resources manually in the root account, you may want to import
them too, so you can manage
everything as code, and so that Terraform doesn’t try to create any duplicate resources. For example, if you already
manually created an AWS Organization in your root account, you’ll need to import it using a command that looks like
this:
aws-vault exec root-iam-user -- terragrunt import \
'module.root_baseline.module.organization.aws_organizations_organization.root[0]' \
'<ORG_ID>'
Where <ORG_ID>
is the ID of your AWS Organization. Note that this is NOT the same as the AWS account ID, but a
separate ID you can find by going to the AWS Organizations page in
the AWS console, clicking on your root account (the one with a star to the left of it), and looking at the root
account’s ARN, which will look something like, arn:aws:organizations::<ACCOUNT_ID>:account/<ORG_ID>/<ACCOUNT_ID>
. The
<ORG_ID>
is the part between slashes, and it’ll look something like o-a2lce3bbqq
.
You may also want to import child accounts you created manually. You’ll need to add each of these to the
child_accounts
variable in terragrunt.hcl
, and you can then import each one as follows:
aws-vault exec root-iam-user -- terragrunt import \
'module.root_baseline.module.organization.aws_organizations_account.child_accounts["<ACCOUNT_NAME>"]' \
'<ACCOUNT_ID>'
Where <ACCOUNT_NAME>
is the name you used for the account in the child_accounts
variable and <ACCOUNT_ID>
is the
12-digit ID of that AWS account.
Once you’re done importing, you’ll want to undo the aws-provider-patch
workaround. The easiest way to do that is to
delete the .terraform
or .terragrunt-cache
folders to remove any locally cached modules, as they would’ve been
modified by the aws-provider-patch
command.
rm -rf .terragrunt-cache