tfctl 1.4.0 → 1.6.1
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 +4 -4
- data/.bundle/config +1 -0
- data/.github/workflows/linter.yml +4 -1
- data/.github/workflows/release.yml +4 -5
- data/CHANGELOG.adoc +30 -0
- data/README.adoc +21 -21
- data/RELEASING.adoc +11 -5
- data/bin/tfctl +1 -1
- data/examples/control_tower/modules/s3-bucket/main.tf +4 -0
- data/examples/control_tower/profiles/example-profile/main.tf +8 -1
- data/examples/control_tower/profiles/example-profile/terraform.tf +7 -0
- data/examples/control_tower/profiles/example-profile/variables.tf +2 -2
- data/examples/control_tower/tfctl.yaml +2 -0
- data/lib/tfctl/aws_org.rb +17 -14
- data/lib/tfctl/config.rb +1 -1
- data/lib/tfctl/executor.rb +8 -2
- data/lib/tfctl/generator.rb +13 -8
- data/lib/tfctl/schema.rb +2 -0
- data/lib/tfctl/version.rb +1 -1
- data/tfctl.gemspec +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a21de0b7977bcd56a7055e677dc1689e07b7e62e89d29319fb5e432100f434f
|
4
|
+
data.tar.gz: ac7a0f930a88e683692e5863fcbf2aecb7b0cfe918aa891fe5f6f3df656b901f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a266d7e164849d1d31be19ffc37572e591c74f4b33fece71de1db510088f3221b6dcf454b5173ffc0cbdae5f2fdc2bf54fc92dab82862cf34cb188fff37953ee
|
7
|
+
data.tar.gz: 6e9f97dbf88dc5a759545b88a3031b9cf2dcb72ab3d5120ba11c9a89c6d15a3fb381d8ed834dac18cd2f4e8f64f0d5862b6eaf57025c1e5541cfe621192f4de5
|
data/.bundle/config
CHANGED
@@ -27,11 +27,10 @@ jobs:
|
|
27
27
|
env:
|
28
28
|
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
29
29
|
- name: Release on GitHub
|
30
|
-
uses:
|
31
|
-
env:
|
32
|
-
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
30
|
+
uses: ncipollo/release-action@v1
|
33
31
|
with:
|
32
|
+
body: 'See [CHANGELOG](https://github.com/scalefactory/tfctl/blob/master/CHANGELOG.adoc) for details.'
|
33
|
+
token: "${{ secrets.GITHUB_TOKEN }}"
|
34
34
|
draft: false
|
35
35
|
prerelease: false
|
36
|
-
|
37
|
-
tag_name: '${{ github.ref }}'
|
36
|
+
artifacts: pkg/*.gem
|
data/CHANGELOG.adoc
CHANGED
@@ -1,5 +1,35 @@
|
|
1
1
|
= Changelog
|
2
2
|
|
3
|
+
== 1.6.1
|
4
|
+
|
5
|
+
* fix: pagination problem when listing accounts in an OU.
|
6
|
+
|
7
|
+
== 1.6.0
|
8
|
+
|
9
|
+
* fix: pass the default AWS provider explicitly from tfctl generated configuration.
|
10
|
+
This fixes provider inheritance issues when using multiple providers which
|
11
|
+
was introduced in 1.3.0. You may need to add a terraform block with
|
12
|
+
`required_provides` to your profiles if you don't have it defined already.
|
13
|
+
Terraform will warn about this during `init`. Here's an example block:
|
14
|
+
|
15
|
+
----
|
16
|
+
terraform {
|
17
|
+
required_providers {
|
18
|
+
aws = {
|
19
|
+
source = "hashicorp/aws"
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
----
|
24
|
+
|
25
|
+
== 1.5.0
|
26
|
+
|
27
|
+
* feat: support for setting default tags at AWS provider level. (Thanks @patrickli)
|
28
|
+
For details see: https://www.hashicorp.com/blog/default-tags-in-the-terraform-aws-provider
|
29
|
+
* feat: new `tf_state_prefix` config parameter. (Thanks @patrickli)
|
30
|
+
Allows setting an path prefix for state files stored in S3.
|
31
|
+
* feat: print version number in output log
|
32
|
+
|
3
33
|
== 1.4.0
|
4
34
|
|
5
35
|
* feat: support yaml anchors and aliases in configuration file.
|
data/README.adoc
CHANGED
@@ -20,7 +20,8 @@ endif::[]
|
|
20
20
|
|
21
21
|
= tfctl
|
22
22
|
|
23
|
-
image:https://
|
23
|
+
image:https://github.com/scalefactory/tfctl/actions/workflows/linter.yml/badge.svg["Linter", link="https://github.com/scalefactory/tfctl/actions/workflows/linter.yml"]
|
24
|
+
image:https://github.com/scalefactory/tfctl/actions/workflows/test.yml/badge.svg["Tests", link="https://github.com/scalefactory/tfctl/actions/workflows/test.yml"]
|
24
25
|
image:https://badge.fury.io/rb/tfctl.svg["Gem Version", link="https://badge.fury.io/rb/tfctl"]
|
25
26
|
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
27
|
|
@@ -33,32 +34,14 @@ infrastructures where new accounts may be created dynamically and on-demand.
|
|
33
34
|
|
34
35
|
It discovers accounts by reading the AWS Organizations API, and can assign
|
35
36
|
Terraform resources to multiple accounts based on the organization hierarchy.
|
36
|
-
Resources can be assigned globally, based on organization unit or to individual
|
37
|
-
accounts. It supports hierarchies of nested
|
38
|
-
and helps keep your Terraform DRY.
|
37
|
+
Resources can be assigned globally, based on organization unit (OU) or to individual
|
38
|
+
accounts. It supports hierarchies of nested OUs, and helps keep your Terraform DRY.
|
39
39
|
|
40
40
|
The Scale Factory originally created tfctl to integrate Terraform with
|
41
41
|
https://aws.amazon.com/solutions/aws-landing-zone/[AWS Landing Zone] and
|
42
42
|
https://aws.amazon.com/controltower/[Control Tower] but should work with most
|
43
43
|
other ways of managing accounts in AWS Organizations.
|
44
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
|
-
|
62
45
|
== Features
|
63
46
|
|
64
47
|
* Discovers AWS accounts automatically.
|
@@ -206,3 +189,20 @@ tfctl -a example-account -u -- plan
|
|
206
189
|
This will show output in real time. Usually output is buffered and displayed
|
207
190
|
after the Terraform command finishes, to make it more readable when running
|
208
191
|
across multiple accounts in parallel.
|
192
|
+
|
193
|
+
== Project status
|
194
|
+
|
195
|
+
`tfctl` is an open source project published by The Scale Factory.
|
196
|
+
|
197
|
+
We currently consider this project to be maintained but we don't actively
|
198
|
+
develop new features. We keep it security patched and ready for use in
|
199
|
+
production environments.
|
200
|
+
|
201
|
+
We’ll take a look at any issues or PRs you open and get back to you as soon as
|
202
|
+
we can. We don’t offer any formal SLA, but we’ll be checking on this project
|
203
|
+
periodically.
|
204
|
+
|
205
|
+
If your issue is urgent, you can flag it as such, and we’ll attempt to triage
|
206
|
+
appropriately, but we have paying customers who also have demands on our time.
|
207
|
+
If your business depends on this project and you have an urgent problem, then
|
208
|
+
you can talk to our sales team about paying us to support you.
|
data/RELEASING.adoc
CHANGED
@@ -6,8 +6,14 @@ releasing a new gem version.
|
|
6
6
|
== Process
|
7
7
|
|
8
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
|
-
*
|
13
|
-
|
9
|
+
* Bump version in `lib/tfctl/version.rb`.
|
10
|
+
* Update `CHANGELOG.adoc`.
|
11
|
+
* Commit.
|
12
|
+
* Tag the release using format: vX.X.X and push the tag.
|
13
|
+
|
14
|
+
----
|
15
|
+
git tag vX.X.X
|
16
|
+
git push origin vX.X.X
|
17
|
+
----
|
18
|
+
|
19
|
+
* GitHub actions will build and release the gem and create a GitHub release automatically.
|
data/bin/tfctl
CHANGED
@@ -138,7 +138,7 @@ begin
|
|
138
138
|
log_level = options[:debug] ? Logger::DEBUG : Logger::INFO
|
139
139
|
log = Tfctl::Logger.new(log_level)
|
140
140
|
|
141
|
-
log.info
|
141
|
+
log.info "tfctl #{Tfctl::VERSION} running"
|
142
142
|
|
143
143
|
config_name = File.basename(options[:config_file]).chomp('.yaml')
|
144
144
|
config_name = 'default' if config_name == 'tfctl'
|
@@ -1,4 +1,11 @@
|
|
1
|
+
resource "random_pet" "bucket_prefix" {
|
2
|
+
}
|
3
|
+
|
1
4
|
module "bucket" {
|
2
5
|
source = "../../modules/s3-bucket"
|
3
|
-
name = "${
|
6
|
+
name = "${random_pet.bucket_prefix.id}-${local.account["data"]["example_bucket_name"]}"
|
7
|
+
}
|
8
|
+
|
9
|
+
output "bucket_arn" {
|
10
|
+
value = module.bucket.arn
|
4
11
|
}
|
@@ -6,7 +6,7 @@ variable "config" {
|
|
6
6
|
|
7
7
|
locals {
|
8
8
|
config = jsondecode(var.config)
|
9
|
-
account_id =
|
9
|
+
account_id = data.aws_caller_identity.current.account_id
|
10
10
|
# get account configuration from tfctl config
|
11
|
-
account
|
11
|
+
account = [for account in local.config["accounts"] : account if account["id"] == local.account_id][0]
|
12
12
|
}
|
@@ -13,6 +13,7 @@
|
|
13
13
|
#
|
14
14
|
|
15
15
|
tf_state_bucket: 'CHANGEME'
|
16
|
+
# tf_state_prefix: ''
|
16
17
|
tf_state_dynamodb_table: 'terraform-lock'
|
17
18
|
tf_state_region: 'eu-west-1'
|
18
19
|
# Role for accessing state resources
|
@@ -22,6 +23,7 @@ aws_provider_version: '>= 2.14'
|
|
22
23
|
# Role used by tfctl to retrieve data from AWS Organizations
|
23
24
|
# Has to be set up in the primary org account
|
24
25
|
tfctl_role_arn: 'arn:aws:iam::PRIMARY_ACCOUNT_ID:role/TfctlRole'
|
26
|
+
# default_tags: {}
|
25
27
|
|
26
28
|
#
|
27
29
|
# Data
|
data/lib/tfctl/aws_org.rb
CHANGED
@@ -25,19 +25,22 @@ module Tfctl
|
|
25
25
|
|
26
26
|
parent_id = aws_ou_ids[ou_path]
|
27
27
|
|
28
|
-
@aws_org_client.list_accounts_for_parent(parent_id: parent_id).
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
28
|
+
@aws_org_client.list_accounts_for_parent(parent_id: parent_id).each do |response|
|
29
|
+
response.accounts.each do |account|
|
30
|
+
next unless account.status == 'ACTIVE'
|
31
|
+
|
32
|
+
output[:accounts] << {
|
33
|
+
name: account.name,
|
34
|
+
id: account.id,
|
35
|
+
arn: account.arn,
|
36
|
+
email: account.email,
|
37
|
+
ou_path: ou_path.to_s,
|
38
|
+
ou_parents: ou_path.to_s.split('/'),
|
39
|
+
profiles: [],
|
40
|
+
}
|
41
|
+
end
|
40
42
|
end
|
43
|
+
|
41
44
|
end
|
42
45
|
output
|
43
46
|
end
|
@@ -71,8 +74,8 @@ module Tfctl
|
|
71
74
|
@aws_org_client.list_children(
|
72
75
|
child_type: 'ORGANIZATIONAL_UNIT',
|
73
76
|
parent_id: parent_id,
|
74
|
-
).each do |
|
75
|
-
|
77
|
+
).each do |response|
|
78
|
+
response.children.each do |child|
|
76
79
|
|
77
80
|
begin
|
78
81
|
ou = @aws_org_client.describe_organizational_unit(
|
data/lib/tfctl/config.rb
CHANGED
data/lib/tfctl/executor.rb
CHANGED
@@ -41,6 +41,12 @@ module Tfctl
|
|
41
41
|
# Create the command
|
42
42
|
exec = [cmd] + [subcmd] + args
|
43
43
|
|
44
|
+
runcmd = if Gem.win_platform?
|
45
|
+
exec.join(' ')
|
46
|
+
else
|
47
|
+
exec.shelljoin
|
48
|
+
end
|
49
|
+
|
44
50
|
# Set environment variables for Terraform
|
45
51
|
env = {
|
46
52
|
'TF_INPUT' => '0',
|
@@ -49,10 +55,10 @@ module Tfctl
|
|
49
55
|
# 'TF_LOG' => 'TRACE'
|
50
56
|
}
|
51
57
|
|
52
|
-
log.debug "#{account_name}: Executing: #{
|
58
|
+
log.debug "#{account_name}: Executing: #{runcmd}"
|
53
59
|
|
54
60
|
FileUtils.cd path
|
55
|
-
Open3.popen3(env,
|
61
|
+
Open3.popen3(env, runcmd) do |stdin, stdout, stderr, wait_thr|
|
56
62
|
stdin.close_write
|
57
63
|
|
58
64
|
# capture stdout and stderr in separate threads to prevent deadlocks
|
data/lib/tfctl/generator.rb
CHANGED
@@ -9,13 +9,12 @@ module Tfctl
|
|
9
9
|
module_function
|
10
10
|
|
11
11
|
def write_json_block(path, block)
|
12
|
-
File.
|
13
|
-
f.write("#{JSON.pretty_generate(block)}\n")
|
14
|
-
end
|
12
|
+
File.write(path, "#{JSON.pretty_generate(block)}\n")
|
15
13
|
end
|
16
14
|
|
17
15
|
def make(account:, config:)
|
18
16
|
target_dir = "#{PROJECT_ROOT}/.tfctl/#{config[:config_name]}/#{account[:name]}"
|
17
|
+
tf_state_prefix = config.fetch(:tf_state_prefix, '').delete_suffix('/')
|
19
18
|
tf_version = config.fetch(:tf_required_version, '>= 0.12.29')
|
20
19
|
aws_provider_version = config.fetch(:aws_provider_version, '>= 2.14')
|
21
20
|
|
@@ -33,7 +32,7 @@ module Tfctl
|
|
33
32
|
'backend' => {
|
34
33
|
's3' => {
|
35
34
|
'bucket' => config[:tf_state_bucket],
|
36
|
-
'key' =>
|
35
|
+
'key' => [tf_state_prefix, account[:name], 'tfstate'].join('/').delete_prefix('/'),
|
37
36
|
'region' => config[:tf_state_region],
|
38
37
|
'role_arn' => config[:tf_state_role_arn],
|
39
38
|
'dynamodb_table' => config[:tf_state_dynamodb_table],
|
@@ -47,10 +46,13 @@ module Tfctl
|
|
47
46
|
provider_block = {
|
48
47
|
'provider' => {
|
49
48
|
'aws' => {
|
50
|
-
'region'
|
51
|
-
'assume_role'
|
49
|
+
'region' => account[:region],
|
50
|
+
'assume_role' => {
|
52
51
|
'role_arn' => "arn:aws:iam::#{account[:id]}:role/#{account[:tf_execution_role]}",
|
53
52
|
},
|
53
|
+
'default_tags' => {
|
54
|
+
'tags' => config.fetch(:default_tags, {}),
|
55
|
+
},
|
54
56
|
},
|
55
57
|
},
|
56
58
|
}
|
@@ -76,8 +78,11 @@ module Tfctl
|
|
76
78
|
profile_block = {
|
77
79
|
'module' => {
|
78
80
|
profile => {
|
79
|
-
'source'
|
80
|
-
'config'
|
81
|
+
'source' => "../../../profiles/#{profile}",
|
82
|
+
'config' => '${var.config}',
|
83
|
+
'providers' => {
|
84
|
+
'aws' => 'aws',
|
85
|
+
},
|
81
86
|
},
|
82
87
|
},
|
83
88
|
}
|
data/lib/tfctl/schema.rb
CHANGED
@@ -34,6 +34,7 @@ module Tfctl
|
|
34
34
|
'type' => 'object',
|
35
35
|
'properties' => {
|
36
36
|
'tf_state_bucket' => { 'type' => 'string' },
|
37
|
+
'tf_state_prefix' => { 'type' => 'string' },
|
37
38
|
'tf_state_role_arn' => {
|
38
39
|
'type' => 'string',
|
39
40
|
'pattern' => iam_arn_pattern,
|
@@ -48,6 +49,7 @@ module Tfctl
|
|
48
49
|
},
|
49
50
|
'data' => { 'type' => 'object' },
|
50
51
|
'exclude_accounts' => { 'type' => 'array' },
|
52
|
+
'default_tags' => { 'type' => 'object' },
|
51
53
|
'organization_root' => org_schema,
|
52
54
|
'organization_units' => org_schema,
|
53
55
|
'account_overrides' => org_schema,
|
data/lib/tfctl/version.rb
CHANGED
data/tfctl.gemspec
CHANGED
@@ -20,7 +20,6 @@ Gem::Specification.new do |spec|
|
|
20
20
|
end
|
21
21
|
spec.bindir = 'bin'
|
22
22
|
spec.executables = spec.files.grep(%r{^bin/tfctl}) { |f| File.basename(f) }
|
23
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
23
|
spec.require_paths = ['lib']
|
25
24
|
|
26
25
|
spec.required_ruby_version = '>= 2.5.0'
|
@@ -36,4 +35,5 @@ Gem::Specification.new do |spec|
|
|
36
35
|
spec.add_development_dependency 'rspec', '~> 3.9'
|
37
36
|
spec.add_development_dependency 'rubocop', '~> 1.3'
|
38
37
|
spec.add_development_dependency 'rubocop-rspec', '~> 2.2'
|
38
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
39
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.
|
4
|
+
version: 1.6.1
|
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: 2022-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-organizations
|
@@ -164,6 +164,7 @@ files:
|
|
164
164
|
- examples/control_tower/modules/s3-bucket/variables.tf
|
165
165
|
- examples/control_tower/profiles/example-profile/data.tf
|
166
166
|
- examples/control_tower/profiles/example-profile/main.tf
|
167
|
+
- examples/control_tower/profiles/example-profile/terraform.tf
|
167
168
|
- examples/control_tower/profiles/example-profile/variables.tf
|
168
169
|
- examples/control_tower/tfctl.yaml
|
169
170
|
- lib/hash.rb
|
@@ -180,7 +181,8 @@ files:
|
|
180
181
|
homepage: https://github.com/scalefactory/tfctl
|
181
182
|
licenses:
|
182
183
|
- MIT
|
183
|
-
metadata:
|
184
|
+
metadata:
|
185
|
+
rubygems_mfa_required: 'true'
|
184
186
|
post_install_message:
|
185
187
|
rdoc_options: []
|
186
188
|
require_paths:
|