stack_master 2.3.0 → 2.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: 6b2fd9ecb9882d2a0b00adee84d08975f5ae51b72297a449c5bd5218efbf8a17
4
- data.tar.gz: c2eb23ba0ae38e6db17cc296a4a151aaaf7785bbff222d75ec671251ac398f1f
3
+ metadata.gz: 676259d50dc1b2634f669f98625e3203f57d39eb77d6e6bf55f0e3b13318174b
4
+ data.tar.gz: f759222c480eed85f359c9b6f713f865664f156078a065bd4ad16fcd2db47417
5
5
  SHA512:
6
- metadata.gz: e5832578334d932b9750b9aa2914c6c0fc9d2b391606c3ab8fc60868b151c5b9fa996d2b89636d14515aff3b9892c8a913c1e0531ac57262a67072931a2a8f1c
7
- data.tar.gz: 87dd2728b566b6d775a0b8cb7137acc856f1ee854436e35789578c1c129798594375bc9ad06018be2285605878918cf89d872c3df72fe20a3260380407e32b25
6
+ metadata.gz: 482ccf3ecd0511921404744dd8f081f6f1c17c954918bc8fef5e5d6b2bf4ddebafd74d942c22b858062abb9e38fa238031d0fb310c8a5dbd02482833b6119641
7
+ data.tar.gz: f9fdaf391a317ccaa1cad9e527b8bd4b529c7446b6bc13335a31fc8fde7504878f6f9183959742710e9a5b637ad3a783b302f2955f7ed6c2b67ba2876ef6fb62
data/README.md CHANGED
@@ -625,7 +625,7 @@ stacks:
625
625
 
626
626
  ## Allowed accounts
627
627
 
628
- The AWS account the command is executing in can be restricted to a specific list of allowed accounts. This is useful in reducing the possibility of applying non-production changes in a production account. Each stack definition can specify the `allowed_accounts` property with an array of AWS account IDs the stack is allowed to work with.
628
+ The AWS account the command is executing in can be restricted to a specific list of allowed accounts. This is useful in reducing the possibility of applying non-production changes in a production account. Each stack definition can specify the `allowed_accounts` property with an array of AWS account IDs or aliases the stack is allowed to work with.
629
629
 
630
630
  This is an opt-in feature which is enabled by specifying at least one account to allow.
631
631
 
@@ -644,7 +644,7 @@ stacks:
644
644
  template: myapp_db.rb
645
645
  allowed_accounts: # only allow these accounts (overrides the stack defaults)
646
646
  - '1234567890'
647
- - '9876543210'
647
+ - my-account-alias
648
648
  tags:
649
649
  purpose: back-end
650
650
  myapp-web:
@@ -659,7 +659,7 @@ stacks:
659
659
  purpose: back-end
660
660
  ```
661
661
 
662
- In the cases where you want to bypass the account check, there is StackMaster flag `--skip-account-check` that can be used.
662
+ In the cases where you want to bypass the account check, there is the StackMaster flag `--skip-account-check` that can be used.
663
663
 
664
664
  ## Commands
665
665
 
@@ -7,6 +7,7 @@ require 'aws-sdk-ecr'
7
7
  require 'aws-sdk-s3'
8
8
  require 'aws-sdk-sns'
9
9
  require 'aws-sdk-ssm'
10
+ require 'aws-sdk-iam'
10
11
  require 'colorize'
11
12
  require 'active_support/core_ext/hash/keys'
12
13
  require 'active_support/core_ext/object/blank'
@@ -45,6 +46,7 @@ module StackMaster
45
46
 
46
47
  autoload :StackDiffer, 'stack_master/stack_differ'
47
48
  autoload :Validator, 'stack_master/validator'
49
+ autoload :ParameterValidator, 'stack_master/parameter_validator'
48
50
 
49
51
  require 'stack_master/template_compilers/sparkle_formation'
50
52
  require 'stack_master/template_compilers/json'
@@ -262,13 +262,15 @@ module StackMaster
262
262
  if running_in_allowed_account?(allowed_accounts)
263
263
  block.call
264
264
  else
265
- StackMaster.stdout.puts "Account '#{identity.account}' is not an allowed account. Allowed accounts are #{allowed_accounts}."
265
+ account_text = "'#{identity.account}'"
266
+ account_text << " (#{identity.account_aliases.join(', ')})" if identity.account_aliases.any?
267
+ StackMaster.stdout.puts "Account #{account_text} is not an allowed account. Allowed accounts are #{allowed_accounts}."
266
268
  false
267
269
  end
268
270
  end
269
271
 
270
272
  def running_in_allowed_account?(allowed_accounts)
271
- StackMaster.skip_account_check? || identity.running_in_allowed_account?(allowed_accounts)
273
+ StackMaster.skip_account_check? || identity.running_in_account?(allowed_accounts)
272
274
  end
273
275
 
274
276
  def identity
@@ -1,5 +1,3 @@
1
- require 'pathname'
2
-
3
1
  module StackMaster
4
2
  module Commands
5
3
  class Apply
@@ -205,18 +203,8 @@ module StackMaster
205
203
  end
206
204
 
207
205
  def ensure_valid_parameters!
208
- if @proposed_stack.missing_parameters?
209
- message = <<~MESSAGE
210
- Empty/blank parameters detected, ensure values exist for those parameters.
211
- Parameters will be read from the following locations:
212
- MESSAGE
213
- base_dir = Pathname.new(@stack_definition.base_dir)
214
- @stack_definition.parameter_file_globs.each do |glob|
215
- parameter_file = Pathname.new(glob).relative_path_from(base_dir)
216
- message << " - #{parameter_file}\n"
217
- end
218
- failed!(message)
219
- end
206
+ pv = ParameterValidator.new(stack: @proposed_stack, stack_definition: @stack_definition)
207
+ failed!(pv.error_message) if pv.missing_parameters?
220
208
  end
221
209
 
222
210
  def ensure_valid_template_body_size!
@@ -44,7 +44,7 @@ module StackMaster
44
44
  end
45
45
 
46
46
  def running_in_allowed_account?(allowed_accounts)
47
- StackMaster.skip_account_check? || identity.running_in_allowed_account?(allowed_accounts)
47
+ StackMaster.skip_account_check? || identity.running_in_account?(allowed_accounts)
48
48
  end
49
49
 
50
50
  def identity
@@ -1,16 +1,25 @@
1
1
  module StackMaster
2
2
  class Identity
3
- def running_in_allowed_account?(allowed_accounts)
4
- allowed_accounts.nil? || allowed_accounts.empty? || allowed_accounts.include?(account)
3
+ MissingIamPermissionsError = Class.new(StandardError)
4
+
5
+ def running_in_account?(accounts)
6
+ accounts.nil? ||
7
+ accounts.empty? ||
8
+ contains_account_id?(accounts) ||
9
+ contains_account_alias?(accounts)
5
10
  end
6
11
 
7
12
  def account
8
13
  @account ||= sts.get_caller_identity.account
9
14
  end
10
15
 
11
- private
16
+ def account_aliases
17
+ @aliases ||= iam.list_account_aliases.account_aliases
18
+ rescue Aws::IAM::Errors::AccessDenied
19
+ raise MissingIamPermissionsError, 'Failed to retrieve account aliases. Missing required IAM permission: iam:ListAccountAliases'
20
+ end
12
21
 
13
- attr_reader :sts
22
+ private
14
23
 
15
24
  def region
16
25
  @region ||= ENV['AWS_REGION'] || Aws.config[:region] || Aws.shared_config.region || 'us-east-1'
@@ -19,5 +28,17 @@ module StackMaster
19
28
  def sts
20
29
  @sts ||= Aws::STS::Client.new(region: region)
21
30
  end
31
+
32
+ def iam
33
+ @iam ||= Aws::IAM::Client.new(region: region)
34
+ end
35
+
36
+ def contains_account_id?(ids)
37
+ ids.include?(account)
38
+ end
39
+
40
+ def contains_account_alias?(aliases)
41
+ account_aliases.any? { |account_alias| aliases.include?(account_alias) }
42
+ end
22
43
  end
23
44
  end
@@ -0,0 +1,36 @@
1
+ require 'pathname'
2
+
3
+ module StackMaster
4
+ class ParameterValidator
5
+ def initialize(stack:, stack_definition:)
6
+ @stack = stack
7
+ @stack_definition = stack_definition
8
+ end
9
+
10
+ def error_message
11
+ return nil unless missing_parameters?
12
+ message = "Empty/blank parameters detected. Please provide values for these parameters:"
13
+ missing_parameters.each do |parameter_name|
14
+ message << "\n - #{parameter_name}"
15
+ end
16
+ message << "\nParameters will be read from files matching the following globs:"
17
+ base_dir = Pathname.new(@stack_definition.base_dir)
18
+ @stack_definition.parameter_file_globs.each do |glob|
19
+ parameter_file = Pathname.new(glob).relative_path_from(base_dir)
20
+ message << "\n - #{parameter_file}"
21
+ end
22
+ message
23
+ end
24
+
25
+ def missing_parameters?
26
+ missing_parameters.any?
27
+ end
28
+
29
+ private
30
+
31
+ def missing_parameters
32
+ @missing_parameters ||=
33
+ @stack.parameters_with_defaults.select { |_key, value| value.nil? }.keys
34
+ end
35
+ end
36
+ end
@@ -45,6 +45,7 @@ module StackMaster
45
45
  credentials_key = "#{account}:#{role}"
46
46
  @credentials.fetch(credentials_key) do
47
47
  @credentials[credentials_key] = Aws::AssumeRoleCredentials.new(
48
+ region: StackMaster.cloud_formation_driver.region,
48
49
  role_arn: "arn:aws:iam::#{account}:role/#{role}",
49
50
  role_session_name: "stack-master-role-assumer"
50
51
  )
@@ -27,12 +27,6 @@ module StackMaster
27
27
  template_default_parameters.merge(parameters)
28
28
  end
29
29
 
30
- def missing_parameters?
31
- parameters_with_defaults.any? do |key, value|
32
- value == nil
33
- end
34
- end
35
-
36
30
  def self.find(region, stack_name)
37
31
  cf = StackMaster.cloud_formation_driver
38
32
  cf_stack = cf.describe_stacks(stack_name: stack_name).stacks.first
@@ -10,12 +10,12 @@ module StackMaster
10
10
  end
11
11
 
12
12
  def perform
13
- parameter_hash = ParameterLoader.load(@stack_definition.parameter_files)
14
- compile_time_parameters = ParameterResolver.resolve(@config, @stack_definition, parameter_hash[:compile_time_parameters])
15
-
16
13
  StackMaster.stdout.print "#{@stack_definition.stack_name}: "
17
- template_body = TemplateCompiler.compile(@config, @stack_definition.compiler, @stack_definition.template_dir, @stack_definition.template, compile_time_parameters, @stack_definition.compiler_options)
18
- cf.validate_template(template_body: TemplateUtils.maybe_compressed_template_body(template_body))
14
+ if parameter_validator.missing_parameters?
15
+ StackMaster.stdout.puts "invalid\n#{parameter_validator.error_message}"
16
+ return false
17
+ end
18
+ cf.validate_template(template_body: TemplateUtils.maybe_compressed_template_body(stack.template_body))
19
19
  StackMaster.stdout.puts "valid"
20
20
  true
21
21
  rescue Aws::CloudFormation::Errors::ValidationError => e
@@ -28,5 +28,13 @@ module StackMaster
28
28
  def cf
29
29
  @cf ||= StackMaster.cloud_formation_driver
30
30
  end
31
+
32
+ def stack
33
+ @stack ||= Stack.generate(@stack_definition, @config)
34
+ end
35
+
36
+ def parameter_validator
37
+ @parameter_validator ||= ParameterValidator.new(stack: stack, stack_definition: @stack_definition)
38
+ end
31
39
  end
32
40
  end
@@ -1,3 +1,3 @@
1
1
  module StackMaster
2
- VERSION = "2.3.0"
2
+ VERSION = "2.4.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stack_master
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Hodgkiss
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-03-18 00:00:00.000000000 Z
12
+ date: 2020-04-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -255,6 +255,20 @@ dependencies:
255
255
  - - "~>"
256
256
  - !ruby/object:Gem::Version
257
257
  version: '1'
258
+ - !ruby/object:Gem::Dependency
259
+ name: aws-sdk-iam
260
+ requirement: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
263
+ - !ruby/object:Gem::Version
264
+ version: '1'
265
+ type: :runtime
266
+ prerelease: false
267
+ version_requirements: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - "~>"
270
+ - !ruby/object:Gem::Version
271
+ version: '1'
258
272
  - !ruby/object:Gem::Dependency
259
273
  name: diffy
260
274
  requirement: !ruby/object:Gem::Requirement
@@ -472,6 +486,7 @@ files:
472
486
  - lib/stack_master/parameter_resolvers/security_group.rb
473
487
  - lib/stack_master/parameter_resolvers/sns_topic_name.rb
474
488
  - lib/stack_master/parameter_resolvers/stack_output.rb
489
+ - lib/stack_master/parameter_validator.rb
475
490
  - lib/stack_master/prompter.rb
476
491
  - lib/stack_master/resolver_array.rb
477
492
  - lib/stack_master/role_assumer.rb
@@ -523,8 +538,8 @@ licenses:
523
538
  metadata:
524
539
  bug_tracker_uri: https://github.com/envato/stack_master/issues
525
540
  changelog_uri: https://github.com/envato/stack_master/blob/master/CHANGELOG.md
526
- documentation_uri: https://www.rubydoc.info/gems/stack_master/2.3.0
527
- source_code_uri: https://github.com/envato/stack_master/tree/v2.3.0
541
+ documentation_uri: https://www.rubydoc.info/gems/stack_master/2.4.0
542
+ source_code_uri: https://github.com/envato/stack_master/tree/v2.4.0
528
543
  post_install_message:
529
544
  rdoc_options: []
530
545
  require_paths: