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 +4 -4
- data/README.md +3 -3
- data/lib/stack_master.rb +2 -0
- data/lib/stack_master/cli.rb +4 -2
- data/lib/stack_master/commands/apply.rb +2 -14
- data/lib/stack_master/commands/status.rb +1 -1
- data/lib/stack_master/identity.rb +25 -4
- data/lib/stack_master/parameter_validator.rb +36 -0
- data/lib/stack_master/role_assumer.rb +1 -0
- data/lib/stack_master/stack.rb +0 -6
- data/lib/stack_master/validator.rb +13 -5
- data/lib/stack_master/version.rb +1 -1
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 676259d50dc1b2634f669f98625e3203f57d39eb77d6e6bf55f0e3b13318174b
|
4
|
+
data.tar.gz: f759222c480eed85f359c9b6f713f865664f156078a065bd4ad16fcd2db47417
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
-
|
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
|
|
data/lib/stack_master.rb
CHANGED
@@ -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'
|
data/lib/stack_master/cli.rb
CHANGED
@@ -262,13 +262,15 @@ module StackMaster
|
|
262
262
|
if running_in_allowed_account?(allowed_accounts)
|
263
263
|
block.call
|
264
264
|
else
|
265
|
-
|
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.
|
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
|
-
|
209
|
-
|
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.
|
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
|
-
|
4
|
-
|
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
|
-
|
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
|
-
|
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
|
)
|
data/lib/stack_master/stack.rb
CHANGED
@@ -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
|
-
|
18
|
-
|
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
|
data/lib/stack_master/version.rb
CHANGED
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.
|
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
|
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.
|
527
|
-
source_code_uri: https://github.com/envato/stack_master/tree/v2.
|
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:
|