tfctl 1.0.0.rc1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -4
- data/.travis.yml +13 -12
- data/CHANGELOG.adoc +23 -1
- data/README.adoc +43 -28
- data/bin/tfctl +3 -6
- data/docs/configuration.adoc +45 -11
- data/docs/control_tower.adoc +75 -37
- data/docs/creating_a_profile.adoc +32 -10
- data/docs/iam_permissions.adoc +23 -3
- data/docs/project_layout.adoc +32 -6
- data/examples/control_tower/{conf/example.yaml → tfctl.yaml} +0 -0
- data/lib/tfctl/aws_org.rb +17 -15
- data/lib/tfctl/config.rb +1 -2
- data/lib/tfctl/schema.rb +2 -2
- data/lib/tfctl/version.rb +1 -1
- data/tfctl.gemspec +6 -4
- metadata +15 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1825dfe2e3683c1ef8444460e5cd7baedde7424ec60de056a7a3351e69ad380
|
4
|
+
data.tar.gz: db929e7c2323d42c99719610e18bf5ef17277521ff69985b668e6f26ca71feea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7288eadc3e15802584060d055a2dd51fa34aae5fe0fba8d4eefd69d3e654fa0bcdef3c05b9a410891f31b4b3f9564301350d6298f7fcdbdf175deef9ac236a74
|
7
|
+
data.tar.gz: c9515472d66fb6d6f423aaf2d01504901a2e42acf47aa6d59919d7737dc37f2817436e5364241afca523cd70cccf2082d0e791573aabb9ff55b3da42fcd760c2
|
data/.rubocop.yml
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
---
|
2
2
|
AllCops:
|
3
|
-
TargetRubyVersion: 2.
|
3
|
+
TargetRubyVersion: 2.5
|
4
4
|
DisplayCopNames: true
|
5
5
|
|
6
6
|
Layout/IndentationWidth:
|
7
7
|
Width: 4
|
8
8
|
|
9
|
-
Layout/
|
9
|
+
Layout/HeredocIndentation:
|
10
10
|
Enabled: false
|
11
11
|
|
12
12
|
Layout/EmptyLines:
|
@@ -15,7 +15,7 @@ Layout/EmptyLines:
|
|
15
15
|
Layout/EmptyLinesAroundMethodBody:
|
16
16
|
Enabled: false
|
17
17
|
|
18
|
-
Layout/
|
18
|
+
Layout/HashAlignment:
|
19
19
|
EnforcedHashRocketStyle:
|
20
20
|
- table
|
21
21
|
EnforcedColonStyle:
|
@@ -45,7 +45,7 @@ Metrics/BlockLength:
|
|
45
45
|
Metrics/MethodLength:
|
46
46
|
Enabled: false
|
47
47
|
|
48
|
-
|
48
|
+
Layout/LineLength:
|
49
49
|
Max: 140
|
50
50
|
|
51
51
|
Metrics/AbcSize:
|
@@ -77,3 +77,7 @@ Style/TrailingCommaInHashLiteral:
|
|
77
77
|
|
78
78
|
Style/RedundantReturn:
|
79
79
|
Enabled: false
|
80
|
+
|
81
|
+
# don't break older Rubies just because of style
|
82
|
+
Style/RedundantBegin:
|
83
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
rvm:
|
2
|
-
|
3
|
-
|
2
|
+
- 2.5
|
3
|
+
- 2.6
|
4
|
+
- 2.7
|
4
5
|
os: linux
|
5
6
|
language: ruby
|
6
7
|
script: make test
|
7
8
|
jobs:
|
8
9
|
include:
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
- stage: Gem release
|
11
|
+
rvm: 2.6
|
12
|
+
deploy:
|
13
|
+
provider: rubygems
|
14
|
+
api_key:
|
15
|
+
secure: LAVcdER+LtQ2TSUrVOY7Be1BC7GXJRD0QBt386vRM5Nld5QaD9Ow9gtN6FprzkzloI4R8BkPWqZbAT6YjC+C0AFB5HK6iPwD2bLsiF9w3ccDD+yrW99RHxiErpmYMun2PqZv0WkJ/pkEplPCKMRFv7SM7W9DMRlU7dsXc1v6IVyIb5u3A04jErS2jXXKY0ijlCDJYVo8zzYL6yUmUcXhc//3CIVnu2Miu6Qr8h7e6jMXNUWfMkwEXsFP9id4TsCz7hRY+39PkiBAknHTN5UqjjJiEOknZnHeTBcVPvi2h2xv+fFLSzVTxlxaRsVoMCShQp5D12qzhQObRJsRVQFs8Yyg9IYMyPdxssFYyUZFaAy5taWDm57uM3HTHylm/Dq3LmXTgGNxWUUkf2oh1g7R6cYZpBUQwiEPzhZQ7CoBQbGUAJmH9ZU9m+cr8kuAOUipd6BNEDvn/fIH4WJsRCNP72JGX16JBpuICvpkuNhskZT91xFlYk1pTXHOxNpbTcxUTgMHhrTqspeRXPmf6DiYGvjMb2S6kaoGqCIRIcwl0TGKuMsOMqR9SqF8gubkqHMVbSl1E7mwBn4ke8/7IGoMkWOGwUpVxqVOLBHi6zSR09RTVSbKl4oiFV3ZwmVPSxDncq54MptyJ2WCZ7dD6ht2l+VA8iGwYeIoqOpwGWxyuNI=
|
16
|
+
gem: tfctl
|
17
|
+
on:
|
18
|
+
tags: true
|
19
|
+
repo: scalefactory/tfctl
|
data/CHANGELOG.adoc
CHANGED
@@ -1,6 +1,28 @@
|
|
1
1
|
= Changelog
|
2
2
|
|
3
|
-
==
|
3
|
+
== Upcoming
|
4
|
+
|
5
|
+
== 1.2.2
|
6
|
+
* chore: reverted PR #11 - not necessary and introduced regression. See PR #13 for details.
|
7
|
+
|
8
|
+
== 1.2.1
|
9
|
+
* chore: added required Ruby version to Gemspec.
|
10
|
+
|
11
|
+
== 1.2.0
|
12
|
+
|
13
|
+
* feat: pass TF_ environment variables to terraform (PR #11).
|
14
|
+
|
15
|
+
== 1.1.1
|
16
|
+
|
17
|
+
* fix: handle empty response from Organizations API containing children (thanks @grothja)
|
18
|
+
* chore: stopped testing on EOL Rubies 2.3 and 2.4 (but should still currently work)
|
19
|
+
* chore: dependencies minimum version bump
|
20
|
+
|
21
|
+
== 1.1.0
|
22
|
+
|
23
|
+
* feat: look for configuration in `tfctl.yaml` by default.
|
24
|
+
|
25
|
+
== 1.0.0
|
4
26
|
|
5
27
|
* feat(config): JSON schema config validation
|
6
28
|
* feat(config): added 'data' parameter
|
data/README.adoc
CHANGED
@@ -28,15 +28,16 @@ toc::[]
|
|
28
28
|
|
29
29
|
== Overview
|
30
30
|
|
31
|
-
|
31
|
+
`tfctl` is a small Terraform wrapper for working with multi-account AWS
|
32
32
|
infrastructures where new accounts may be created dynamically and on-demand.
|
33
33
|
|
34
|
-
It discovers accounts by reading the AWS Organizations API and can assign
|
34
|
+
It discovers accounts by reading the AWS Organizations API, and can assign
|
35
35
|
Terraform resources to multiple accounts based on the organization hierarchy.
|
36
36
|
Resources can be assigned globally, based on organization unit or to individual
|
37
|
-
accounts. It supports
|
37
|
+
accounts. It supports hierarchies of nested Organizational Units (OUs),
|
38
|
+
and helps keep your Terraform DRY.
|
38
39
|
|
39
|
-
|
40
|
+
The Scale Factory originally created tfctl to integrate Terraform with
|
40
41
|
https://aws.amazon.com/solutions/aws-landing-zone/[AWS Landing Zone] and
|
41
42
|
https://aws.amazon.com/controltower/[Control Tower] but should work with most
|
42
43
|
other ways of managing accounts in AWS Organizations.
|
@@ -57,7 +58,7 @@ other ways of managing accounts in AWS Organizations.
|
|
57
58
|
== Requirements
|
58
59
|
|
59
60
|
* Terraform >= 0.12
|
60
|
-
* Ruby >= 2.
|
61
|
+
* Ruby >= 2.4
|
61
62
|
* Accounts managed in AWS Organizations (by Landing Zone, Control Tower, some
|
62
63
|
other means)
|
63
64
|
|
@@ -65,17 +66,19 @@ other ways of managing accounts in AWS Organizations.
|
|
65
66
|
|
66
67
|
To install the latest release from RubyGems run:
|
67
68
|
|
69
|
+
[source,shell]
|
68
70
|
----
|
69
71
|
gem install tfctl
|
70
72
|
----
|
71
73
|
|
72
|
-
Alternatively you can build and install from this repo with:
|
74
|
+
Alternatively, you can build and install from this repo with:
|
73
75
|
|
76
|
+
[source,shell]
|
74
77
|
----
|
75
78
|
make install
|
76
79
|
----
|
77
80
|
|
78
|
-
==
|
81
|
+
== Documentation
|
79
82
|
|
80
83
|
* https://github.com/scalefactory/tfctl/tree/master/docs/control_tower.adoc[Control Tower quick start guide]
|
81
84
|
* https://github.com/scalefactory/tfctl/tree/master/docs/project_layout.adoc[Project layout]
|
@@ -85,23 +88,25 @@ make install
|
|
85
88
|
|
86
89
|
== Running tfctl
|
87
90
|
|
88
|
-
|
89
|
-
Terraform configuration in `.tfctl
|
91
|
+
You should run `tfctl` from the root of your project directory. It will generate
|
92
|
+
Terraform configuration in `.tfctl/` (add this to your `.gitignore`).
|
90
93
|
|
91
94
|
Anatomy of a tfctl command:
|
92
95
|
|
96
|
+
[source,shell]
|
93
97
|
----
|
94
98
|
tfctl -c CONFIG_FILE TARGET_OPTIONS -- TERRAFORM_COMMAND
|
95
99
|
----
|
96
100
|
|
97
|
-
* `-c` specifies which tfctl config file to use (
|
101
|
+
* `-c` specifies which tfctl config file to use (defaults to `tfctl.yaml` in
|
102
|
+
current working directory if not set)
|
98
103
|
* `TARGET_OPTIONS` specifies which accounts to target. This could be an individual
|
99
104
|
account, a group of accounts in an organizational unit or all accounts.
|
100
105
|
* `TERRAFORM_COMMAND` will be passed to `terraform` along with any
|
101
106
|
options. See https://www.terraform.io/docs/commands/index.html[Terraform
|
102
107
|
commands] for details.
|
103
108
|
|
104
|
-
NOTE: You must have your AWS credentials configured before
|
109
|
+
NOTE: You must have your AWS credentials configured before you run `tfctl`, or run
|
105
110
|
it using an AWS credentials helper such as
|
106
111
|
https://github.com/99designs/aws-vault[aws-vault].
|
107
112
|
|
@@ -109,68 +114,78 @@ https://github.com/99designs/aws-vault[aws-vault].
|
|
109
114
|
|
110
115
|
Show help:
|
111
116
|
|
117
|
+
[source,shell]
|
112
118
|
----
|
113
119
|
tfctl -h
|
114
120
|
----
|
115
121
|
|
116
122
|
Show merged configuration:
|
117
123
|
|
124
|
+
[source,shell]
|
118
125
|
----
|
119
|
-
tfctl -
|
126
|
+
tfctl -s
|
120
127
|
----
|
121
128
|
|
122
129
|
List all discovered accounts:
|
123
130
|
|
131
|
+
[source,shell]
|
124
132
|
----
|
125
|
-
tfctl
|
133
|
+
tfctl --all -l
|
126
134
|
----
|
127
135
|
|
128
136
|
TIP: This can be narrowed down using targeting options and is a good way to
|
129
137
|
test what accounts match.
|
130
138
|
|
131
|
-
Run
|
139
|
+
Run `terraform init` across all accounts:
|
132
140
|
|
141
|
+
[source,shell]
|
133
142
|
----
|
134
|
-
tfctl
|
143
|
+
tfctl --all -- init
|
135
144
|
----
|
136
145
|
|
137
|
-
|
146
|
+
Plan Terraform across all accounts in the `test` OU:
|
138
147
|
|
148
|
+
[source,shell]
|
139
149
|
----
|
140
|
-
tfctl -
|
150
|
+
tfctl -o test -- plan
|
141
151
|
----
|
142
152
|
|
143
|
-
|
153
|
+
Plan Terraform in `live` accounts, assuming that `live` is a child OU in multiple
|
144
154
|
organization units:
|
145
155
|
|
156
|
+
[source,shell]
|
146
157
|
----
|
147
|
-
tfctl -
|
158
|
+
tfctl -o '.*/live' -- plan
|
148
159
|
----
|
149
160
|
|
150
|
-
Run plan
|
161
|
+
Run a plan for an individual account:
|
151
162
|
|
163
|
+
[source,shell]
|
152
164
|
----
|
153
|
-
tfctl -
|
165
|
+
tfctl -a example-account - plan
|
154
166
|
----
|
155
167
|
|
156
|
-
|
168
|
+
Apply Terraform changes across all accounts:
|
157
169
|
|
170
|
+
[source,shell]
|
158
171
|
----
|
159
|
-
tfctl
|
172
|
+
tfctl --all -- apply
|
160
173
|
----
|
161
174
|
|
162
|
-
|
175
|
+
Destroy Terraform-managed resources in all the `test` OU accounts:
|
163
176
|
|
177
|
+
[source,shell]
|
164
178
|
----
|
165
|
-
tfctl -
|
179
|
+
tfctl -o test -- destroy -auto-approve
|
166
180
|
----
|
167
181
|
|
168
182
|
Don't buffer the output:
|
169
183
|
|
184
|
+
[source,shell]
|
170
185
|
----
|
171
|
-
tfctl -
|
186
|
+
tfctl -a example-account -u -- plan
|
172
187
|
----
|
173
188
|
|
174
189
|
This will show output in real time. Usually output is buffered and displayed
|
175
|
-
after Terraform command finishes to make it more readable when running
|
176
|
-
multiple accounts in parallel.
|
190
|
+
after the Terraform command finishes, to make it more readable when running
|
191
|
+
across multiple accounts in parallel.
|
data/bin/tfctl
CHANGED
@@ -22,7 +22,7 @@ options = {
|
|
22
22
|
ou: nil,
|
23
23
|
all: nil,
|
24
24
|
show_config: false,
|
25
|
-
config_file:
|
25
|
+
config_file: 'tfctl.yaml',
|
26
26
|
unbuffered: false,
|
27
27
|
debug: false,
|
28
28
|
use_cache: false,
|
@@ -68,10 +68,6 @@ begin
|
|
68
68
|
|
69
69
|
# Validate CLI arguments
|
70
70
|
|
71
|
-
if options[:config_file].nil?
|
72
|
-
raise OptionParser::MissingArgument, '--config-file'
|
73
|
-
end
|
74
|
-
|
75
71
|
unless File.exist? options[:config_file]
|
76
72
|
raise OptionParser::InvalidOption,
|
77
73
|
"Config file not found in: #{options[:config_file]}"
|
@@ -104,7 +100,7 @@ end
|
|
104
100
|
|
105
101
|
|
106
102
|
|
107
|
-
#
|
103
|
+
# Execute terraform in target accounts
|
108
104
|
def run_account(config, account, options, tf_argv, log)
|
109
105
|
|
110
106
|
# Skip excluded accounts
|
@@ -145,6 +141,7 @@ begin
|
|
145
141
|
log.info 'tfctl running'
|
146
142
|
|
147
143
|
config_name = File.basename(options[:config_file]).chomp('.yaml')
|
144
|
+
config_name = 'default' if config_name == 'tfctl'
|
148
145
|
log.info "Using config: #{config_name}"
|
149
146
|
|
150
147
|
log.info 'Working out AWS account topology'
|
data/docs/configuration.adoc
CHANGED
@@ -1,7 +1,31 @@
|
|
1
|
-
|
1
|
+
// Settings:
|
2
|
+
:idprefix:
|
3
|
+
:idseparator: -
|
4
|
+
ifndef::env-github[:icons: font]
|
5
|
+
ifdef::env-github,env-browser[]
|
6
|
+
:toc: macro
|
7
|
+
:toclevels: 1
|
8
|
+
endif::[]
|
9
|
+
ifdef::env-github[]
|
10
|
+
:branch: master
|
11
|
+
:status:
|
12
|
+
:outfilesuffix: .adoc
|
13
|
+
:!toc-title:
|
14
|
+
:caution-caption: :fire:
|
15
|
+
:important-caption: :exclamation:
|
16
|
+
:note-caption: :paperclip:
|
17
|
+
:tip-caption: :bulb:
|
18
|
+
:warning-caption: :warning:
|
19
|
+
endif::[]
|
2
20
|
|
3
|
-
|
4
|
-
|
21
|
+
= Configuration
|
22
|
+
|
23
|
+
toc::[]
|
24
|
+
|
25
|
+
== Overview
|
26
|
+
|
27
|
+
`tfctl` retrieves initial account configuration from AWS Organizations and merges
|
28
|
+
it with configuration specified in YAML format (`tfctl.yaml` by default).
|
5
29
|
|
6
30
|
The configuration is merged in the following order:
|
7
31
|
|
@@ -44,11 +68,11 @@ organization_units:
|
|
44
68
|
|
45
69
|
This will result in all three profiles deployed to accounts in `team` OU.
|
46
70
|
|
47
|
-
TIP: You can display the fully merged configuration by running `tfctl -
|
48
|
-
|
49
|
-
|
71
|
+
TIP: You can display the fully merged configuration by running `tfctl -s`.
|
72
|
+
It's safe to run as it doesn't make any changes to AWS resources. It's a good
|
73
|
+
way to test your configuration.
|
50
74
|
|
51
|
-
|
75
|
+
== Defining arbitrary data
|
52
76
|
|
53
77
|
You can define arbitrary data under the `data:` parameter, both in the root of
|
54
78
|
the config and in the organization sections. It will be available in Terraform
|
@@ -57,9 +81,19 @@ subnet ranges, s3 bucket names and so on. `data:` in organization sections
|
|
57
81
|
will be merged with accounts following the usual merge order as described
|
58
82
|
above.
|
59
83
|
|
60
|
-
|
84
|
+
== Handling secrets
|
85
|
+
|
86
|
+
CAUTION: Do not commit secrets into your Terraform or tfctl configuration.
|
61
87
|
|
62
|
-
|
63
|
-
|
88
|
+
Instead, use AWS Secrets Manager and retrieve secrets in Terraform profiles using
|
89
|
+
the
|
64
90
|
https://www.terraform.io/docs/providers/aws/d/secretsmanager_secret.html[secrets
|
65
|
-
manager data source]
|
91
|
+
manager data source].
|
92
|
+
|
93
|
+
== Configuration Schema
|
94
|
+
|
95
|
+
The configuration file is validated using https://json-schema.org/[JSON Schema].
|
96
|
+
|
97
|
+
The schema is defined in
|
98
|
+
https://github.com/scalefactory/tfctl/blob/master/lib/tfctl/schema.rb[lib/tfctl/schema.rb]
|
99
|
+
and is a good place to look up all available options.
|
data/docs/control_tower.adoc
CHANGED
@@ -1,17 +1,37 @@
|
|
1
|
-
:
|
2
|
-
|
3
|
-
|
1
|
+
// Settings:
|
2
|
+
:idprefix:
|
3
|
+
:idseparator: -
|
4
|
+
ifndef::env-github[:icons: font]
|
5
|
+
ifdef::env-github,env-browser[]
|
6
|
+
:toc: macro
|
7
|
+
:toclevels: 1
|
8
|
+
endif::[]
|
9
|
+
ifdef::env-github[]
|
10
|
+
:branch: master
|
11
|
+
:status:
|
12
|
+
:outfilesuffix: .adoc
|
13
|
+
:!toc-title:
|
14
|
+
:caution-caption: :fire:
|
15
|
+
:important-caption: :exclamation:
|
16
|
+
:note-caption: :paperclip:
|
17
|
+
:tip-caption: :bulb:
|
18
|
+
:warning-caption: :warning:
|
19
|
+
endif::[]
|
20
|
+
|
21
|
+
= Control Tower integration guide
|
4
22
|
|
5
23
|
This guide will help you integrate Terraform with AWS Control Tower using the
|
6
24
|
tfctl wrapper. This involves setting up resources for remote state tracking,
|
7
25
|
necessary IAM roles and a tfctl project.
|
8
26
|
|
9
|
-
|
27
|
+
toc::[]
|
28
|
+
|
29
|
+
== Overview
|
10
30
|
|
11
31
|
For state tracking we're going to create a dedicated `shared-services` account
|
12
32
|
under a `mgmt` organization unit. We'll use S3 for state storage and DynamoDB
|
13
33
|
for locking. `TerraformState` IAM role will be created for cross account
|
14
|
-
access to state resources from the primary account.
|
34
|
+
access to state resources from the primary AWS account.
|
15
35
|
|
16
36
|
In the primary account we'll create a `TfctlOrgAccess` role. It gives tfctl
|
17
37
|
read only access to AWS Organizations which is used to discover accounts and
|
@@ -26,16 +46,17 @@ account factory and can be assumed from the primary account.
|
|
26
46
|
We're going to create a `live` and `test` organization units in Control Tower
|
27
47
|
and provision a couple of accounts for testing.
|
28
48
|
|
29
|
-
|
49
|
+
== Prerequisites
|
30
50
|
|
31
51
|
Before starting you'll need:
|
32
52
|
|
33
|
-
*
|
34
|
-
*
|
35
|
-
*
|
53
|
+
* An AWS account that is part of an Organization.
|
54
|
+
* Control Tower set up in your primary AWS account.
|
55
|
+
* A user with `AdministratorAccess` privileges in your primary AWS account.
|
56
|
+
* AWS CLI tools installed on your local machine.
|
36
57
|
* Terraform 0.12 or higher.
|
37
58
|
|
38
|
-
|
59
|
+
== Configure Control Tower
|
39
60
|
|
40
61
|
Create the following organization units in Control Tower:
|
41
62
|
|
@@ -52,30 +73,35 @@ Then provision accounts:
|
|
52
73
|
NOTE: Control Tower accounts need to be provisioned one at a time. It takes
|
53
74
|
approximately 20 mins to provision one.
|
54
75
|
|
55
|
-
|
76
|
+
== Install tfctl
|
56
77
|
|
78
|
+
[source,shell]
|
57
79
|
----
|
58
80
|
git clone git@github.com:scalefactory/tfctl.git
|
59
81
|
cd tfctl/ && sudo make install
|
60
82
|
----
|
61
83
|
|
62
|
-
|
84
|
+
== Set up AWS resources
|
63
85
|
|
64
86
|
It's assumed you have configured AWS CLI access to the primary account.
|
65
87
|
|
66
88
|
We'll use CloudFormation templates in `examples/bootstrap/`.
|
67
89
|
|
68
|
-
First export configuration using environment variables making sure to change to
|
69
|
-
values to suit your
|
90
|
+
First export configuration using environment variables, making sure to change to
|
91
|
+
values to suit your setup:
|
70
92
|
|
93
|
+
[source,shell]
|
71
94
|
----
|
72
|
-
|
73
|
-
export
|
95
|
+
# Change these to match your setup
|
96
|
+
export PRIMARY_ACCOUNT_ID=123456789012
|
97
|
+
export SHARED_SERVICES_ACCOUNT_ID=345678901234
|
74
98
|
export STATE_BUCKET_NAME='example-terraform-state'
|
99
|
+
export AWS_REGION=eu-west-1
|
75
100
|
----
|
76
101
|
|
77
102
|
Create the remote state resources stack set:
|
78
103
|
|
104
|
+
[source,shell]
|
79
105
|
----
|
80
106
|
cd examples/bootstrap/
|
81
107
|
|
@@ -87,31 +113,34 @@ aws cloudformation create-stack-set \
|
|
87
113
|
--execution-role-name AWSControlTowerExecution \
|
88
114
|
--administration-role-arn arn:aws:iam::${PRIMARY_ACCOUNT_ID}:role/service-role/AWSControlTowerStackSetRole \
|
89
115
|
--parameters ParameterKey=PrimaryAccountId,ParameterValue=${PRIMARY_ACCOUNT_ID} \
|
90
|
-
ParameterKey=TerraformStateBucket,ParameterValue
|
116
|
+
ParameterKey=TerraformStateBucket,ParameterValue="${STATE_BUCKET_NAME}"
|
91
117
|
----
|
92
118
|
|
93
|
-
Create a stack set instance in
|
119
|
+
Create a stack set instance in your shared services account:
|
94
120
|
|
121
|
+
[source,shell]
|
95
122
|
----
|
96
123
|
aws cloudformation create-stack-instances \
|
97
124
|
--stack-set-name TerraformState \
|
98
125
|
--accounts ${SHARED_SERVICES_ACCOUNT_ID} \
|
99
|
-
--regions
|
126
|
+
--regions ${AWS_REGION}
|
100
127
|
----
|
101
128
|
|
102
129
|
Check status:
|
103
130
|
|
131
|
+
[source,shell]
|
104
132
|
----
|
105
133
|
aws cloudformation describe-stack-instance \
|
106
134
|
--stack-set-name TerraformState \
|
107
135
|
--stack-instance-account ${SHARED_SERVICES_ACCOUNT_ID} \
|
108
|
-
--stack-instance-region
|
136
|
+
--stack-instance-region ${AWS_REGION}
|
109
137
|
----
|
110
138
|
|
111
139
|
NOTE: Initial status will be `OUTDATED`, it should change to `CURRENT` once deployed.
|
112
140
|
|
113
|
-
Deploy `TfctlOrgAccess` IAM role stack:
|
141
|
+
Deploy the `TfctlOrgAccess` IAM role stack:
|
114
142
|
|
143
|
+
[source,shell]
|
115
144
|
----
|
116
145
|
aws cloudformation create-stack \
|
117
146
|
--stack-name TfctlOrgAccess \
|
@@ -122,70 +151,79 @@ aws cloudformation create-stack \
|
|
122
151
|
|
123
152
|
Check status:
|
124
153
|
|
154
|
+
[source,shell]
|
125
155
|
----
|
126
156
|
aws cloudformation describe-stacks --stack-name TfctlOrgAccess
|
127
157
|
----
|
128
158
|
|
129
159
|
NOTE: Successful status should read: `CREATE_COMPLETE`.
|
130
160
|
|
131
|
-
|
161
|
+
== Configure tfctl
|
132
162
|
|
133
163
|
Copy the example project directory `examples/control_tower` somewhere convenient
|
134
|
-
and edit `
|
164
|
+
and edit `tfctl.yaml`.
|
135
165
|
|
136
166
|
You need to modify the following parameters:
|
137
167
|
|
138
168
|
* `tf_state_bucket` - set to `$STATE_BUCKET_NAME`
|
139
169
|
* `tf_state_role_arn` - set shared services account ID
|
140
170
|
* `tfctl_role_arn` - set primary account ID
|
141
|
-
* `primary_account` - set the primary account name. You can find it
|
171
|
+
* `primary_account` - set the primary account name. You can find it through AWS Organizations in the console.
|
142
172
|
|
143
173
|
TIP: You should keep your project directory under version control.
|
144
174
|
|
145
|
-
|
175
|
+
== Deploy example tfctl profile
|
146
176
|
|
147
177
|
The example profile will create an S3 bucket in accounts under `test`, `live`
|
148
178
|
and `mgmt` OUs.
|
149
179
|
|
150
180
|
NOTE: Run tfctl commands from the root of you project directory.
|
151
181
|
|
152
|
-
First dump the configuration to verify everything works:
|
182
|
+
First, dump the configuration to verify everything works:
|
153
183
|
|
184
|
+
[source,shell]
|
154
185
|
----
|
155
|
-
tfctl -
|
186
|
+
tfctl -s
|
156
187
|
----
|
157
188
|
|
158
|
-
This will not make any changes but will print out
|
189
|
+
This will not make any changes but will print out YAML containing the final,
|
159
190
|
merged configuration data. It should contain a list of discovered accounts and
|
160
191
|
their configuration.
|
161
192
|
|
162
|
-
Initialise
|
193
|
+
Initialise Terraform for all discovered accounts:
|
163
194
|
|
195
|
+
[source,shell]
|
164
196
|
----
|
165
|
-
tfctl
|
197
|
+
tfctl --all -- init
|
166
198
|
----
|
167
199
|
|
168
200
|
Tfctl will run Terraform against all accounts in parallel.
|
169
201
|
|
170
|
-
|
202
|
+
`plan` the Terraform:
|
171
203
|
|
204
|
+
[source,shell]
|
172
205
|
----
|
173
|
-
tfctl
|
206
|
+
tfctl --all -- plan
|
174
207
|
----
|
175
208
|
|
176
|
-
and apply:
|
209
|
+
and `apply` it:
|
177
210
|
|
211
|
+
[source,shell]
|
178
212
|
----
|
179
|
-
tfctl
|
213
|
+
tfctl --all -- apply
|
180
214
|
----
|
181
215
|
|
182
|
-
To destroy created resources run:
|
183
216
|
|
217
|
+
== Clean up
|
218
|
+
|
219
|
+
To destroy created resources, run:
|
220
|
+
|
221
|
+
[source,shell]
|
184
222
|
----
|
185
|
-
tfctl
|
223
|
+
tfctl --all -- destroy -auto-approve
|
186
224
|
----
|
187
225
|
|
188
|
-
That's it! You can now execute
|
226
|
+
That's it! You can now execute Terraform across your Control Tower estate.
|
189
227
|
|
190
228
|
TIP: Your project directory should be under version control excluding the
|
191
229
|
`.tfctl` directory which is automatically generated.
|
@@ -1,9 +1,31 @@
|
|
1
|
-
|
1
|
+
// Settings:
|
2
|
+
:idprefix:
|
3
|
+
:idseparator: -
|
4
|
+
ifndef::env-github[:icons: font]
|
5
|
+
ifdef::env-github,env-browser[]
|
6
|
+
:toc: macro
|
7
|
+
:toclevels: 1
|
8
|
+
endif::[]
|
9
|
+
ifdef::env-github[]
|
10
|
+
:branch: master
|
11
|
+
:status:
|
12
|
+
:outfilesuffix: .adoc
|
13
|
+
:!toc-title:
|
14
|
+
:caution-caption: :fire:
|
15
|
+
:important-caption: :exclamation:
|
16
|
+
:note-caption: :paperclip:
|
17
|
+
:tip-caption: :bulb:
|
18
|
+
:warning-caption: :warning:
|
19
|
+
endif::[]
|
20
|
+
|
21
|
+
= Creating and deploying a tfctl profile
|
2
22
|
|
3
23
|
This guide will show you how to create a tfctl profile, declare some resources
|
4
24
|
in it and deploy it to to a group of accounts in an organization unit.
|
5
25
|
|
6
|
-
|
26
|
+
toc::[]
|
27
|
+
|
28
|
+
== Create a new profile
|
7
29
|
|
8
30
|
In your tfctl project directory create a new profile:
|
9
31
|
|
@@ -59,7 +81,7 @@ profile. Tfctl configuration can be accessed using this variable. This It
|
|
59
81
|
includes an array of all discovered accounts as well their parameters from
|
60
82
|
tfctl config file.
|
61
83
|
|
62
|
-
TIP: You can run `tfctl -
|
84
|
+
TIP: You can run `tfctl -s` to show the config data in
|
63
85
|
yaml format. This exact data is available in the `config` variable in your
|
64
86
|
profile.
|
65
87
|
|
@@ -87,7 +109,7 @@ resource "aws_s3_bucket" "example" {
|
|
87
109
|
This will create an S3 bucket with a name containing the current account name
|
88
110
|
(which will vary depending on which account it's deployed to).
|
89
111
|
|
90
|
-
|
112
|
+
== Assign profile to accounts
|
91
113
|
|
92
114
|
Before you can deploy the new profile you need to tell `tfctl` which accounts
|
93
115
|
to deploy it to.
|
@@ -102,7 +124,7 @@ You have few options here:
|
|
102
124
|
For the sake of this example we're going to deploy our bucket to all accounts
|
103
125
|
in `test` OU.
|
104
126
|
|
105
|
-
In tfctl
|
127
|
+
In `tfctl.yaml` add the profile to the `test` OU:
|
106
128
|
|
107
129
|
[source, yaml]
|
108
130
|
----
|
@@ -113,13 +135,13 @@ organization_units:
|
|
113
135
|
----
|
114
136
|
|
115
137
|
|
116
|
-
|
138
|
+
== Plan
|
117
139
|
|
118
140
|
To see what would happen when the change is applied run:
|
119
141
|
|
120
142
|
----
|
121
|
-
tfctl -
|
122
|
-
tfctl -
|
143
|
+
tfctl -o test -- init
|
144
|
+
tfctl -o test -- plan
|
123
145
|
----
|
124
146
|
|
125
147
|
This will run `terraform init` to initialise terraform and then `terraform
|
@@ -161,9 +183,9 @@ what went wrong.
|
|
161
183
|
tfctl will generate a plan file automatically and use it with `apply` in the
|
162
184
|
next step.
|
163
185
|
|
164
|
-
|
186
|
+
== Apply
|
165
187
|
|
166
188
|
Once you're happy with the plan, apply it.
|
167
189
|
----
|
168
|
-
tfctl -
|
190
|
+
tfctl -o test -- apply
|
169
191
|
----
|
data/docs/iam_permissions.adoc
CHANGED
@@ -1,6 +1,26 @@
|
|
1
|
-
|
1
|
+
// Settings:
|
2
|
+
:idprefix:
|
3
|
+
:idseparator: -
|
4
|
+
ifndef::env-github[:icons: font]
|
5
|
+
ifdef::env-github,env-browser[]
|
6
|
+
:toc: macro
|
7
|
+
:toclevels: 1
|
8
|
+
endif::[]
|
9
|
+
ifdef::env-github[]
|
10
|
+
:branch: master
|
11
|
+
:status:
|
12
|
+
:outfilesuffix: .adoc
|
13
|
+
:!toc-title:
|
14
|
+
:caution-caption: :fire:
|
15
|
+
:important-caption: :exclamation:
|
16
|
+
:note-caption: :paperclip:
|
17
|
+
:tip-caption: :bulb:
|
18
|
+
:warning-caption: :warning:
|
19
|
+
endif::[]
|
2
20
|
|
3
|
-
|
21
|
+
= IAM roles
|
22
|
+
|
23
|
+
`tfctl` usually requires three IAM roles to be configured:
|
4
24
|
|
5
25
|
* `TfctlRole` - read only access to AWS Organizations set up in the primary account.
|
6
26
|
* `TerraformStateRole` - access to remote state resources (S3, DynamoDB) in the
|
@@ -8,7 +28,7 @@ Tfctl usually requires three IAM roles to be configured:
|
|
8
28
|
* `TerraformExecutionRole` - configured in all spoke accounts and used for executing Terraform.
|
9
29
|
|
10
30
|
The user executing tfctl needs permission to assume all three roles cross
|
11
|
-
account.
|
31
|
+
account. Once these are configured, tfctl automatically assumes roles for you.
|
12
32
|
|
13
33
|
It's possible to configure different Terraform execution roles in different
|
14
34
|
spoke accounts based on OU or account names. This can be used to restrict
|
data/docs/project_layout.adoc
CHANGED
@@ -1,10 +1,29 @@
|
|
1
|
-
|
1
|
+
// Settings:
|
2
|
+
:idprefix:
|
3
|
+
:idseparator: -
|
4
|
+
ifndef::env-github[:icons: font]
|
5
|
+
ifdef::env-github,env-browser[]
|
6
|
+
:toc: macro
|
7
|
+
:toclevels: 1
|
8
|
+
endif::[]
|
9
|
+
ifdef::env-github[]
|
10
|
+
:branch: master
|
11
|
+
:status:
|
12
|
+
:outfilesuffix: .adoc
|
13
|
+
:!toc-title:
|
14
|
+
:caution-caption: :fire:
|
15
|
+
:important-caption: :exclamation:
|
16
|
+
:note-caption: :paperclip:
|
17
|
+
:tip-caption: :bulb:
|
18
|
+
:warning-caption: :warning:
|
19
|
+
endif::[]
|
20
|
+
|
21
|
+
= Project layout
|
2
22
|
|
3
23
|
Example project structure
|
4
24
|
----
|
5
25
|
project_dir/
|
6
|
-
├── conf
|
7
|
-
│ └── example.yaml
|
26
|
+
├── tfctl.conf
|
8
27
|
├── modules
|
9
28
|
│ └── s3-bucket
|
10
29
|
│ ├── main.tf
|
@@ -16,7 +35,9 @@ project_dir/
|
|
16
35
|
└── variables.tf
|
17
36
|
----
|
18
37
|
|
19
|
-
|
38
|
+
toc::[]
|
39
|
+
|
40
|
+
== tfctl configuration file
|
20
41
|
|
21
42
|
Assigns Terraform profiles and configuration to accounts based on:
|
22
43
|
|
@@ -29,7 +50,12 @@ The configuration data is exposed to terraform via a profile `config` variable.
|
|
29
50
|
It also defines Terraform and tfctl configuration such as state tracking and
|
30
51
|
what IAM roles to use.
|
31
52
|
|
32
|
-
|
53
|
+
By default, tfctl will use `tfctl.yaml` in its current working directory. You
|
54
|
+
can specify a different file using `-c`. Multiple configurations are supported
|
55
|
+
in the same project directory and generated data will be stored separately for
|
56
|
+
each config file in `.tfctl/`.
|
57
|
+
|
58
|
+
== `profiles`
|
33
59
|
|
34
60
|
Profiles are re-usable collections of resources which can be applied to
|
35
61
|
accounts. They are implemented just like usual modules but provide an
|
@@ -38,6 +64,6 @@ other data sources). Profiles often compose multiple modules and provide
|
|
38
64
|
configuration data to them. This approach makes it possible to re-use standard
|
39
65
|
modules (e.g. Terraform module registry).
|
40
66
|
|
41
|
-
|
67
|
+
== `modules`
|
42
68
|
|
43
69
|
Standard Terraform modules.
|
File without changes
|
data/lib/tfctl/aws_org.rb
CHANGED
@@ -71,23 +71,25 @@ module Tfctl
|
|
71
71
|
@aws_org_client.list_children(
|
72
72
|
child_type: 'ORGANIZATIONAL_UNIT',
|
73
73
|
parent_id: parent_id,
|
74
|
-
).
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
74
|
+
).each do |resp|
|
75
|
+
resp.children.each do |child|
|
76
|
+
|
77
|
+
begin
|
78
|
+
ou = @aws_org_client.describe_organizational_unit(
|
79
|
+
organizational_unit_id: child.id,
|
80
|
+
).organizational_unit
|
81
|
+
rescue Aws::Organizations::Errors::TooManyRequestsException
|
82
|
+
# FIXME: - use logger
|
83
|
+
puts 'AWS Organizations: too many requests. Retrying in 5 secs.'
|
84
|
+
sleep 5
|
85
|
+
retries += 1
|
86
|
+
retry if retries < 10
|
87
|
+
end
|
87
88
|
|
88
|
-
|
89
|
+
ou_name = parent_name == :root ? ou.name.to_sym : "#{parent_name}/#{ou.name}".to_sym
|
89
90
|
|
90
|
-
|
91
|
+
output[ou_name] = ou.id
|
92
|
+
end
|
91
93
|
end
|
92
94
|
output
|
93
95
|
end
|
data/lib/tfctl/config.rb
CHANGED
@@ -48,7 +48,7 @@ module Tfctl
|
|
48
48
|
@config.to_json
|
49
49
|
end
|
50
50
|
|
51
|
-
# Filters accounts by account property
|
51
|
+
# Filters accounts by an account property
|
52
52
|
def find_accounts(property_name, property_value)
|
53
53
|
output =[]
|
54
54
|
@config[:accounts].each do |account|
|
@@ -88,7 +88,6 @@ module Tfctl
|
|
88
88
|
|
89
89
|
# Retrieves AWS Organizations data and merges it with data from yaml config.
|
90
90
|
def load_config(config_name, yaml_config, aws_org_config)
|
91
|
-
|
92
91
|
# AWS Organizations data
|
93
92
|
config = aws_org_config
|
94
93
|
# Merge organization sections from yaml file
|
data/lib/tfctl/schema.rb
CHANGED
@@ -29,7 +29,7 @@ module Tfctl
|
|
29
29
|
def main_schema
|
30
30
|
iam_arn_pattern = 'arn:aws:iam:[a-z\-0-9]*:[0-9]{12}:[a-zA-Z\/+@=.,]*'
|
31
31
|
|
32
|
-
# rubocop:disable Layout/
|
32
|
+
# rubocop:disable Layout/HashAlignment
|
33
33
|
{
|
34
34
|
'type' => 'object',
|
35
35
|
'properties' => {
|
@@ -61,7 +61,7 @@ module Tfctl
|
|
61
61
|
],
|
62
62
|
'additionalProperties' => false,
|
63
63
|
}
|
64
|
-
# rubocop:enable Layout/
|
64
|
+
# rubocop:enable Layout/HashAlignment
|
65
65
|
end
|
66
66
|
|
67
67
|
def org_schema
|
data/lib/tfctl/version.rb
CHANGED
data/tfctl.gemspec
CHANGED
@@ -23,14 +23,16 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
+
spec.required_ruby_version = '>= 2.5.0'
|
27
|
+
|
26
28
|
# Think when adding new dependencies. Is it really necessary?
|
27
29
|
# "The things you own end up owning you" etc.
|
28
|
-
spec.add_dependency 'aws-sdk-organizations', '~> 1.
|
30
|
+
spec.add_dependency 'aws-sdk-organizations', '~> 1.40'
|
29
31
|
spec.add_dependency 'json_schemer', '~> 0.2'
|
30
|
-
spec.add_dependency 'parallel', '~> 1.
|
32
|
+
spec.add_dependency 'parallel', '~> 1.19'
|
31
33
|
spec.add_dependency 'terminal-table', '~> 1.8'
|
32
34
|
|
33
35
|
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
34
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
35
|
-
spec.add_development_dependency 'rubocop', '~> 0.
|
36
|
+
spec.add_development_dependency 'rspec', '~> 3.9'
|
37
|
+
spec.add_development_dependency 'rubocop', '~> 0.84'
|
36
38
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tfctl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Wasilczuk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-organizations
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.40'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.40'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: json_schemer
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.19'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.19'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: terminal-table
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,28 +86,28 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '3.
|
89
|
+
version: '3.9'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '3.
|
96
|
+
version: '3.9'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: rubocop
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
103
|
+
version: '0.84'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0.
|
110
|
+
version: '0.84'
|
111
111
|
description:
|
112
112
|
email:
|
113
113
|
- akw@scalefactory.com
|
@@ -135,12 +135,12 @@ files:
|
|
135
135
|
- examples/bootstrap/terraform-exec-role.template
|
136
136
|
- examples/bootstrap/terraform-state.template
|
137
137
|
- examples/bootstrap/tfctl-org-access.template
|
138
|
-
- examples/control_tower/conf/example.yaml
|
139
138
|
- examples/control_tower/modules/s3-bucket/main.tf
|
140
139
|
- examples/control_tower/modules/s3-bucket/variables.tf
|
141
140
|
- examples/control_tower/profiles/example-profile/data.tf
|
142
141
|
- examples/control_tower/profiles/example-profile/main.tf
|
143
142
|
- examples/control_tower/profiles/example-profile/variables.tf
|
143
|
+
- examples/control_tower/tfctl.yaml
|
144
144
|
- lib/hash.rb
|
145
145
|
- lib/tfctl.rb
|
146
146
|
- lib/tfctl/aws_org.rb
|
@@ -164,15 +164,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
164
164
|
requirements:
|
165
165
|
- - ">="
|
166
166
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
167
|
+
version: 2.5.0
|
168
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
169
|
requirements:
|
170
|
-
- - "
|
170
|
+
- - ">="
|
171
171
|
- !ruby/object:Gem::Version
|
172
|
-
version:
|
172
|
+
version: '0'
|
173
173
|
requirements: []
|
174
|
-
|
175
|
-
rubygems_version: 2.7.7
|
174
|
+
rubygems_version: 3.0.8
|
176
175
|
signing_key:
|
177
176
|
specification_version: 4
|
178
177
|
summary: Terraform wrapper for managing multi-account AWS infrastructures
|