tfctl 1.1.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6f58a8ef8569aaec8cc41ad269adf690caca7d13472a26ed65ed846c835273c
4
- data.tar.gz: '013649daccb576adbe4c0c95c8fe64347b641dcb07ad2f0d3e5ca1420188489c'
3
+ metadata.gz: 3a7ca446d4435cb6780d2b8018e89c8f7b981a247d249ff3fe8083b94edd66ac
4
+ data.tar.gz: bc7f96ea1cb77fb6366453f4e268e55be5c398e939002f382223c94e09bccbf0
5
5
  SHA512:
6
- metadata.gz: 2a65943659c0dabe88c40d76d46a99ed23f1ef335486c719729ca599e10e60b2abc0bc81577f2490ca7bbbe64e479ab641b0197a1160b7a1e061f274f224419b
7
- data.tar.gz: 8c0cebbab4bc2738357979d1fa013bbf485ea398a3c5ecfdbb4ab9bf670060660a890b900cfca8425764c1387d4ffe5c0d0c50d3a59a10f16ba4f20f3687a303
6
+ metadata.gz: 777d4fd03430d1d54bcf822f3f4087c7e0c55374d69167487057201ef20132abe5de5c2b949cfa33aa1d48c346a9e0cd819db8193af5a8803dacb7d3988e526e
7
+ data.tar.gz: 5cc2a820f7930d930efc43c8eab98e9b825e6d1cf48390ca929f01b4136cdbd33509978a31e16f5f4956c458e007bc6dc759e86ee9305b4f14d21c77f267d8ab
data/.bundle/config ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ BUNDLE_PATH: "vendor/bundle"
3
+ BUNDLE_BIN: "vendor/bin"
@@ -0,0 +1,7 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
@@ -0,0 +1,14 @@
1
+ name: Linting
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ lint:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - uses: ruby/setup-ruby@v1
11
+ with:
12
+ ruby-version: 3.0
13
+ - name: Rubocop
14
+ run: make rubocop
@@ -0,0 +1,37 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ publish:
10
+ name: Build + Publish
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - name: Set up Ruby 2.6
15
+ uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: 2.6
18
+ - name: Build gem
19
+ run: make pkg
20
+ - name: Publish to RubyGems
21
+ run: |
22
+ mkdir -p $HOME/.gem
23
+ touch $HOME/.gem/credentials
24
+ chmod 0600 $HOME/.gem/credentials
25
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
26
+ gem push pkg/*.gem
27
+ env:
28
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
29
+ - name: Release on GitHub
30
+ uses: actions/create-release@v1
31
+ env:
32
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
33
+ with:
34
+ draft: false
35
+ prerelease: false
36
+ release_name: 'Release ${{ github.ref }}'
37
+ tag_name: '${{ github.ref }}'
@@ -0,0 +1,23 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ rspec:
11
+ runs-on: ${{ matrix.os }}-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu, macos]
16
+ ruby: [2.6, 2.7, 3.0]
17
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby }}
23
+ - run: make spec
data/.gitignore CHANGED
@@ -4,7 +4,6 @@
4
4
  pkg/
5
5
  *.gem
6
6
  vendor/
7
- .bundle
8
7
  bin/bundle
9
8
  bin/htmldiff
10
9
  bin/ldiff
data/.rubocop.yml CHANGED
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  AllCops:
3
- TargetRubyVersion: 2.3
3
+ TargetRubyVersion: 2.5
4
4
  DisplayCopNames: true
5
+ NewCops: enable
5
6
 
6
7
  Layout/IndentationWidth:
7
8
  Width: 4
@@ -45,7 +46,7 @@ Metrics/BlockLength:
45
46
  Metrics/MethodLength:
46
47
  Enabled: false
47
48
 
48
- Metrics/LineLength:
49
+ Layout/LineLength:
49
50
  Max: 140
50
51
 
51
52
  Metrics/AbcSize:
@@ -77,3 +78,7 @@ Style/TrailingCommaInHashLiteral:
77
78
 
78
79
  Style/RedundantReturn:
79
80
  Enabled: false
81
+
82
+ # don't break older Rubies just because of style
83
+ Style/RedundantBegin:
84
+ Enabled: false
data/CHANGELOG.adoc CHANGED
@@ -1,5 +1,34 @@
1
1
  = Changelog
2
2
 
3
+ == 1.4.0
4
+
5
+ * feat: support yaml anchors and aliases in configuration file.
6
+
7
+ == 1.3.0
8
+
9
+ * feat: support new Terraform provider syntax
10
+
11
+ BREAKING CHANGE: The minimum supported Terraform version has been bumped to
12
+ 0.12.29. If you are running an older version of Terraform you will need to
13
+ update to the latest Terraform in 0.12.x series before updating tfctl. Once
14
+ tfctl is updated you can upgrade Terraform to further versions.
15
+
16
+ == 1.2.2
17
+ * chore: reverted PR #11 - not necessary and introduced regression. See PR #13 for details.
18
+
19
+ == 1.2.1
20
+ * chore: added required Ruby version to Gemspec.
21
+
22
+ == 1.2.0
23
+
24
+ * feat: pass TF_ environment variables to terraform (PR #11).
25
+
26
+ == 1.1.1
27
+
28
+ * fix: handle empty response from Organizations API containing children (thanks @grothja)
29
+ * chore: stopped testing on EOL Rubies 2.3 and 2.4 (but should still currently work)
30
+ * chore: dependencies minimum version bump
31
+
3
32
  == 1.1.0
4
33
 
5
34
  * feat: look for configuration in `tfctl.yaml` by default.
data/Makefile CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  vendor:
4
4
  $(info => Installing Ruby dependencies)
5
- @bundle install --path vendor --with developement --binstubs=vendor/bin
5
+ @bundle install
6
+ @bundle binstubs --all --path vendor/bin
6
7
 
7
8
  test: vendor rubocop spec
8
9
 
@@ -10,11 +11,11 @@ guard: vendor
10
11
  $(info => Starting guard)
11
12
  @bundle exec guard
12
13
 
13
- rubocop:
14
+ rubocop: vendor
14
15
  $(info => Running rubocop)
15
16
  @vendor/bin/rubocop
16
17
 
17
- spec:
18
+ spec: vendor
18
19
  $(info => Running spec tests)
19
20
  @vendor/bin/rspec
20
21
 
@@ -31,6 +32,5 @@ clean:
31
32
  $(info => Cleaning)
32
33
  @rm -rf pkg/
33
34
  @rm -rf vendor/
34
- @rm -rf .bundle
35
35
  @rm -f Gemfile.lock
36
36
  @rm -rf spec/reports/
data/README.adoc CHANGED
@@ -20,7 +20,7 @@ endif::[]
20
20
 
21
21
  = tfctl
22
22
 
23
- image:https://travis-ci.org/scalefactory/tfctl.svg?branch=master["Build Status", link="https://travis-ci.org/scalefactory/tfctl"]
23
+ image:https://travis-ci.com/scalefactory/tfctl.svg?branch=master["Build Status", link="https://travis-ci.com/scalefactory/tfctl"]
24
24
  image:https://badge.fury.io/rb/tfctl.svg["Gem Version", link="https://badge.fury.io/rb/tfctl"]
25
25
  image:https://img.shields.io/badge/terraform-0.12-blue.svg["Terraform 0.12", link="https://img.shields.io/badge/terraform-0.12-blue"]
26
26
 
@@ -28,19 +28,37 @@ 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.
43
44
 
45
+ == Project status
46
+
47
+ `tfctl` is an open source project published by The Scale Factory.
48
+
49
+ We currently consider this project to be maintained but we don't actively
50
+ develop new features. We keep it security patched and ready for use in
51
+ production environments.
52
+
53
+ We’ll take a look at any issues or PRs you open and get back to you as soon as
54
+ we can. We don’t offer any formal SLA, but we’ll be checking on this project
55
+ periodically.
56
+
57
+ If your issue is urgent, you can flag it as such, and we’ll attempt to triage
58
+ appropriately, but we have paying customers who also have demands on our time.
59
+ If your business depends on this project and you have an urgent problem, then
60
+ you can talk to our sales team about paying us to support you.
61
+
44
62
  == Features
45
63
 
46
64
  * Discovers AWS accounts automatically.
@@ -56,8 +74,8 @@ other ways of managing accounts in AWS Organizations.
56
74
 
57
75
  == Requirements
58
76
 
59
- * Terraform >= 0.12
60
- * Ruby >= 2.3
77
+ * Terraform >= 0.12.29
78
+ * Ruby >= 2.5
61
79
  * Accounts managed in AWS Organizations (by Landing Zone, Control Tower, some
62
80
  other means)
63
81
 
@@ -65,17 +83,19 @@ other ways of managing accounts in AWS Organizations.
65
83
 
66
84
  To install the latest release from RubyGems run:
67
85
 
86
+ [source,shell]
68
87
  ----
69
88
  gem install tfctl
70
89
  ----
71
90
 
72
- Alternatively you can build and install from this repo with:
91
+ Alternatively, you can build and install from this repo with:
73
92
 
93
+ [source,shell]
74
94
  ----
75
95
  make install
76
96
  ----
77
97
 
78
- == Docs
98
+ == Documentation
79
99
 
80
100
  * https://github.com/scalefactory/tfctl/tree/master/docs/control_tower.adoc[Control Tower quick start guide]
81
101
  * https://github.com/scalefactory/tfctl/tree/master/docs/project_layout.adoc[Project layout]
@@ -85,11 +105,12 @@ make install
85
105
 
86
106
  == Running tfctl
87
107
 
88
- tfctl should be run from the root of the project directory. It will generate
89
- Terraform configuration in `.tfctl/`.
108
+ You should run `tfctl` from the root of your project directory. It will generate
109
+ Terraform configuration in `.tfctl/` (add this to your `.gitignore`).
90
110
 
91
111
  Anatomy of a tfctl command:
92
112
 
113
+ [source,shell]
93
114
  ----
94
115
  tfctl -c CONFIG_FILE TARGET_OPTIONS -- TERRAFORM_COMMAND
95
116
  ----
@@ -102,7 +123,7 @@ tfctl -c CONFIG_FILE TARGET_OPTIONS -- TERRAFORM_COMMAND
102
123
  options. See https://www.terraform.io/docs/commands/index.html[Terraform
103
124
  commands] for details.
104
125
 
105
- NOTE: You must have your AWS credentials configured before running tfctl or run
126
+ NOTE: You must have your AWS credentials configured before you run `tfctl`, or run
106
127
  it using an AWS credentials helper such as
107
128
  https://github.com/99designs/aws-vault[aws-vault].
108
129
 
@@ -110,18 +131,21 @@ https://github.com/99designs/aws-vault[aws-vault].
110
131
 
111
132
  Show help:
112
133
 
134
+ [source,shell]
113
135
  ----
114
136
  tfctl -h
115
137
  ----
116
138
 
117
139
  Show merged configuration:
118
140
 
141
+ [source,shell]
119
142
  ----
120
143
  tfctl -s
121
144
  ----
122
145
 
123
146
  List all discovered accounts:
124
147
 
148
+ [source,shell]
125
149
  ----
126
150
  tfctl --all -l
127
151
  ----
@@ -129,49 +153,56 @@ tfctl --all -l
129
153
  TIP: This can be narrowed down using targeting options and is a good way to
130
154
  test what accounts match.
131
155
 
132
- Run Terraform init across all accounts:
156
+ Run `terraform init` across all accounts:
133
157
 
158
+ [source,shell]
134
159
  ----
135
160
  tfctl --all -- init
136
161
  ----
137
162
 
138
- Run plan in `test` OU accounts:
163
+ Plan Terraform across all accounts in the `test` OU:
139
164
 
165
+ [source,shell]
140
166
  ----
141
167
  tfctl -o test -- plan
142
168
  ----
143
169
 
144
- Run plan in `live` accounts assuming that `live` is a child OU in multiple
170
+ Plan Terraform in `live` accounts, assuming that `live` is a child OU in multiple
145
171
  organization units:
146
172
 
173
+ [source,shell]
147
174
  ----
148
175
  tfctl -o '.*/live' -- plan
149
176
  ----
150
177
 
151
- Run plan in an individual account:
178
+ Run a plan for an individual account:
152
179
 
180
+ [source,shell]
153
181
  ----
154
182
  tfctl -a example-account - plan
155
183
  ----
156
184
 
157
- Run apply in all accounts:
185
+ Apply Terraform changes across all accounts:
158
186
 
187
+ [source,shell]
159
188
  ----
160
189
  tfctl --all -- apply
161
190
  ----
162
191
 
163
- Run destroy in `test` OU accounts:
192
+ Destroy Terraform-managed resources in all the `test` OU accounts:
164
193
 
194
+ [source,shell]
165
195
  ----
166
196
  tfctl -o test -- destroy -auto-approve
167
197
  ----
168
198
 
169
199
  Don't buffer the output:
170
200
 
201
+ [source,shell]
171
202
  ----
172
203
  tfctl -a example-account -u -- plan
173
204
  ----
174
205
 
175
206
  This will show output in real time. Usually output is buffered and displayed
176
- after Terraform command finishes to make it more readable when running across
177
- multiple accounts in parallel.
207
+ after the Terraform command finishes, to make it more readable when running
208
+ across multiple accounts in parallel.
data/RELEASING.adoc ADDED
@@ -0,0 +1,13 @@
1
+ = Releasing
2
+
3
+ This document is aimed at `tfctl` maintainers and describes the process of
4
+ releasing a new gem version.
5
+
6
+ == Process
7
+
8
+ * Smoke test in SF test accounts: https://github.com/scalefactory/tfctl-test
9
+ * Bump version in `lib/tfctl/version.rb`
10
+ * Update `CHANGELOG.adoc`
11
+ * Commit
12
+ * Create a new GitHub release and version tag using format: vX.X.X
13
+ * TravisCI will build and release the gem automatically: https://travis-ci.org/github/scalefactory/tfctl
data/bin/tfctl CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- if File.directory?(File.dirname(__FILE__) + '/../vendor')
4
+ if File.directory?("#{File.dirname(__FILE__)}/../vendor")
5
5
  require 'bundler/setup'
6
6
  end
7
7
  require 'optparse'
@@ -81,8 +81,8 @@ begin
81
81
  targetting_opts = %i[account ou all]
82
82
  targets_set = []
83
83
  options.each do |k, v|
84
- if targetting_opts.include?(k)
85
- targets_set << k.to_s unless v.nil?
84
+ if targetting_opts.include?(k) and !v.nil?
85
+ targets_set << k.to_s
86
86
  end
87
87
  end
88
88
  if targets_set.length > 1
@@ -146,7 +146,7 @@ begin
146
146
 
147
147
  log.info 'Working out AWS account topology'
148
148
 
149
- yaml_config = YAML.safe_load(File.read(options[:config_file]))
149
+ yaml_config = YAML.safe_load(File.read(options[:config_file]), aliases: true)
150
150
  Tfctl::Schema.validate(yaml_config)
151
151
  yaml_config.symbolize_names!
152
152
 
@@ -24,8 +24,8 @@ toc::[]
24
24
 
25
25
  == Overview
26
26
 
27
- Tfctl retrieves initial account configuration from AWS Organizations and merges
28
- it with configuration specified in a yaml file (`tfctl.yaml` by default).
27
+ `tfctl` retrieves initial account configuration from AWS Organizations and merges
28
+ it with configuration specified in YAML format (`tfctl.yaml` by default).
29
29
 
30
30
  The configuration is merged in the following order:
31
31
 
@@ -83,14 +83,16 @@ above.
83
83
 
84
84
  == Handling secrets
85
85
 
86
- No secrets should be committed into Terraform or tfctl configuration. Use AWS
87
- Secrets Manager instead and retrieve in Terraform profiles using
86
+ CAUTION: Do not commit secrets into your Terraform or tfctl configuration.
87
+
88
+ Instead, use AWS Secrets Manager and retrieve secrets in Terraform profiles using
89
+ the
88
90
  https://www.terraform.io/docs/providers/aws/d/secretsmanager_secret.html[secrets
89
- manager data source]
91
+ manager data source].
90
92
 
91
93
  == Configuration Schema
92
94
 
93
- Config file is validated using https://json-schema.org/[JSON Schema].
95
+ The configuration file is validated using https://json-schema.org/[JSON Schema].
94
96
 
95
97
  The schema is defined in
96
98
  https://github.com/scalefactory/tfctl/blob/master/lib/tfctl/schema.rb[lib/tfctl/schema.rb]
@@ -31,7 +31,7 @@ toc::[]
31
31
  For state tracking we're going to create a dedicated `shared-services` account
32
32
  under a `mgmt` organization unit. We'll use S3 for state storage and DynamoDB
33
33
  for locking. `TerraformState` IAM role will be created for cross account
34
- access to state resources from the primary account.
34
+ access to state resources from the primary AWS account.
35
35
 
36
36
  In the primary account we'll create a `TfctlOrgAccess` role. It gives tfctl
37
37
  read only access to AWS Organizations which is used to discover accounts and
@@ -50,9 +50,10 @@ and provision a couple of accounts for testing.
50
50
 
51
51
  Before starting you'll need:
52
52
 
53
- * Control Tower set up in your primary account.
54
- * A user with `AdministratorAccess` privileges in primary account.
55
- * 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.
56
57
  * Terraform 0.12 or higher.
57
58
 
58
59
  == Configure Control Tower
@@ -74,6 +75,7 @@ approximately 20 mins to provision one.
74
75
 
75
76
  == Install tfctl
76
77
 
78
+ [source,shell]
77
79
  ----
78
80
  git clone git@github.com:scalefactory/tfctl.git
79
81
  cd tfctl/ && sudo make install
@@ -85,17 +87,21 @@ It's assumed you have configured AWS CLI access to the primary account.
85
87
 
86
88
  We'll use CloudFormation templates in `examples/bootstrap/`.
87
89
 
88
- First export configuration using environment variables making sure to change to
89
- values to suit your set up:
90
+ First export configuration using environment variables, making sure to change to
91
+ values to suit your setup:
90
92
 
93
+ [source,shell]
91
94
  ----
92
- export PRIMARY_ACCOUNT_ID=11111111
93
- 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
94
98
  export STATE_BUCKET_NAME='example-terraform-state'
99
+ export AWS_REGION=eu-west-1
95
100
  ----
96
101
 
97
102
  Create the remote state resources stack set:
98
103
 
104
+ [source,shell]
99
105
  ----
100
106
  cd examples/bootstrap/
101
107
 
@@ -107,31 +113,34 @@ aws cloudformation create-stack-set \
107
113
  --execution-role-name AWSControlTowerExecution \
108
114
  --administration-role-arn arn:aws:iam::${PRIMARY_ACCOUNT_ID}:role/service-role/AWSControlTowerStackSetRole \
109
115
  --parameters ParameterKey=PrimaryAccountId,ParameterValue=${PRIMARY_ACCOUNT_ID} \
110
- ParameterKey=TerraformStateBucket,ParameterValue=${STATE_BUCKET_NAME}
116
+ ParameterKey=TerraformStateBucket,ParameterValue="${STATE_BUCKET_NAME}"
111
117
  ----
112
118
 
113
- Create a stack set instance in you shared services account:
119
+ Create a stack set instance in your shared services account:
114
120
 
121
+ [source,shell]
115
122
  ----
116
123
  aws cloudformation create-stack-instances \
117
124
  --stack-set-name TerraformState \
118
125
  --accounts ${SHARED_SERVICES_ACCOUNT_ID} \
119
- --regions eu-west-1
126
+ --regions ${AWS_REGION}
120
127
  ----
121
128
 
122
129
  Check status:
123
130
 
131
+ [source,shell]
124
132
  ----
125
133
  aws cloudformation describe-stack-instance \
126
134
  --stack-set-name TerraformState \
127
135
  --stack-instance-account ${SHARED_SERVICES_ACCOUNT_ID} \
128
- --stack-instance-region eu-west-1
136
+ --stack-instance-region ${AWS_REGION}
129
137
  ----
130
138
 
131
139
  NOTE: Initial status will be `OUTDATED`, it should change to `CURRENT` once deployed.
132
140
 
133
- Deploy `TfctlOrgAccess` IAM role stack:
141
+ Deploy the `TfctlOrgAccess` IAM role stack:
134
142
 
143
+ [source,shell]
135
144
  ----
136
145
  aws cloudformation create-stack \
137
146
  --stack-name TfctlOrgAccess \
@@ -142,6 +151,7 @@ aws cloudformation create-stack \
142
151
 
143
152
  Check status:
144
153
 
154
+ [source,shell]
145
155
  ----
146
156
  aws cloudformation describe-stacks --stack-name TfctlOrgAccess
147
157
  ----
@@ -158,7 +168,7 @@ You need to modify the following parameters:
158
168
  * `tf_state_bucket` - set to `$STATE_BUCKET_NAME`
159
169
  * `tf_state_role_arn` - set shared services account ID
160
170
  * `tfctl_role_arn` - set primary account ID
161
- * `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.
162
172
 
163
173
  TIP: You should keep your project directory under version control.
164
174
 
@@ -169,43 +179,51 @@ and `mgmt` OUs.
169
179
 
170
180
  NOTE: Run tfctl commands from the root of you project directory.
171
181
 
172
- First dump the configuration to verify everything works:
182
+ First, dump the configuration to verify everything works:
173
183
 
184
+ [source,shell]
174
185
  ----
175
186
  tfctl -s
176
187
  ----
177
188
 
178
- 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,
179
190
  merged configuration data. It should contain a list of discovered accounts and
180
191
  their configuration.
181
192
 
182
- Initialise terraform for all discovered accounts:
193
+ Initialise Terraform for all discovered accounts:
183
194
 
195
+ [source,shell]
184
196
  ----
185
197
  tfctl --all -- init
186
198
  ----
187
199
 
188
200
  Tfctl will run Terraform against all accounts in parallel.
189
201
 
190
- Run plan:
202
+ `plan` the Terraform:
191
203
 
204
+ [source,shell]
192
205
  ----
193
206
  tfctl --all -- plan
194
207
  ----
195
208
 
196
- and apply:
209
+ and `apply` it:
197
210
 
211
+ [source,shell]
198
212
  ----
199
213
  tfctl --all -- apply
200
214
  ----
201
215
 
202
- To destroy created resources run:
203
216
 
217
+ == Clean up
218
+
219
+ To destroy created resources, run:
220
+
221
+ [source,shell]
204
222
  ----
205
223
  tfctl --all -- destroy -auto-approve
206
224
  ----
207
225
 
208
- 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.
209
227
 
210
228
  TIP: Your project directory should be under version control excluding the
211
229
  `.tfctl` directory which is automatically generated.
@@ -20,7 +20,7 @@ endif::[]
20
20
 
21
21
  = IAM roles
22
22
 
23
- Tfctl usually requires three IAM roles to be configured:
23
+ `tfctl` usually requires three IAM roles to be configured:
24
24
 
25
25
  * `TfctlRole` - read only access to AWS Organizations set up in the primary account.
26
26
  * `TerraformStateRole` - access to remote state resources (S3, DynamoDB) in the
@@ -28,7 +28,7 @@ Tfctl usually requires three IAM roles to be configured:
28
28
  * `TerraformExecutionRole` - configured in all spoke accounts and used for executing Terraform.
29
29
 
30
30
  The user executing tfctl needs permission to assume all three roles cross
31
- account. Tfctl will assume roles automatically for you.
31
+ account. Once these are configured, tfctl automatically assumes roles for you.
32
32
 
33
33
  It's possible to configure different Terraform execution roles in different
34
34
  spoke accounts based on OU or account names. This can be used to restrict
@@ -50,12 +50,12 @@ The configuration data is exposed to terraform via a profile `config` variable.
50
50
  It also defines Terraform and tfctl configuration such as state tracking and
51
51
  what IAM roles to use.
52
52
 
53
- By default tfctl will use `tfctl.yaml` in it's current working directory. You
53
+ By default, tfctl will use `tfctl.yaml` in its current working directory. You
54
54
  can specify a different file using `-c`. Multiple configurations are supported
55
55
  in the same project directory and generated data will be stored separately for
56
56
  each config file in `.tfctl/`.
57
57
 
58
- == profiles
58
+ == `profiles`
59
59
 
60
60
  Profiles are re-usable collections of resources which can be applied to
61
61
  accounts. They are implemented just like usual modules but provide an
@@ -64,6 +64,6 @@ other data sources). Profiles often compose multiple modules and provide
64
64
  configuration data to them. This approach makes it possible to re-use standard
65
65
  modules (e.g. Terraform module registry).
66
66
 
67
- == modules
67
+ == `modules`
68
68
 
69
69
  Standard Terraform modules.
@@ -1,4 +1,4 @@
1
1
  module "bucket" {
2
2
  source = "../../modules/s3-bucket"
3
- name = "${local.account_id}-${local.account["example_bucket_name"]}"
3
+ name = "${local.account_id}-${local.account["data"]["example_bucket_name"]}"
4
4
  }
@@ -7,6 +7,6 @@ variable "config" {
7
7
  locals {
8
8
  config = jsondecode(var.config)
9
9
  account_id = "${data.aws_caller_identity.current.account_id}"
10
- # get current account configuration from tfctl config
10
+ # get account configuration from tfctl config
11
11
  account = [ for account in local.config["accounts"]: account if account["id"] == local.account_id ][0]
12
12
  }
@@ -5,7 +5,7 @@
5
5
  # create final configuration used by tfctl. You can view the merged
6
6
  # configuration by running:
7
7
  #
8
- # tfctl -c conf/example.yaml -s
8
+ # tfctl -c conf/tfctl.yaml -s
9
9
  #
10
10
 
11
11
  #
@@ -17,7 +17,7 @@ tf_state_dynamodb_table: 'terraform-lock'
17
17
  tf_state_region: 'eu-west-1'
18
18
  # Role for accessing state resources
19
19
  tf_state_role_arn: 'arn:aws:iam::SHARED_SERVICES_ACCOUNT_ID:role/TerraformStateRole'
20
- tf_required_version: '>= 0.12.0'
20
+ tf_required_version: '>= 0.12.29'
21
21
  aws_provider_version: '>= 2.14'
22
22
  # Role used by tfctl to retrieve data from AWS Organizations
23
23
  # Has to be set up in the primary org account
data/lib/hash.rb CHANGED
@@ -18,13 +18,14 @@ class Hash
18
18
  merge(second.to_h, &merger)
19
19
  end
20
20
 
21
- # Copied from ruby 2.6 Psych for 2.3 compatibility.
22
21
  def symbolize_names!(result = self)
23
22
  case result
24
23
  when Hash
24
+ # rubocop:disable Style/HashEachMethods
25
25
  result.keys.each do |key|
26
26
  result[key.to_sym] = symbolize_names!(result.delete(key))
27
27
  end
28
+ # rubocop:enable Style/HashEachMethods
28
29
  when Array
29
30
  result.map! { |r| symbolize_names!(r) }
30
31
  end
data/lib/tfctl.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'tfctl/aws_org.rb'
4
- require_relative 'tfctl/config.rb'
5
- require_relative 'tfctl/error.rb'
6
- require_relative 'tfctl/executor.rb'
7
- require_relative 'tfctl/generator.rb'
8
- require_relative 'tfctl/logger.rb'
9
- require_relative 'tfctl/schema.rb'
10
- require_relative 'tfctl/version.rb'
3
+ require_relative 'tfctl/aws_org'
4
+ require_relative 'tfctl/config'
5
+ require_relative 'tfctl/error'
6
+ require_relative 'tfctl/executor'
7
+ require_relative 'tfctl/generator'
8
+ require_relative 'tfctl/logger'
9
+ require_relative 'tfctl/schema'
10
+ require_relative 'tfctl/version'
data/lib/tfctl/aws_org.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'error.rb'
3
+ require_relative 'error'
4
4
  require 'aws-sdk-organizations'
5
5
 
6
6
  module Tfctl
@@ -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
data/lib/tfctl/config.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../hash.rb'
4
- require_relative 'error.rb'
3
+ require_relative '../hash'
4
+ require_relative 'error'
5
5
  require 'yaml'
6
6
  require 'json'
7
7
 
@@ -121,7 +121,9 @@ module Tfctl
121
121
  return config unless config.key?(:exclude_accounts)
122
122
 
123
123
  config[:accounts].each_with_index do |account, idx|
124
+ # rubocop:disable Style/IfWithBooleanLiteralBranches
124
125
  config[:accounts][idx][:excluded] = config[:exclude_accounts].include?(account[:name]) ? true : false
126
+ # rubocop:enable Style/IfWithBooleanLiteralBranches
125
127
  end
126
128
 
127
129
  config
@@ -3,7 +3,7 @@
3
3
  require 'open3'
4
4
  require 'fileutils'
5
5
  require 'shellwords'
6
- require_relative 'error.rb'
6
+ require_relative 'error'
7
7
 
8
8
  module Tfctl
9
9
  module Executor
@@ -10,21 +10,27 @@ module Tfctl
10
10
 
11
11
  def write_json_block(path, block)
12
12
  File.open(path, 'w') do |f|
13
- f.write(JSON.pretty_generate(block) + "\n")
13
+ f.write("#{JSON.pretty_generate(block)}\n")
14
14
  end
15
15
  end
16
16
 
17
17
  def make(account:, config:)
18
18
  target_dir = "#{PROJECT_ROOT}/.tfctl/#{config[:config_name]}/#{account[:name]}"
19
- tf_version = config.fetch(:tf_required_version, '>= 0.12.0')
19
+ tf_version = config.fetch(:tf_required_version, '>= 0.12.29')
20
20
  aws_provider_version = config.fetch(:aws_provider_version, '>= 2.14')
21
21
 
22
22
  FileUtils.mkdir_p target_dir
23
23
 
24
24
  terraform_block = {
25
25
  'terraform' => {
26
- 'required_version' => tf_version,
27
- 'backend' => {
26
+ 'required_version' => tf_version,
27
+ 'required_providers' => {
28
+ 'aws' => {
29
+ 'source' => 'hashicorp/aws',
30
+ 'version' => aws_provider_version,
31
+ },
32
+ },
33
+ 'backend' => {
28
34
  's3' => {
29
35
  'bucket' => config[:tf_state_bucket],
30
36
  'key' => "#{account[:name]}/tfstate",
@@ -41,7 +47,6 @@ module Tfctl
41
47
  provider_block = {
42
48
  'provider' => {
43
49
  'aws' => {
44
- 'version' => aws_provider_version,
45
50
  'region' => account[:region],
46
51
  'assume_role' => {
47
52
  'role_arn' => "arn:aws:iam::#{account[:id]}:role/#{account[:tf_execution_role]}",
@@ -71,11 +76,8 @@ module Tfctl
71
76
  profile_block = {
72
77
  'module' => {
73
78
  profile => {
74
- 'source' => "../../../profiles/#{profile}",
75
- 'config' => '${var.config}',
76
- 'providers' => {
77
- 'aws' => 'aws',
78
- },
79
+ 'source' => "../../../profiles/#{profile}",
80
+ 'config' => '${var.config}',
79
81
  },
80
82
  },
81
83
  }
data/lib/tfctl/logger.rb CHANGED
@@ -6,7 +6,7 @@ module Tfctl
6
6
  class Logger
7
7
 
8
8
  def initialize(log_level)
9
- @outlog = ::Logger.new(STDOUT)
9
+ @outlog = ::Logger.new($stdout)
10
10
 
11
11
  self.level = log_level
12
12
 
data/lib/tfctl/schema.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json_schemer'
4
- require_relative 'error.rb'
4
+ require_relative 'error'
5
5
 
6
6
  # Config validator using JSON schema
7
7
 
data/lib/tfctl/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tfctl
4
- VERSION = '1.1.0'
4
+ VERSION = '1.4.0'
5
5
  end
data/tfctl.gemspec CHANGED
@@ -23,14 +23,17 @@ 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'
31
- spec.add_dependency 'terminal-table', '~> 1.8'
32
+ spec.add_dependency 'parallel', '~> 1.19'
33
+ spec.add_dependency 'terminal-table', '>= 1.8', '< 4.0'
32
34
 
33
- spec.add_development_dependency 'guard-rspec', '~> 4.7'
34
- spec.add_development_dependency 'rspec', '~> 3.8'
35
- spec.add_development_dependency 'rubocop', '~> 0.76'
35
+ spec.add_development_dependency 'guard-rspec', '~> 4.7'
36
+ spec.add_development_dependency 'rspec', '~> 3.9'
37
+ spec.add_development_dependency 'rubocop', '~> 1.3'
38
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.2'
36
39
  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.1.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Wasilczuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-15 00:00:00.000000000 Z
11
+ date: 2021-06-23 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,28 +44,34 @@ 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
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '1.8'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '4.0'
62
65
  type: :runtime
63
66
  prerelease: false
64
67
  version_requirements: !ruby/object:Gem::Requirement
65
68
  requirements:
66
- - - "~>"
69
+ - - ">="
67
70
  - !ruby/object:Gem::Version
68
71
  version: '1.8'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '4.0'
69
75
  - !ruby/object:Gem::Dependency
70
76
  name: guard-rspec
71
77
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +92,42 @@ dependencies:
86
92
  requirements:
87
93
  - - "~>"
88
94
  - !ruby/object:Gem::Version
89
- version: '3.8'
95
+ version: '3.9'
90
96
  type: :development
91
97
  prerelease: false
92
98
  version_requirements: !ruby/object:Gem::Requirement
93
99
  requirements:
94
100
  - - "~>"
95
101
  - !ruby/object:Gem::Version
96
- version: '3.8'
102
+ version: '3.9'
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: rubocop
99
105
  requirement: !ruby/object:Gem::Requirement
100
106
  requirements:
101
107
  - - "~>"
102
108
  - !ruby/object:Gem::Version
103
- version: '0.76'
109
+ version: '1.3'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.3'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rubocop-rspec
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '2.2'
104
124
  type: :development
105
125
  prerelease: false
106
126
  version_requirements: !ruby/object:Gem::Requirement
107
127
  requirements:
108
128
  - - "~>"
109
129
  - !ruby/object:Gem::Version
110
- version: '0.76'
130
+ version: '2.2'
111
131
  description:
112
132
  email:
113
133
  - akw@scalefactory.com
@@ -116,16 +136,21 @@ executables:
116
136
  extensions: []
117
137
  extra_rdoc_files: []
118
138
  files:
139
+ - ".bundle/config"
140
+ - ".github/dependabot.yml"
141
+ - ".github/workflows/linter.yml"
142
+ - ".github/workflows/release.yml"
143
+ - ".github/workflows/test.yml"
119
144
  - ".gitignore"
120
145
  - ".rspec"
121
146
  - ".rubocop.yml"
122
- - ".travis.yml"
123
147
  - CHANGELOG.adoc
124
148
  - Gemfile
125
149
  - Guardfile
126
150
  - LICENSE
127
151
  - Makefile
128
152
  - README.adoc
153
+ - RELEASING.adoc
129
154
  - bin/tfctl
130
155
  - docs/configuration.adoc
131
156
  - docs/control_tower.adoc
@@ -164,15 +189,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
189
  requirements:
165
190
  - - ">="
166
191
  - !ruby/object:Gem::Version
167
- version: '0'
192
+ version: 2.5.0
168
193
  required_rubygems_version: !ruby/object:Gem::Requirement
169
194
  requirements:
170
195
  - - ">="
171
196
  - !ruby/object:Gem::Version
172
197
  version: '0'
173
198
  requirements: []
174
- rubyforge_project:
175
- rubygems_version: 2.7.7
199
+ rubygems_version: 3.0.3.1
176
200
  signing_key:
177
201
  specification_version: 4
178
202
  summary: Terraform wrapper for managing multi-account AWS infrastructures
data/.travis.yml DELETED
@@ -1,18 +0,0 @@
1
- rvm:
2
- - 2.3
3
- - 2.6
4
- os: linux
5
- language: ruby
6
- script: make test
7
- jobs:
8
- include:
9
- - stage: Gem release
10
- rvm: 2.6
11
- deploy:
12
- provider: rubygems
13
- api_key:
14
- secure: LAVcdER+LtQ2TSUrVOY7Be1BC7GXJRD0QBt386vRM5Nld5QaD9Ow9gtN6FprzkzloI4R8BkPWqZbAT6YjC+C0AFB5HK6iPwD2bLsiF9w3ccDD+yrW99RHxiErpmYMun2PqZv0WkJ/pkEplPCKMRFv7SM7W9DMRlU7dsXc1v6IVyIb5u3A04jErS2jXXKY0ijlCDJYVo8zzYL6yUmUcXhc//3CIVnu2Miu6Qr8h7e6jMXNUWfMkwEXsFP9id4TsCz7hRY+39PkiBAknHTN5UqjjJiEOknZnHeTBcVPvi2h2xv+fFLSzVTxlxaRsVoMCShQp5D12qzhQObRJsRVQFs8Yyg9IYMyPdxssFYyUZFaAy5taWDm57uM3HTHylm/Dq3LmXTgGNxWUUkf2oh1g7R6cYZpBUQwiEPzhZQ7CoBQbGUAJmH9ZU9m+cr8kuAOUipd6BNEDvn/fIH4WJsRCNP72JGX16JBpuICvpkuNhskZT91xFlYk1pTXHOxNpbTcxUTgMHhrTqspeRXPmf6DiYGvjMb2S6kaoGqCIRIcwl0TGKuMsOMqR9SqF8gubkqHMVbSl1E7mwBn4ke8/7IGoMkWOGwUpVxqVOLBHi6zSR09RTVSbKl4oiFV3ZwmVPSxDncq54MptyJ2WCZ7dD6ht2l+VA8iGwYeIoqOpwGWxyuNI=
15
- gem: tfctl
16
- on:
17
- tags: true
18
- repo: scalefactory/tfctl