stack_master 2.3.0 → 2.4.0

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