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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7851ad9d647739e471f8d430dace8564f16f403f3a7e7ffb98e21384db641f53
4
- data.tar.gz: d52713addc4006e2e67f5c3fce56941697d4a9edfb6bea08953c76eeb673c2f1
3
+ metadata.gz: b1825dfe2e3683c1ef8444460e5cd7baedde7424ec60de056a7a3351e69ad380
4
+ data.tar.gz: db929e7c2323d42c99719610e18bf5ef17277521ff69985b668e6f26ca71feea
5
5
  SHA512:
6
- metadata.gz: 10f6a9bce18e905d787783a6f61a3f93724b4809d1c7f2c7e7c1edc155ba2c14b17a97096140f790c9b7e250dc4fd24f7af5c86fefea8139d25e1b15dddc65d3
7
- data.tar.gz: 9e81ab6abbc15df5917ea6b23a1b167a2cd745e057d462ce0e28db47b27c6f12477c62293893c59f1977c5c406df31dbb797818fc27978b5bd9255a12bd86370
6
+ metadata.gz: 7288eadc3e15802584060d055a2dd51fa34aae5fe0fba8d4eefd69d3e654fa0bcdef3c05b9a410891f31b4b3f9564301350d6298f7fcdbdf175deef9ac236a74
7
+ data.tar.gz: c9515472d66fb6d6f423aaf2d01504901a2e42acf47aa6d59919d7737dc37f2817436e5364241afca523cd70cccf2082d0e791573aabb9ff55b3da42fcd760c2
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  AllCops:
3
- TargetRubyVersion: 2.3
3
+ TargetRubyVersion: 2.5
4
4
  DisplayCopNames: true
5
5
 
6
6
  Layout/IndentationWidth:
7
7
  Width: 4
8
8
 
9
- Layout/IndentHeredoc:
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/AlignHash:
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
- Metrics/LineLength:
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
@@ -1,18 +1,19 @@
1
1
  rvm:
2
- - 2.3
3
- - 2.6
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
- - stage: Gem release
10
- rvm: 2.6
11
- deploy:
12
- provider: rubygems
13
- api_key:
14
- secure: FKAONS7x6koN7oiEULr4ViwjlDBzbE0bCgqhXRP4DfTtlRyEymeqgrfSIqkVH7unjd0muIGrMBnFuaQVSx7648RS1Ss0QAJo32SVnzoYl1P03cijqNmbbaf1jRdA3IGmh0gV5vsXrmlHiP8gfAuC9PqQ550OzxzWUvEI8vXgSTibmKd/PoQinv5g/dq0gBFjlhSMt/k3Z9WlMmkEsAro/r/Ie2M7mItHPT65f0ga5q5SeujPQQ3Sd/l3mznh37bmnw5RZpFDYdA7jL2p0Y58XJPBU8soa3ZC5GeHyxCYVoGh6EDGAFb83ERRT6rQ7ywkOufTv1o497P7a/prSbvT6fzc+DcugXPEaglT+dUXMe36OoF907Xva4vq3xIHV2N/yrxbDM85hmMk22wEU+9wpDDzFNQnfsXNbaHG9F7gLgy0eoTRrSuJf6cPDlE8pwvn7b8cjieeqWc//ZNhSYnHYZGER4LFINWVxs68Eofmmqp2IESTcUpJ8oB4bV+bzzyobJMRobOXu2hvgCrTdr6r/PnckpAfZE/l4nVQa14f1FU//8bU3DwvNun6TX1Ujp+XNiRDUlvP2KnkBU4s5rsIkL3lCHW7r6GipSk6SOvGMTz5eySMsoWvZQBdAzk/OxcIteeWH9pdo1Hbu5x2/bwyuTRCQ9E79CKWDKlIQwCgUY0=
15
- gem: tfctl
16
- on:
17
- tags: true
18
- repo: scalefactory/tfctl
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
@@ -1,6 +1,28 @@
1
1
  = Changelog
2
2
 
3
- == 1.0.0-rc1 (unreleased)
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
@@ -28,15 +28,16 @@ toc::[]
28
28
 
29
29
  == Overview
30
30
 
31
- Tfctl is a small Terraform wrapper for working with multi-account AWS
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 nested OU hierarchies and helps keep your Terraform DRY.
37
+ accounts. It supports hierarchies of nested Organizational Units (OUs),
38
+ and helps keep your Terraform DRY.
38
39
 
39
- Tfctl was originally developed to integrate Terraform with
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.3
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
- == Docs
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
- tfctl should be run from the root of the project directory. It will generate
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 (usually in `conf/`)
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 running tfctl or run
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 -c conf/example.yaml -s
126
+ tfctl -s
120
127
  ----
121
128
 
122
129
  List all discovered accounts:
123
130
 
131
+ [source,shell]
124
132
  ----
125
- tfctl -c conf/example.yaml --all -l
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 Terraform init across all accounts:
139
+ Run `terraform init` across all accounts:
132
140
 
141
+ [source,shell]
133
142
  ----
134
- tfctl -c conf/example.yaml --all -- init
143
+ tfctl --all -- init
135
144
  ----
136
145
 
137
- Run plan in `test` OU accounts:
146
+ Plan Terraform across all accounts in the `test` OU:
138
147
 
148
+ [source,shell]
139
149
  ----
140
- tfctl -c conf/example.yaml -o test -- plan
150
+ tfctl -o test -- plan
141
151
  ----
142
152
 
143
- Run plan in `live` accounts assuming that `live` is a child OU in multiple
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 -c conf/example.yaml -o '.*/live' -- plan
158
+ tfctl -o '.*/live' -- plan
148
159
  ----
149
160
 
150
- Run plan in an individual account:
161
+ Run a plan for an individual account:
151
162
 
163
+ [source,shell]
152
164
  ----
153
- tfctl -c conf/example.yaml -a example-account - plan
165
+ tfctl -a example-account - plan
154
166
  ----
155
167
 
156
- Run apply in all accounts:
168
+ Apply Terraform changes across all accounts:
157
169
 
170
+ [source,shell]
158
171
  ----
159
- tfctl -c conf/example.yaml --all -- apply
172
+ tfctl --all -- apply
160
173
  ----
161
174
 
162
- Run destroy in `test` OU accounts:
175
+ Destroy Terraform-managed resources in all the `test` OU accounts:
163
176
 
177
+ [source,shell]
164
178
  ----
165
- tfctl -c conf/example.yaml -o test -- destroy -auto-approve
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 -c conf/example.yaml -a example-account -u -- plan
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 across
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: nil,
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
- # Generates configuration and runs Terraform commands for a target account.
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'
@@ -1,7 +1,31 @@
1
- == Configuration
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
- Tfctl retrieves initial account configuration from AWS Organizations and merges
4
- it with configuration specified in a yaml file.
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 -c
48
- conf/CONFIG_FILE.yaml -s`. It's safe to run as it doesn't make any changes to
49
- AWS resources. It's a good way to test your configuration.
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
- === Defining arbitrary data
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
- === Handling secrets
84
+ == Handling secrets
85
+
86
+ CAUTION: Do not commit secrets into your Terraform or tfctl configuration.
61
87
 
62
- No secrets should be committed into Terraform or tfctl configuration. Use AWS
63
- Secrets Manager instead and retrieve in Terraform profiles using
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.
@@ -1,17 +1,37 @@
1
- :toc:
2
-
3
- == Control Tower integration guide
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
- === Overview
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
- === Prerequisites
49
+ == Prerequisites
30
50
 
31
51
  Before starting you'll need:
32
52
 
33
- * Control Tower set up in your primary account.
34
- * A user with `AdministratorAccess` privileges in primary account.
35
- * AWS CLI tools installed on your machine.
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
- === Configure Control Tower
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
- === Install tfctl
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
- === Set up AWS resources
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 set up:
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
- export PRIMARY_ACCOUNT_ID=11111111
73
- export SHARED_SERVICES_ACCOUNT_ID=22222222
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=${STATE_BUCKET_NAME}
116
+ ParameterKey=TerraformStateBucket,ParameterValue="${STATE_BUCKET_NAME}"
91
117
  ----
92
118
 
93
- Create a stack set instance in you shared services account:
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 eu-west-1
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 eu-west-1
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
- === Configure tfctl
161
+ == Configure tfctl
132
162
 
133
163
  Copy the example project directory `examples/control_tower` somewhere convenient
134
- and edit `conf/example.yaml`.
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 in AWS Organizations.
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
- === Deploy example tfctl profile
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 -c conf/example.yaml -s
186
+ tfctl -s
156
187
  ----
157
188
 
158
- This will not make any changes but will print out a yaml containing the final,
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 terraform for all discovered accounts:
193
+ Initialise Terraform for all discovered accounts:
163
194
 
195
+ [source,shell]
164
196
  ----
165
- tfctl -c conf/example.yaml --all -- init
197
+ tfctl --all -- init
166
198
  ----
167
199
 
168
200
  Tfctl will run Terraform against all accounts in parallel.
169
201
 
170
- Run plan:
202
+ `plan` the Terraform:
171
203
 
204
+ [source,shell]
172
205
  ----
173
- tfctl -c conf/example.yaml --all -- plan
206
+ tfctl --all -- plan
174
207
  ----
175
208
 
176
- and apply:
209
+ and `apply` it:
177
210
 
211
+ [source,shell]
178
212
  ----
179
- tfctl -c conf/example.yaml --all -- apply
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 -c conf/example.yaml --all -- destroy -auto-approve
223
+ tfctl --all -- destroy -auto-approve
186
224
  ----
187
225
 
188
- That's it! You can now execute terraform across your Control Tower estate.
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
- == Creating and deploying a tfctl profile
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
- === Create a new profile
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 -c conf/CONFIG_FILE.yaml -s` to show the config data in
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
- === Assign profile to accounts
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 config file add the profile to the `test` OU:
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
- === Plan
138
+ == Plan
117
139
 
118
140
  To see what would happen when the change is applied run:
119
141
 
120
142
  ----
121
- tfctl -c conf/example.yaml -o test -- init
122
- tfctl -c conf/example.yaml -o test -- plan
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
- === Apply
186
+ == Apply
165
187
 
166
188
  Once you're happy with the plan, apply it.
167
189
  ----
168
- tfctl -c conf/example.yaml -o test -- apply
190
+ tfctl -o test -- apply
169
191
  ----
@@ -1,6 +1,26 @@
1
- == IAM roles
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
- Tfctl usually requires three IAM roles to be configured:
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. Tfctl will assume roles automatically for you.
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
@@ -1,10 +1,29 @@
1
- == Project layout
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
- === tfctl configuration file
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
- === profiles
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
- === modules
67
+ == `modules`
42
68
 
43
69
  Standard Terraform modules.
@@ -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
- ).children.each do |child|
75
-
76
- begin
77
- ou = @aws_org_client.describe_organizational_unit(
78
- organizational_unit_id: child.id,
79
- ).organizational_unit
80
- rescue Aws::Organizations::Errors::TooManyRequestsException
81
- # FIXME: - use logger
82
- puts 'AWS Organizations: too many requests. Retrying in 5 secs.'
83
- sleep 5
84
- retries += 1
85
- retry if retries < 10
86
- end
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
- ou_name = parent_name == :root ? ou.name.to_sym : "#{parent_name}/#{ou.name}".to_sym
89
+ ou_name = parent_name == :root ? ou.name.to_sym : "#{parent_name}/#{ou.name}".to_sym
89
90
 
90
- output[ou_name] = ou.id
91
+ output[ou_name] = ou.id
92
+ end
91
93
  end
92
94
  output
93
95
  end
@@ -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
@@ -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/AlignHash
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/AlignHash
64
+ # rubocop:enable Layout/HashAlignment
65
65
  end
66
66
 
67
67
  def org_schema
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tfctl
4
- VERSION = '1.0.0.rc1'
4
+ VERSION = '1.2.2'
5
5
  end
@@ -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.13'
30
+ spec.add_dependency 'aws-sdk-organizations', '~> 1.40'
29
31
  spec.add_dependency 'json_schemer', '~> 0.2'
30
- spec.add_dependency 'parallel', '~> 1.17'
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.8'
35
- spec.add_development_dependency 'rubocop', '~> 0.76'
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.0.0.rc1
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: 2019-11-10 00:00:00.000000000 Z
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.13'
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.13'
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.17'
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.17'
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.8'
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.8'
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.76'
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.76'
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: '0'
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: 1.3.1
172
+ version: '0'
173
173
  requirements: []
174
- rubyforge_project:
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