Skip to content

Commit f00b102

Browse files
authored
add docs (#16)
1 parent ace48ed commit f00b102

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed

.github/terraform-backend.drawio.png

61.1 KB
Loading

README.md

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,81 @@
77
[![pre-commit-check](https://github.com/rhythmictech/terraform-aws-backend/workflows/pre-commit-check/badge.svg?branch=master&event=push)](https://github.com/rhythmictech/terraform-aws-backend/actions?query=workflow%3Apre-commit-check+event%3Apush+branch%3Amaster)
88
<a href="https://twitter.com/intent/follow?screen_name=RhythmicTech"><img src="https://img.shields.io/twitter/follow/RhythmicTech?style=social&logo=twitter" alt="follow on Twitter"></a>
99

10-
Creates a backend S3 bucket and DynamoDB table for managing Terraform state. Note that when bootstrapping a new environment, it is typically easier to use a separate method for creating the bucket and lock table. This module is intended to create a backend in an AWS account that is already Terraform-managed. This is useful to store the state for other accounts externally, which is always preferred.
10+
Creates an S3 bucket and DynamoDB table for managing Terraform state. Note that when bootstrapping a new environment, it is typically easier to use a separate method for creating the bucket and lock table, like [a CloudFormation Stack](https://github.com/rhythmictech/AWS-CFN-Terraform-Bootstrap). This module is intended to create a backend in an AWS account that is already Terraform-managed. This is useful to store the state for other accounts externally.
11+
12+
This module will create a CloudFormation stack and an optional wrapper script to deploy it. This stack is suitable to run in any account that will store its Terraform state in the bucket created by this module. It creates an IAM role with the AdministratorAccess policy attached and with an External ID which can then be assumed by terraform to create resources in the child account(s).
13+
14+
![visualization](.github/terraform-backend.drawio.png)
1115

1216
*Breaking Changes*
1317

1418
Previous versions of this module had support for cross-account management in a way that proved awkward for many uses cases and made it more difficult than it should've to fully secure the tfstate between accounts. Version 4.x and later eliminates support for this and refocuses the module on using centralized tfstate buckets with cross-account role assumption for execution of terraform. As a result, many variable names have changed and functionality has been dropped. Upgrade to this version at your own peril.
1519

16-
## Usage
20+
## Multi-Account Usage
21+
These instructions assume two AWS accounts; a "Parent" account which holds the terraform state and IAM users, and a "Child" account.
22+
23+
1) In the parent account create this module. The below code is a serving suggestion.
1724
```
1825
module "backend" {
19-
source = "rhythmictech/backend/aws"
20-
21-
bucket = "project-tfstate"
22-
region = "us-east-1"
23-
table = "tf-locktable"
26+
source = "rhythmictech/backend/aws"
27+
version = "4.1.0"
28+
29+
bucket_name = "${local.account_id}-${var.region}-terraform-state"
30+
create_assumerole_template = true
31+
logging_target_bucket = module.s3logging-bucket.s3_bucket_name
32+
logging_target_prefix = "${local.account_id}-${var.region}-tf-state"
33+
tags = module.tags.tags_no_name
2434
}
2535
```
2636

37+
It will create a folder with a shell script and a CloudFormation stack in it.
38+
39+
2) Log into the child account and run the shell script, `assumerole/addrole.sh`. This will create a CloudFormation stack in that child account.
40+
41+
3) In the terraform code for the child account create the provider and backend sections like below, substituting `PARENT_ACCT_ID` and `PARENT_REGION`, `CHILD_ACCT_ID`, AND `EXTERNAL_ID`.
42+
43+
terraform backend config:
44+
```
45+
bucket = "PARENT_ACCT_ID-PARENT_REGION-terraform-state"
46+
dynamodb_table = "tf-locktable"
47+
key = "account.tfstate"
48+
region = "PARENT_REGION"
49+
```
50+
51+
provider config:
52+
```
53+
provider "aws" {
54+
assume_role {
55+
role_arn = "arn:aws:iam::CHILD_ACCT_ID:role/Terraform"
56+
session_name = "terraform-network"
57+
external_id = "EXTERNAL_ID"
58+
}
59+
}
60+
```
61+
62+
4) Log in to the master account and run terraform using this backend and provider config. The state will be stored in the parent account but terraform will assume the child account role.
63+
2764
## Cross Account State Management
28-
To use this bucket to manage the state for other AWS accounts, you must create IAM roles in those accounts and allow the users who run Terraform to assume them.
2965

3066
See [Use AssumeRole to Provision AWS Resources Across Accounts](https://learn.hashicorp.com/tutorials/terraform/aws-assumerole) for more information on this pattern.
3167

32-
This module is not intended to hold the state for the account in which it is created. If the account itself is also Terraform managed, it is recommended to create a separate bucket for its own state manually or via a different IaC method (e.g., CloudFormation).
68+
This module is not intended to hold the state for the account in which it is created. If the account itself is also Terraform managed, it is recommended to create a separate bucket for its own state manually or via a different IaC method (e.g., CloudFormation) to avoid the chicken-and-egg problem. See [this CloudFormation template](https://github.com/rhythmictech/AWS-CFN-Terraform-Bootstrap) to create terraform backend for this or any other single account.
69+
70+
You can test the ability to assume a role in the child account by logging in with the parent account and running this
71+
```
72+
73+
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \
74+
$(aws sts assume-role \
75+
--external-id EXTERNAL_ID \
76+
--role-arn arn:aws:iam::CHILD_ACCT_ID:role/Terraform \
77+
--role-session-name testme \
78+
--query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]" \
79+
--output text))%
80+
81+
AWS_SECURITY_TOKEN=
82+
```
83+
Then `aws sts get-caller-identity` should reveal you to be in the child account.
3384

34-
This module will create a CloudFormation stack and an optional wrapper script to deploy it. This stack is suitable to run in any account that will store its Terraform state in this backend. It creates an IAM role with the AdministratorAccess policy attached and with an External ID.
3585

3686
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
3787
## Requirements
@@ -102,7 +152,9 @@ No modules.
102152

103153
| Name | Description |
104154
|------|-------------|
155+
| <a name="output_backend_config_stub"></a> [backend\_config\_stub](#output\_backend\_config\_stub) | Backend config stub to be used in child account(s) |
105156
| <a name="output_external_id"></a> [external\_id](#output\_external\_id) | External ID attached to IAM role in managed accounts |
106157
| <a name="output_kms_key_arn"></a> [kms\_key\_arn](#output\_kms\_key\_arn) | ARN of KMS Key for S3 bucket |
158+
| <a name="output_provider_config_stub"></a> [provider\_config\_stub](#output\_provider\_config\_stub) | Provider config stub to be used in child account(s) |
107159
| <a name="output_s3_bucket_backend"></a> [s3\_bucket\_backend](#output\_s3\_bucket\_backend) | S3 bucket used to store TF state |
108160
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

outputs.tf

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
output "external_id" {
23
description = "External ID attached to IAM role in managed accounts"
34
value = local.external_id
@@ -12,3 +13,29 @@ output "s3_bucket_backend" {
1213
description = "S3 bucket used to store TF state"
1314
value = aws_s3_bucket.this.bucket
1415
}
16+
17+
##########################################
18+
# stubs
19+
##########################################
20+
21+
output "backend_config_stub" {
22+
description = "Backend config stub to be used in child account(s)"
23+
value = <<EOF
24+
bucket = "${aws_s3_bucket.this.bucket}"
25+
dynamodb_table = "${aws_dynamodb_table.this.name}"
26+
region = "${local.region}"
27+
EOF
28+
}
29+
30+
output "provider_config_stub" {
31+
description = "Provider config stub to be used in child account(s)"
32+
value = <<EOF
33+
provider "aws" {
34+
assume_role {
35+
role_arn = "arn:aws:iam::REPLACE_WITH_CHILD_ACCT_ID:role/${var.assumerole_role_name}"
36+
session_name = "terraform-network"
37+
external_id = "${local.external_id}"
38+
}
39+
}
40+
EOF
41+
}

0 commit comments

Comments
 (0)