tfctl 0.2.0 → 1.0.0.rc1

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: 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