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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a7ca446d4435cb6780d2b8018e89c8f7b981a247d249ff3fe8083b94edd66ac
4
- data.tar.gz: bc7f96ea1cb77fb6366453f4e268e55be5c398e939002f382223c94e09bccbf0
3
+ metadata.gz: 0a21de0b7977bcd56a7055e677dc1689e07b7e62e89d29319fb5e432100f434f
4
+ data.tar.gz: ac7a0f930a88e683692e5863fcbf2aecb7b0cfe918aa891fe5f6f3df656b901f
5
5
  SHA512:
6
- metadata.gz: 777d4fd03430d1d54bcf822f3f4087c7e0c55374d69167487057201ef20132abe5de5c2b949cfa33aa1d48c346a9e0cd819db8193af5a8803dacb7d3988e526e
7
- data.tar.gz: 5cc2a820f7930d930efc43c8eab98e9b825e6d1cf48390ca929f01b4136cdbd33509978a31e16f5f4956c458e007bc6dc759e86ee9305b4f14d21c77f267d8ab
6
+ metadata.gz: a266d7e164849d1d31be19ffc37572e591c74f4b33fece71de1db510088f3221b6dcf454b5173ffc0cbdae5f2fdc2bf54fc92dab82862cf34cb188fff37953ee
7
+ data.tar.gz: 6e9f97dbf88dc5a759545b88a3031b9cf2dcb72ab3d5120ba11c9a89c6d15a3fb381d8ed834dac18cd2f4e8f64f0d5862b6eaf57025c1e5541cfe621192f4de5
data/.bundle/config CHANGED
@@ -1,3 +1,4 @@
1
1
  ---
2
2
  BUNDLE_PATH: "vendor/bundle"
3
3
  BUNDLE_BIN: "vendor/bin"
4
+ BUNDLE_WITH: "developement"
@@ -1,6 +1,9 @@
1
1
  name: Linting
2
2
 
3
- on: [push]
3
+ on:
4
+ push:
5
+ pull_request:
6
+ branches: [ master ]
4
7
 
5
8
  jobs:
6
9
  lint:
@@ -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: actions/create-release@v1
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
- release_name: 'Release ${{ github.ref }}'
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://travis-ci.com/scalefactory/tfctl.svg?branch=master["Build Status", link="https://travis-ci.com/scalefactory/tfctl"]
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 Organizational Units (OUs),
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
- * 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
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 'tfctl running'
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'
@@ -2,3 +2,7 @@ resource aws_s3_bucket bucket {
2
2
  bucket = var.name
3
3
  acl = "private"
4
4
  }
5
+
6
+ output "arn" {
7
+ value = aws_s3_bucket.bucket.arn
8
+ }
@@ -1,4 +1,11 @@
1
+ resource "random_pet" "bucket_prefix" {
2
+ }
3
+
1
4
  module "bucket" {
2
5
  source = "../../modules/s3-bucket"
3
- name = "${local.account_id}-${local.account["data"]["example_bucket_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
  }
@@ -0,0 +1,7 @@
1
+ terraform {
2
+ required_providers {
3
+ aws = {
4
+ source = "hashicorp/aws"
5
+ }
6
+ }
7
+ }
@@ -6,7 +6,7 @@ variable "config" {
6
6
 
7
7
  locals {
8
8
  config = jsondecode(var.config)
9
- account_id = "${data.aws_caller_identity.current.account_id}"
9
+ account_id = data.aws_caller_identity.current.account_id
10
10
  # get account configuration from tfctl config
11
- account = [ for account in local.config["accounts"]: account if account["id"] == local.account_id ][0]
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).accounts.each do |account|
29
- next unless account.status == 'ACTIVE'
30
-
31
- output[:accounts] << {
32
- name: account.name,
33
- id: account.id,
34
- arn: account.arn,
35
- email: account.email,
36
- ou_path: ou_path.to_s,
37
- ou_parents: ou_path.to_s.split('/'),
38
- profiles: [],
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 |resp|
75
- resp.children.each do |child|
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
@@ -105,7 +105,7 @@ module Tfctl
105
105
 
106
106
  def write_cache(cache_file)
107
107
  FileUtils.mkdir_p File.dirname(cache_file)
108
- File.open(cache_file, 'w') { |f| f.write to_yaml }
108
+ File.write(cache_file, to_yaml)
109
109
  end
110
110
 
111
111
  def read_cache(cache_file)
@@ -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: #{exec.shelljoin}"
58
+ log.debug "#{account_name}: Executing: #{runcmd}"
53
59
 
54
60
  FileUtils.cd path
55
- Open3.popen3(env, exec.shelljoin) do |stdin, stdout, stderr, wait_thr|
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
@@ -9,13 +9,12 @@ module Tfctl
9
9
  module_function
10
10
 
11
11
  def write_json_block(path, block)
12
- File.open(path, 'w') do |f|
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' => "#{account[:name]}/tfstate",
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' => account[: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' => "../../../profiles/#{profile}",
80
- 'config' => '${var.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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tfctl
4
- VERSION = '1.4.0'
4
+ VERSION = '1.6.1'
5
5
  end
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.0
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: 2021-06-23 00:00:00.000000000 Z
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: