Skip to content

Commit 1edda49

Browse files
authored
Merge pull request #366 from gruntwork-io/lz-import
Update landing zone deployment guide with instructions on how to import existing resources
2 parents acec1cf + 407916f commit 1edda49

File tree

1 file changed

+218
-19
lines changed

1 file changed

+218
-19
lines changed

_posts/2019-08-12-how-to-configure-production-grade-aws-account-structure.adoc

Lines changed: 218 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -983,13 +983,13 @@ Enable MFA::
983983
https://www.theverge.com/2017/9/18/16328172/sms-two-factor-authentication-hack-password-bitcoin[vulnerabilities with the cellular system],
984984
so using a virtual or hardware MFA device is preferable; that said, MFA with SMS is still better than no MFA at all.
985985

986-
=== Apply the security baseline to the root account
986+
=== Configure the security baseline for the root account
987987

988-
Next, we'll apply a security baseline to the root account that is responsible for creating all the child accounts.
988+
Next, we'll configure a security baseline for the root account that is responsible for creating all the child accounts.
989989
It will also configure AWS Organizations, IAM Roles, IAM Users, IAM Groups, IAM Password Policies, Amazon GuardDuty,
990990
AWS CloudTrail and AWS Config.
991991

992-
Let's first apply the security baseline by using the `account-baseline-root` module from https://github.com/gruntwork-io/module-security[module-security].
992+
We'll be using the `account-baseline-root` module from https://github.com/gruntwork-io/module-security[module-security].
993993

994994
[.exceptional]
995995
IMPORTANT: You must be a [js-subscribe-cta]#Gruntwork subscriber# to access `module-security`.
@@ -1360,9 +1360,20 @@ repo, setting the `ref` param to the version you released earlier:
13601360
----
13611361
terraform {
13621362
source = "git@github.com/<YOUR_ORG>/infrastructure-modules.git//landingzone/account-baseline-root?ref=v0.3.0"
1363+
1364+
# This module deploys some resources (e.g., AWS Config) across all AWS regions, each of which needs its own provider,
1365+
# which in Terraform means a separate process. To avoid all these processes thrashing the CPU, which leads to network
1366+
# connectivity issues, we limit the parallelism here.
1367+
extra_arguments "parallelism" {
1368+
commands = get_terraform_commands_that_need_parallelism()
1369+
arguments = ["-parallelism=2"]
1370+
}
13631371
}
13641372
----
13651373

1374+
[.exceptional]
1375+
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`), as shown above, with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
1376+
13661377
Set the variables for the `account-baseline-root` module in this environment in the `inputs = { ... }` block of `terragrunt.hcl`:
13671378

13681379
.infrastructure-live/root/_global/account-baseline/terragrunt.hcl
@@ -1376,7 +1387,7 @@ inputs = {
13761387
# Prefix all resources with this name
13771388
name_prefix = "<COMPANY_NAME>-root"
13781389
1379-
# If you've already created an AWS Organization in your root account, set this variable to false
1390+
# If you've already created an AWS Organization in your root account, you'll be able to import it later in this guide
13801391
create_organization = true
13811392
13821393
# The child AWS accounts to create in this AWS organization
@@ -1406,6 +1417,9 @@ inputs = {
14061417
14071418
# The IAM users to create in this account. Since this is the root account, you should only create IAM users for a
14081419
# small handful of trusted admins.
1420+
#
1421+
# NOTE: Make sure to include the IAM user you created manually here! We'll import the user into Terraform state in
1422+
# the next step of this guide, allowing you to manage this user as code going forward.
14091423
users = {
14101424
alice = {
14111425
groups = ["full-access"]
@@ -1501,16 +1515,176 @@ You should get JSON output with information about your IAM user:
15011515
}
15021516
----
15031517

1504-
You're now ready to deploy the `account-baseline` module in the root account by running `terragrunt apply`:
1518+
You're now almost ready to deploy the `account-baseline` module in the root account. But first, you may need to import
1519+
some existing resources.
1520+
1521+
1522+
=== Import existing resources from the root account
1523+
1524+
Before applying the security baseline to the root account, we need to import any existing resources—including the IAM
1525+
user you created manually earlier—into Terraform state, so that Terraform manages those existing resources instead of
1526+
trying to create totally new ones. You can do this using the
1527+
https://www.terraform.io/docs/import/index.html[`import` command], which uses the format:
1528+
1529+
[source,bash]
1530+
----
1531+
terraform import <ADDRESS> <ID>
1532+
----
1533+
1534+
Where `<ADDRESS>` is the https://www.terraform.io/docs/internals/resource-addressing.html[address] of the Terraform
1535+
resource you're importing and `<ID>` is a resource-specific identifier (e.g., for `aws_instance`, it's the instance ID,
1536+
whereas for `aws_lb`, it's the load balancer's name—check the docs for the resource to find out what to use).
1537+
1538+
As a first example, let's import the IAM user you created manually in the root account. IAM users are managed using the
1539+
`aws_iam_user` resource, and the
1540+
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user#import[documentation for that
1541+
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
1542+
`alice`, who is already one of the entries in the `users` variable in `terragrunt.hcl`. So now we need the `<ADDRESS>`.
1543+
An easy way to get it is to run `plan`:
15051544

15061545
[source,bash]
15071546
----
15081547
cd infrastructure-live/root/_global/account-baseline
1509-
aws-vault exec root-iam-user -- terragrunt apply -parallelism=2
1548+
aws-vault exec root-iam-user -- terragrunt plan
15101549
----
15111550

1512-
[.exceptional]
1513-
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`) with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
1551+
You should get a whole bunch of log output, including something that looks like this:
1552+
1553+
----
1554+
------------------------------------------------------------------------
1555+
1556+
An execution plan has been generated and is shown below.
1557+
Resource actions are indicated with the following symbols:
1558+
+ create
1559+
<= read (data resources)
1560+
1561+
Terraform will perform the following actions:
1562+
1563+
# ... (ommitting lots of log output for simplicity) ...
1564+
1565+
# module.root_baseline.module.iam_users.aws_iam_user.user["alice"] will be created
1566+
+ resource "aws_iam_user" "user" {
1567+
+ arn = (known after apply)
1568+
+ force_destroy = true
1569+
+ id = (known after apply)
1570+
+ name = "alice"
1571+
+ path = "/"
1572+
+ unique_id = (known after apply)
1573+
}
1574+
1575+
# ... (ommitting lots of log output for simplicity) ...
1576+
1577+
Plan: 160 to add, 0 to change, 0 to destroy.
1578+
1579+
------------------------------------------------------------------------
1580+
1581+
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
1582+
can't guarantee that exactly these actions will be performed if
1583+
"terraform apply" is subsequently run.
1584+
----
1585+
1586+
This `plan` output is telling you that Terraform will create a bunch of resources, including the `aws_iam_user` named
1587+
`alice`. Of course, this user already exists, so we want to `import` the user rather than create it again. The text
1588+
next to the `#` gives you the `<ADDRESS>` to use:
1589+
1590+
----
1591+
# module.root_baseline.module.iam_users.aws_iam_user.user["alice"] will be created
1592+
----
1593+
1594+
So the `<ADDRESS>` you want is `module.root_baseline.module.iam_users.aws_iam_user.user["alice"]`. Now, normally, you'd
1595+
run `import` right away, but due to a https://github.com/hashicorp/terraform/issues/13018[Terraform bug], `import`
1596+
doesn't work on certain types of modules—namely, those with nested `provider` blocks that use dynamic data—and will
1597+
produce an error like `unknown variable accessed: var.region in:`. This issue has been open for over 3 years, so we
1598+
built a workaround for it in Terragrunt: the
1599+
https://terragrunt.gruntwork.io/docs/reference/cli-options/#aws-provider-patch[`aws-provider-patch` command].
1600+
1601+
The idea behind the workaround is to temporarily hard-code the dynamic data in nested `provider` blocks. In particular,
1602+
we need to temporarily hard-code some of the `region` parameters of the nested `provider` blocks used by
1603+
`account-baseline-root` as follows:
1604+
1605+
[source,bash]
1606+
----
1607+
terragrunt aws-provider-patch --terragrunt-override-attr region=eu-west-1
1608+
----
1609+
1610+
_Note: You can use any region you want for the region parameter. It's just temporary._
1611+
1612+
After running this command, you can finally import your IAM user:
1613+
1614+
[source,bash]
1615+
----
1616+
aws-vault exec root-iam-user -- terragrunt import \
1617+
'module.root_baseline.module.iam_users.aws_iam_user.user["alice"]' \
1618+
'alice'
1619+
----
1620+
1621+
You should see log output that looks something like this:
1622+
1623+
----
1624+
[terragrunt] 2020/10/13 14:19:16 Running command: terraform import module.root_baseline.module.iam_users.aws_iam_user.user["alice"] alice
1625+
module.root_baseline.module.iam_users.aws_iam_user.user["alice"]: Importing from ID "alice"...
1626+
module.root_baseline.module.iam_users.aws_iam_user.user["alice"]: Import prepared!
1627+
Prepared aws_iam_user for import
1628+
module.root_baseline.module.iam_users.aws_iam_user.user["alice"]: Refreshing state... [id=alice]
1629+
1630+
Import successful!
1631+
1632+
The resources that were imported are shown above. These resources are now in
1633+
your Terraform state and will henceforth be managed by Terraform.
1634+
----
1635+
1636+
You'll now be able to manage that IAM user as code going forward!
1637+
1638+
If you created other resources manually in the root account, you may want to `import` them too, so you can manage
1639+
everything as code, and so that Terraform doesn't try to create any duplicate resources. For example, if you already
1640+
manually created an AWS Organization in your root account, you'll need to import it using a command that looks like
1641+
this:
1642+
1643+
[source,bash]
1644+
----
1645+
aws-vault exec root-iam-user -- terragrunt import \
1646+
'module.root_baseline.module.organization.aws_organizations_organization.root[0]' \
1647+
'<ORG_ID>'
1648+
----
1649+
1650+
Where `<ORG_ID>` is the ID of your AWS Organization. Note that this is NOT the same as the AWS account ID, but a
1651+
separate ID you can find by going to the https://console.aws.amazon.com/organizations/home[AWS Organizations] page in
1652+
the AWS console, clicking on your root account (the one with a star to the left of it), and looking at the root
1653+
account's ARN, which will look something like, `arn:aws:organizations::<ACCOUNT_ID>:account/<ORG_ID>/<ACCOUNT_ID>`. The
1654+
`<ORG_ID>` is the part between slashes, and it'll look something like `o-a2lce3bbqq`.
1655+
1656+
You may also want to import child accounts you created manually. You'll need to add each of these to the
1657+
`child_accounts` variable in `terragrunt.hcl`, and you can then import each one as follows:
1658+
1659+
[source,bash]
1660+
----
1661+
aws-vault exec root-iam-user -- terragrunt import \
1662+
'module.root_baseline.module.organization.aws_organizations_account.child_accounts["<ACCOUNT_NAME>"]' \
1663+
'<ACCOUNT_ID>'
1664+
----
1665+
1666+
Where `<ACCOUNT_NAME>` is the name you used for the account in the `child_accounts` variable and `<ACCOUNT_ID>` is the
1667+
12-digit ID of that AWS account.
1668+
1669+
Once you're done importing, you'll want to undo the `aws-provider-patch` workaround. The easiest way to do that is to
1670+
delete the `.terraform` or `.terragrunt-cache` folders to remove any locally cached modules, as they would've been
1671+
modified by the `aws-provider-patch` command.
1672+
1673+
[source,bash]
1674+
----
1675+
rm -rf .terragrunt-cache
1676+
----
1677+
1678+
=== Apply the security baseline to the root account
1679+
1680+
You're now ready to apply the security baseline to the root account. You should be authenticated as the same IAM user
1681+
in the root account as in the previous two sections. To apply the security baseline, you run `terragrunt apply`:
1682+
1683+
[source,bash]
1684+
----
1685+
cd infrastructure-live/root/_global/account-baseline
1686+
aws-vault exec root-iam-user -- terragrunt apply
1687+
----
15141688

15151689
[.exceptional]
15161690
IMPORTANT: On some operating systems, such as MacOS, you may also need to increase your open files limit to avoid "pipe: too many open files" errors by running: `ulimit -n 1024`.
@@ -1837,9 +2011,20 @@ repo, setting the `ref` param to the version you released earlier:
18372011
----
18382012
terraform {
18392013
source = "git@github.com/<YOUR_ORG>/infrastructure-modules.git//landingzone/account-baseline-app?ref=v0.3.1"
2014+
2015+
# This module deploys some resources (e.g., AWS Config) across all AWS regions, each of which needs its own provider,
2016+
# which in Terraform means a separate process. To avoid all these processes thrashing the CPU, which leads to network
2017+
# connectivity issues, we limit the parallelism here.
2018+
extra_arguments "parallelism" {
2019+
commands = get_terraform_commands_that_need_parallelism()
2020+
arguments = ["-parallelism=2"]
2021+
}
18402022
}
18412023
----
18422024

2025+
[.exceptional]
2026+
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`), as shown above, with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
2027+
18432028
Set the variables for the `account-baseline-app` module in this environment in the `inputs = { ... }` block of `terragrunt.hcl`:
18442029

18452030
.infrastructure-live/logs/_global/account-baseline/terragrunt.hcl
@@ -1936,12 +2121,10 @@ You're now ready to deploy the `account-baseline` module in the logs account by
19362121
[source,bash]
19372122
----
19382123
cd infrastructure-live/logs/_global/account-baseline
1939-
aws-vault exec logs-from-root -- terragrunt apply -parallelism=2
2124+
aws-vault exec logs-from-root -- terragrunt apply
19402125
----
19412126

19422127
[.exceptional]
1943-
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`) with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
1944-
19452128
IMPORTANT: On some operating systems, such as MacOS, you may also need to increase your open files limit to avoid "pipe: too many open files" errors by running: `ulimit -n 1024`.
19462129

19472130
=== Apply the security baseline to the security account
@@ -2376,9 +2559,20 @@ repo, setting the `ref` param to the version you released earlier:
23762559
----
23772560
terraform {
23782561
source = "git@github.com/<YOUR_ORG>/infrastructure-modules.git//landingzone/account-baseline-security?ref=v0.3.2"
2562+
2563+
# This module deploys some resources (e.g., AWS Config) across all AWS regions, each of which needs its own provider,
2564+
# which in Terraform means a separate process. To avoid all these processes thrashing the CPU, which leads to network
2565+
# connectivity issues, we limit the parallelism here.
2566+
extra_arguments "parallelism" {
2567+
commands = get_terraform_commands_that_need_parallelism()
2568+
arguments = ["-parallelism=2"]
2569+
}
23792570
}
23802571
----
23812572

2573+
[.exceptional]
2574+
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`), as shown above, with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
2575+
23822576
Set the variables for the `account-baseline-security` module in this environment in the `inputs = { ... }` block of `terragrunt.hcl`:
23832577

23842578
.infrastructure-live/security/_global/account-baseline/terragrunt.hcl
@@ -2534,12 +2728,9 @@ You're now ready to deploy the `account-baseline` module in the security account
25342728
[source,bash]
25352729
----
25362730
cd infrastructure-live/security/_global/account-baseline
2537-
aws-vault exec security-from-root -- terragrunt apply -parallelism=2
2731+
aws-vault exec security-from-root -- terragrunt apply
25382732
----
25392733

2540-
[.exceptional]
2541-
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`) with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
2542-
25432734
[.exceptional]
25442735
IMPORTANT: On some operating systems, such as MacOS, you may also need to increase your open files limit to avoid "pipe: too many open files" errors by running: `ulimit -n 1024`.
25452736

@@ -2586,9 +2777,20 @@ repo, setting the `ref` param to the latest version:
25862777
----
25872778
terraform {
25882779
source = "git@github.com/<YOUR_ORG>/infrastructure-modules.git//landingzone/account-baseline-app?ref=v0.3.2"
2780+
2781+
# This module deploys some resources (e.g., AWS Config) across all AWS regions, each of which needs its own provider,
2782+
# which in Terraform means a separate process. To avoid all these processes thrashing the CPU, which leads to network
2783+
# connectivity issues, we limit the parallelism here.
2784+
extra_arguments "parallelism" {
2785+
commands = get_terraform_commands_that_need_parallelism()
2786+
arguments = ["-parallelism=2"]
2787+
}
25892788
}
25902789
----
25912790

2791+
[.exceptional]
2792+
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`), as shown above, with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
2793+
25922794
Set the variables for the `account-baseline-app` module in this environment in the `inputs = { ... }` block of `terragrunt.hcl`:
25932795

25942796
.infrastructure-live/stage/_global/account-baseline/terragrunt.hcl
@@ -2693,12 +2895,9 @@ You're now ready to deploy the `account-baseline` module in the stage account by
26932895
[source,bash]
26942896
----
26952897
cd infrastructure-live/stage/_global/account-baseline
2696-
aws-vault exec stage-from-root -- terragrunt apply -parallelism=2
2898+
aws-vault exec stage-from-root -- terragrunt apply
26972899
----
26982900

2699-
[.exceptional]
2700-
IMPORTANT: We **strongly** recommend setting Terraform parallelism to a low value (e.g., `-parallelism=2`) with the `account-baseline-xxx` modules. This is because these modules deploy multi-region resources (e.g., GuardDuty, AWS Config, etc), and for each region, Terraform spins up a separate process, so if you don't limit the parallelism, it may peg all your CPU cores and lead to network connectivity errors.
2701-
27022901
[.exceptional]
27032902
IMPORTANT: On some operating systems, such as MacOS, you may also need to increase your open files limit to avoid "pipe: too many open files" errors by running: `ulimit -n 1024`.
27042903

0 commit comments

Comments
 (0)