tfctl 0.2.0 → 1.0.0.rc1

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: c01c64c623610d77abd88401c0bbea5d56eb4963b58d7ad3178a777d5d297ead
4
- data.tar.gz: f6bbe78e0bc1f504105bcfa3362ce90368e5cd79b4b075a92e57b8adadbce1c1
3
+ metadata.gz: 7851ad9d647739e471f8d430dace8564f16f403f3a7e7ffb98e21384db641f53
4
+ data.tar.gz: d52713addc4006e2e67f5c3fce56941697d4a9edfb6bea08953c76eeb673c2f1
5
5
  SHA512:
6
- metadata.gz: 2eb1394b05e67619f9a2498348f4a1f96b2eb4bf0afd7252c74d822c3f35e08a6bc953c7eae95388ab2a5e34f5c8c7dbde91db9f59f0f02cbba51e23030542e2
7
- data.tar.gz: a17c9e00867b95243e47ddc495a662a2464dcc251148fb7bfd51e8c380de6845235a4b7411fd5c833cbcdb5c3f582ff2bbbafc21f5c7bb2bb707c3955cfd9381
6
+ metadata.gz: 10f6a9bce18e905d787783a6f61a3f93724b4809d1c7f2c7e7c1edc155ba2c14b17a97096140f790c9b7e250dc4fd24f7af5c86fefea8139d25e1b15dddc65d3
7
+ data.tar.gz: 9e81ab6abbc15df5917ea6b23a1b167a2cd745e057d462ce0e28db47b27c6f12477c62293893c59f1977c5c406df31dbb797818fc27978b5bd9255a12bd86370
data/CHANGELOG.adoc CHANGED
@@ -1,5 +1,15 @@
1
1
  = Changelog
2
2
 
3
+ == 1.0.0-rc1 (unreleased)
4
+
5
+ * feat(config): JSON schema config validation
6
+ * feat(config): added 'data' parameter
7
+
8
+ BREAKING CHANGE: This release moves user defined data under a separate `data`
9
+ parameter so it can be easily distinguished from parameters required by tfctl.
10
+ Configuration file will need to be updated to reflect this to pass validation.
11
+
12
+
3
13
  == 0.2.0
4
14
 
5
15
  * feat: configurable Terraform and AWS provider version requirements
data/README.adoc CHANGED
@@ -1,19 +1,40 @@
1
- :toc:
1
+ // Settings:
2
+ :idprefix:
3
+ :idseparator: -
4
+ ifndef::env-github[:icons: font]
5
+ ifdef::env-github,env-browser[]
6
+ :toc: macro
7
+ :toclevels: 1
8
+ endif::[]
9
+ ifdef::env-github[]
10
+ :branch: master
11
+ :status:
12
+ :outfilesuffix: .adoc
13
+ :!toc-title:
14
+ :caution-caption: :fire:
15
+ :important-caption: :exclamation:
16
+ :note-caption: :paperclip:
17
+ :tip-caption: :bulb:
18
+ :warning-caption: :warning:
19
+ endif::[]
2
20
 
3
21
  = tfctl
4
22
 
5
23
  image:https://travis-ci.org/scalefactory/tfctl.svg?branch=master["Build Status", link="https://travis-ci.org/scalefactory/tfctl"]
6
24
  image:https://badge.fury.io/rb/tfctl.svg["Gem Version", link="https://badge.fury.io/rb/tfctl"]
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
+
27
+ toc::[]
7
28
 
8
29
  == Overview
9
30
 
10
31
  Tfctl is a small Terraform wrapper for working with multi-account AWS
11
32
  infrastructures where new accounts may be created dynamically and on-demand.
12
33
 
13
- Discovers accounts by reading the AWS Organizations API and can assign
14
- Terraform resources to accounts based on the organization hierarchy. Resources
15
- can be assigned globally, based on organization unit or individual accounts.
16
- It supports nested OU hierarchies.
34
+ It discovers accounts by reading the AWS Organizations API and can assign
35
+ 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 nested OU hierarchies and helps keep your Terraform DRY.
17
38
 
18
39
  Tfctl was originally developed to integrate Terraform with
19
40
  https://aws.amazon.com/solutions/aws-landing-zone/[AWS Landing Zone] and
data/bin/tfctl CHANGED
@@ -150,6 +150,7 @@ begin
150
150
  log.info 'Working out AWS account topology'
151
151
 
152
152
  yaml_config = YAML.safe_load(File.read(options[:config_file]))
153
+ Tfctl::Schema.validate(yaml_config)
153
154
  yaml_config.symbolize_names!
154
155
 
155
156
  org_units = yaml_config[:organization_units].keys
@@ -212,4 +213,11 @@ begin
212
213
  rescue Tfctl::Error => e
213
214
  log.error(e)
214
215
  exit 1
216
+ rescue Tfctl::ValidationError => e
217
+ log.error(e)
218
+ e.issues.each do |issue|
219
+ log.error("Parameter: #{issue[:data_pointer]}") unless issue[:data_pointer] == ''
220
+ log.error(issue[:details]) unless issue[:details].nil?
221
+ end
222
+ exit 2
215
223
  end
@@ -1,7 +1,7 @@
1
1
  == Configuration
2
2
 
3
3
  Tfctl retrieves initial account configuration from AWS Organizations and merges
4
- it with organization config specified in the yaml file.
4
+ it with configuration specified in a yaml file.
5
5
 
6
6
  The configuration is merged in the following order:
7
7
 
@@ -15,13 +15,16 @@ Parameters further down the hierarchy take precedence. For example:
15
15
  [source, yaml]
16
16
  ----
17
17
  organization_root:
18
- example_param: 'will be overriden further down'
18
+ data:
19
+ example_param: 'will be overriden further down'
19
20
 
20
21
  organization_units:
21
22
  team:
22
- example_param: 'will win in team ou'
23
+ data:
24
+ example_param: 'will win in team ou'
23
25
  team/live:
24
- example_param: 'will win in team/live ou'
26
+ data:
27
+ example_param: 'will win in team/live ou'
25
28
  ----
26
29
 
27
30
  One exception to this rule is the `profiles` parameter. Profiles are additive:
@@ -45,6 +48,15 @@ TIP: You can display the fully merged configuration by running `tfctl -c
45
48
  conf/CONFIG_FILE.yaml -s`. It's safe to run as it doesn't make any changes to
46
49
  AWS resources. It's a good way to test your configuration.
47
50
 
51
+ === Defining arbitrary data
52
+
53
+ You can define arbitrary data under the `data:` parameter, both in the root of
54
+ the config and in the organization sections. It will be available in Terraform
55
+ profiles to use by your modules. You can use this to define things like VPC
56
+ subnet ranges, s3 bucket names and so on. `data:` in organization sections
57
+ will be merged with accounts following the usual merge order as described
58
+ above.
59
+
48
60
  === Handling secrets
49
61
 
50
62
  No secrets should be committed into Terraform or tfctl configuration. Use AWS
@@ -12,8 +12,6 @@
12
12
  # Terraform configuration
13
13
  #
14
14
 
15
- # State management
16
-
17
15
  tf_state_bucket: 'CHANGEME'
18
16
  tf_state_dynamodb_table: 'terraform-lock'
19
17
  tf_state_region: 'eu-west-1'
@@ -26,47 +24,57 @@ aws_provider_version: '>= 2.14'
26
24
  tfctl_role_arn: 'arn:aws:iam::PRIMARY_ACCOUNT_ID:role/TfctlRole'
27
25
 
28
26
  #
29
- # Organization configuration
27
+ # Data
28
+ #
29
+ # Here you can add arbitrary data which will be accessible from Terraform
30
+ # profiles. Data can also be defined per account in the organization sections
31
+ # below.
30
32
  #
33
+ # data:
34
+ # my_parameter: some_value
31
35
 
36
+ #
37
+ # Organization configuration
38
+ #
39
+ # Assign resources and data to accounts based on the organization structure.
40
+ #
32
41
  # IMPORTANT: Removing a Terraform profile here will remove all of it's
33
- # associated resources in AWS!
34
-
35
- # Name of the primary account.
36
- primary_account: CHANGEME
42
+ # associated resources during next apply!
37
43
 
38
- # Configuration to apply in all accounts
44
+ # Configuration to apply to all accounts
39
45
  organization_root:
40
-
41
46
  # Role assumed by Terraform for execution in each account
42
47
  tf_execution_role: 'AWSControlTowerExecution'
43
48
  region: 'eu-west-1'
44
- # Bucket name used by example profile it will be prefixed with the target
45
- # account number for uniqueness across accounts.
46
- example_bucket_name: 'tfctl-example-bucket'
49
+ data:
50
+ # Bucket name used by example profile it will be prefixed with the target
51
+ # account number for uniqueness across accounts.
52
+ example_bucket_name: 'tfctl-example-bucket'
47
53
  # Assign example-profile to all accounts in managed OUs
48
54
  profiles:
49
55
  - example-profile
50
56
 
51
57
  # Configuration to apply to accounts in Organization Units
52
- # Units not listed here will be ignored
58
+ # OU's not listed here will be ignored.
53
59
  organization_units:
54
- # Uncomment if you want to include Core OU accounts
55
- # Core: {}
60
+ # Core: {} # Uncomment if you want to include Core OU accounts
56
61
  live: {}
57
62
  test: {}
58
63
  mgmt:
59
- # Override the example bucket name in mgmt OU accounts
60
- example_bucket_name: 'tfctl-ou-override-example'
64
+ data:
65
+ # Override the example bucket name in mgmt OU accounts
66
+ example_bucket_name: 'tfctl-ou-override-example'
61
67
 
62
68
  # Configuration to apply to individual accounts
63
69
  account_overrides:
64
70
  test-example1:
65
- # Override the bucket name in a specific account
66
- example_bucket_name: 'tfctl-account-override-example'
71
+ data:
72
+ # Override the bucket name in a specific account
73
+ example_bucket_name: 'tfctl-account-override-example'
67
74
 
68
75
 
69
- # Exclude individual accounts from Terraform runs.
70
- #exclude_accounts:
71
- # - Audit
72
- # - 'Log archive'
76
+ # Exclude individual accounts from Terraform runs
77
+ #
78
+ # exclude_accounts:
79
+ # - Audit
80
+ # - 'Log archive'
data/lib/tfctl.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'tfctl/aws_org.rb'
3
4
  require_relative 'tfctl/config.rb'
4
- require_relative 'tfctl/generator.rb'
5
- require_relative 'tfctl/executor.rb'
6
5
  require_relative 'tfctl/error.rb'
6
+ require_relative 'tfctl/executor.rb'
7
+ require_relative 'tfctl/generator.rb'
7
8
  require_relative 'tfctl/logger.rb'
8
- require_relative 'tfctl/aws_org.rb'
9
+ require_relative 'tfctl/schema.rb'
9
10
  require_relative 'tfctl/version.rb'
data/lib/tfctl/error.rb CHANGED
@@ -3,4 +3,13 @@
3
3
  module Tfctl
4
4
  class Error < StandardError
5
5
  end
6
+
7
+ class ValidationError < StandardError
8
+ attr_reader :issues
9
+
10
+ def initialize(message, issues = [])
11
+ super(message)
12
+ @issues = issues
13
+ end
14
+ end
6
15
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json_schemer'
4
+ require_relative 'error.rb'
5
+
6
+ # Config validator using JSON schema
7
+
8
+ module Tfctl
9
+ module Schema
10
+ class << self
11
+
12
+ def validate(data)
13
+ schemer = JSONSchemer.schema(main_schema)
14
+ issues = []
15
+ schemer.validate(data).each do |issue|
16
+ issues << {
17
+ details: issue['details'],
18
+ data_pointer: issue['data_pointer'],
19
+ }
20
+ end
21
+
22
+ return if issues.empty?
23
+
24
+ raise Tfctl::ValidationError.new('Config validation failed', issues)
25
+ end
26
+
27
+ private
28
+
29
+ def main_schema
30
+ iam_arn_pattern = 'arn:aws:iam:[a-z\-0-9]*:[0-9]{12}:[a-zA-Z\/+@=.,]*'
31
+
32
+ # rubocop:disable Layout/AlignHash
33
+ {
34
+ 'type' => 'object',
35
+ 'properties' => {
36
+ 'tf_state_bucket' => { 'type' => 'string' },
37
+ 'tf_state_role_arn' => {
38
+ 'type' => 'string',
39
+ 'pattern' => iam_arn_pattern,
40
+ },
41
+ 'tf_state_dynamodb_table' => { 'type' => 'string' },
42
+ 'tf_state_region' => { 'type' => 'string' },
43
+ 'tf_required_version' => { 'type' => 'string' },
44
+ 'aws_provider_version' => { 'type' => 'string' },
45
+ 'tfctl_role_arn' => {
46
+ 'type' => 'string',
47
+ 'pattern' => iam_arn_pattern,
48
+ },
49
+ 'data' => { 'type' => 'object' },
50
+ 'exclude_accounts' => { 'type' => 'array' },
51
+ 'organization_root' => org_schema,
52
+ 'organization_units' => org_schema,
53
+ 'account_overrides' => org_schema,
54
+ },
55
+ 'required' => %w[
56
+ tf_state_bucket
57
+ tf_state_role_arn
58
+ tf_state_dynamodb_table
59
+ tf_state_region
60
+ tfctl_role_arn
61
+ ],
62
+ 'additionalProperties' => false,
63
+ }
64
+ # rubocop:enable Layout/AlignHash
65
+ end
66
+
67
+ def org_schema
68
+ {
69
+ 'type' => 'object',
70
+ 'properties' => {
71
+ 'profiles' => { 'type'=> 'array' },
72
+ 'data' => { 'type'=> 'object' },
73
+ 'tf_execution_role' => { 'type'=> 'string' },
74
+ 'region' => { 'type'=> 'string' },
75
+ },
76
+ }
77
+ end
78
+ end
79
+ end
80
+ end
data/lib/tfctl/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tfctl
4
- VERSION = '0.2.0'
4
+ VERSION = '1.0.0.rc1'
5
5
  end
data/tfctl.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  # Think when adding new dependencies. Is it really necessary?
27
27
  # "The things you own end up owning you" etc.
28
28
  spec.add_dependency 'aws-sdk-organizations', '~> 1.13'
29
+ spec.add_dependency 'json_schemer', '~> 0.2'
29
30
  spec.add_dependency 'parallel', '~> 1.17'
30
31
  spec.add_dependency 'terminal-table', '~> 1.8'
31
32
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tfctl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Wasilczuk
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json_schemer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: parallel
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -135,6 +149,7 @@ files:
135
149
  - lib/tfctl/executor.rb
136
150
  - lib/tfctl/generator.rb
137
151
  - lib/tfctl/logger.rb
152
+ - lib/tfctl/schema.rb
138
153
  - lib/tfctl/version.rb
139
154
  - tfctl.gemspec
140
155
  homepage: https://github.com/scalefactory/tfctl
@@ -152,9 +167,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
167
  version: '0'
153
168
  required_rubygems_version: !ruby/object:Gem::Requirement
154
169
  requirements:
155
- - - ">="
170
+ - - ">"
156
171
  - !ruby/object:Gem::Version
157
- version: '0'
172
+ version: 1.3.1
158
173
  requirements: []
159
174
  rubyforge_project:
160
175
  rubygems_version: 2.7.7