stack_master 1.13.1 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +52 -2
- data/lib/stack_master/cli.rb +33 -6
- data/lib/stack_master/commands/lint.rb +5 -1
- data/lib/stack_master/commands/status.rb +11 -2
- data/lib/stack_master/config.rb +1 -0
- data/lib/stack_master/identity.rb +23 -0
- data/lib/stack_master/stack_definition.rb +4 -0
- data/lib/stack_master/version.rb +1 -1
- data/lib/stack_master.rb +12 -0
- metadata +5 -6
- data/lib/stack_master/aws_driver/sts.rb +0 -22
- data/lib/stack_master/target_definition.rb +0 -13
- data/lib/stack_master/test_driver/sts.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6272dd327e82ec747c26129191c4c8e262a4350d6634941f14035746399cb77
|
4
|
+
data.tar.gz: 6dc8058810bee48a3aed457669ffdda94e9d88fdc2ab1e1c12b9a689a3799c46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6dad4bcffbc40f9a45377625ef7769f122b5880bbcc61bead8a9ffdd75ee063001f322f9e62079aa00b370f791afd2f76c7acb9b668f6d4f7f41128c63954b65
|
7
|
+
data.tar.gz: a12c14c9024e187a5b734020cda2adc1c40d7983f58450022ea5f2301412968a167f4ebd6233f02097b8341541dc5ae50b213b6b59730f540594df90b327fd5f
|
data/README.md
CHANGED
@@ -31,10 +31,18 @@ etc.
|
|
31
31
|
|
32
32
|
## Installation
|
33
33
|
|
34
|
-
System-wide
|
34
|
+
### System-wide
|
35
35
|
|
36
|
-
|
36
|
+
```shell
|
37
|
+
gem install stack_master
|
37
38
|
|
39
|
+
# if you want linting capabilities:
|
40
|
+
pip install cfn-lint
|
41
|
+
```
|
42
|
+
|
43
|
+
### Bundler
|
44
|
+
|
45
|
+
- `pip install cfn-lint` if you need lint functionality
|
38
46
|
- Add `gem 'stack_master'` to your Gemfile.
|
39
47
|
- Run `bundle install`
|
40
48
|
- Run `bundle exec stack_master init` to generate a directory structure and stack_master.yml file
|
@@ -83,10 +91,14 @@ stacks:
|
|
83
91
|
staging:
|
84
92
|
myapp-vpc:
|
85
93
|
template: myapp_vpc.rb
|
94
|
+
allowed_accounts: '123456789'
|
86
95
|
tags:
|
87
96
|
purpose: front-end
|
88
97
|
myapp-db:
|
89
98
|
template: myapp_db.rb
|
99
|
+
allowed_accounts:
|
100
|
+
- '1234567890'
|
101
|
+
- '9876543210'
|
90
102
|
tags:
|
91
103
|
purpose: back-end
|
92
104
|
myapp-web:
|
@@ -537,6 +549,44 @@ end
|
|
537
549
|
|
538
550
|
Note though that if a dynamic with the same name exists in your `templates/dynamics/` directory it will get loaded since it has higher precedence.
|
539
551
|
|
552
|
+
## Allowed accounts
|
553
|
+
|
554
|
+
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.
|
555
|
+
|
556
|
+
This is an opt-in feature which is enabled by specifying at least one account to allow.
|
557
|
+
|
558
|
+
Unlike other stack defaults, the `allowed_accounts` property values specified in the stack definition override values specified in the stack defaults (i.e., other stack property values are merged together with those specified in the stack defaults). This allows specifying allowed accounts in the stack defaults (inherited by all stacks) and override them for specific stacks. See below example config for an example.
|
559
|
+
|
560
|
+
```yaml
|
561
|
+
stack_defaults:
|
562
|
+
allowed_accounts: '555555555'
|
563
|
+
stacks:
|
564
|
+
us-east-1:
|
565
|
+
myapp-vpc: # only allow account 555555555 (inherited from the stack defaults)
|
566
|
+
template: myapp_vpc.rb
|
567
|
+
tags:
|
568
|
+
purpose: front-end
|
569
|
+
myapp-db:
|
570
|
+
template: myapp_db.rb
|
571
|
+
allowed_accounts: # only allow these accounts (overrides the stack defaults)
|
572
|
+
- '1234567890'
|
573
|
+
- '9876543210'
|
574
|
+
tags:
|
575
|
+
purpose: back-end
|
576
|
+
myapp-web:
|
577
|
+
template: myapp_web.rb
|
578
|
+
allowed_accounts: [] # allow all accounts (overrides the stack defaults)
|
579
|
+
tags:
|
580
|
+
purpose: front-end
|
581
|
+
myapp-redis:
|
582
|
+
template: myapp_redis.rb
|
583
|
+
allowed_accounts: '888888888' # only allow this account (overrides the stack defaults)
|
584
|
+
tags:
|
585
|
+
purpose: back-end
|
586
|
+
```
|
587
|
+
|
588
|
+
In the cases where you want to bypass the account check, there is StackMaster flag `--skip-account-check` that can be used.
|
589
|
+
|
540
590
|
## Commands
|
541
591
|
|
542
592
|
```bash
|
data/lib/stack_master/cli.rb
CHANGED
@@ -34,6 +34,9 @@ module StackMaster
|
|
34
34
|
global_option '-q', '--quiet', 'Do not output the resulting Stack Events, just return immediately' do
|
35
35
|
StackMaster.quiet!
|
36
36
|
end
|
37
|
+
global_option '--skip-account-check', 'Do not check if command is allowed to execute in account' do
|
38
|
+
StackMaster.skip_account_check!
|
39
|
+
end
|
37
40
|
|
38
41
|
command :apply do |c|
|
39
42
|
c.syntax = 'stack_master apply [region_or_alias] [stack_name]'
|
@@ -178,18 +181,22 @@ module StackMaster
|
|
178
181
|
return
|
179
182
|
end
|
180
183
|
|
184
|
+
stack_name = Utils.underscore_to_hyphen(args[1])
|
185
|
+
allowed_accounts = []
|
186
|
+
|
181
187
|
# Because delete can work without a stack_master.yml
|
182
188
|
if options.config and File.file?(options.config)
|
183
189
|
config = load_config(options.config)
|
184
190
|
region = Utils.underscore_to_hyphen(config.unalias_region(args[0]))
|
191
|
+
allowed_accounts = config.find_stack(region, stack_name)&.allowed_accounts
|
185
192
|
else
|
186
193
|
region = args[0]
|
187
194
|
end
|
188
195
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
196
|
+
execute_if_allowed_account(allowed_accounts) do
|
197
|
+
StackMaster.cloud_formation_driver.set_region(region)
|
198
|
+
StackMaster::Commands::Delete.perform(region, stack_name)
|
199
|
+
end
|
193
200
|
end
|
194
201
|
end
|
195
202
|
|
@@ -223,15 +230,35 @@ module StackMaster
|
|
223
230
|
success = false
|
224
231
|
end
|
225
232
|
stack_definitions = stack_definitions.select do |stack_definition|
|
226
|
-
StackStatus.new(config, stack_definition).changed?
|
233
|
+
running_in_allowed_account?(stack_definition.allowed_accounts) && StackStatus.new(config, stack_definition).changed?
|
227
234
|
end if options.changed
|
228
235
|
stack_definitions.each do |stack_definition|
|
229
236
|
StackMaster.cloud_formation_driver.set_region(stack_definition.region)
|
230
237
|
StackMaster.stdout.puts "Executing #{command.command_name} on #{stack_definition.stack_name} in #{stack_definition.region}"
|
231
|
-
success =
|
238
|
+
success = execute_if_allowed_account(stack_definition.allowed_accounts) do
|
239
|
+
command.perform(config, stack_definition, options).success?
|
240
|
+
end
|
232
241
|
end
|
233
242
|
end
|
234
243
|
success
|
235
244
|
end
|
245
|
+
|
246
|
+
def execute_if_allowed_account(allowed_accounts, &block)
|
247
|
+
raise ArgumentError, "Block required to execute this method" unless block_given?
|
248
|
+
if running_in_allowed_account?(allowed_accounts)
|
249
|
+
block.call
|
250
|
+
else
|
251
|
+
StackMaster.stdout.puts "Account '#{identity.account}' is not an allowed account. Allowed accounts are #{allowed_accounts}."
|
252
|
+
false
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def running_in_allowed_account?(allowed_accounts)
|
257
|
+
StackMaster.skip_account_check? || identity.running_in_allowed_account?(allowed_accounts)
|
258
|
+
end
|
259
|
+
|
260
|
+
def identity
|
261
|
+
@identity ||= StackMaster::Identity.new
|
262
|
+
end
|
236
263
|
end
|
237
264
|
end
|
@@ -13,7 +13,11 @@ module StackMaster
|
|
13
13
|
|
14
14
|
def perform
|
15
15
|
unless cfn_lint_available
|
16
|
-
failed!
|
16
|
+
failed! 'Failed to run cfn-lint. You may need to install it using'\
|
17
|
+
'`pip install cfn-lint`, or add it to $PATH.'\
|
18
|
+
"\n"\
|
19
|
+
'(See https://github.com/aws-cloudformation/cfn-python-lint'\
|
20
|
+
' for package information)'
|
17
21
|
end
|
18
22
|
|
19
23
|
Tempfile.open(['stack', ".#{proposed_stack.template_format}"]) do |f|
|
@@ -16,12 +16,13 @@ module StackMaster
|
|
16
16
|
progress if @show_progress
|
17
17
|
status = @config.stacks.map do |stack_definition|
|
18
18
|
stack_status = StackStatus.new(@config, stack_definition)
|
19
|
+
allowed_accounts = stack_definition.allowed_accounts
|
19
20
|
progress.increment if @show_progress
|
20
21
|
{
|
21
22
|
region: stack_definition.region,
|
22
23
|
stack_name: stack_definition.stack_name,
|
23
|
-
stack_status: stack_status.status,
|
24
|
-
different: stack_status.changed_message,
|
24
|
+
stack_status: running_in_allowed_account?(allowed_accounts) ? stack_status.status : "Disallowed account",
|
25
|
+
different: running_in_allowed_account?(allowed_accounts) ? stack_status.changed_message : "N/A",
|
25
26
|
}
|
26
27
|
end
|
27
28
|
tp.set :max_width, self.window_size
|
@@ -41,6 +42,14 @@ module StackMaster
|
|
41
42
|
def sort_params(hash)
|
42
43
|
hash.sort.to_h
|
43
44
|
end
|
45
|
+
|
46
|
+
def running_in_allowed_account?(allowed_accounts)
|
47
|
+
StackMaster.skip_account_check? || identity.running_in_allowed_account?(allowed_accounts)
|
48
|
+
end
|
49
|
+
|
50
|
+
def identity
|
51
|
+
@identity ||= StackMaster::Identity.new
|
52
|
+
end
|
44
53
|
end
|
45
54
|
end
|
46
55
|
end
|
data/lib/stack_master/config.rb
CHANGED
@@ -116,6 +116,7 @@ module StackMaster
|
|
116
116
|
'base_dir' => @base_dir,
|
117
117
|
'template_dir' => @template_dir,
|
118
118
|
'additional_parameter_lookup_dirs' => @region_to_aliases[region])
|
119
|
+
stack_attributes['allowed_accounts'] = attributes['allowed_accounts'] if attributes['allowed_accounts']
|
119
120
|
@stacks << StackDefinition.new(stack_attributes)
|
120
121
|
end
|
121
122
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class Identity
|
3
|
+
def running_in_allowed_account?(allowed_accounts)
|
4
|
+
allowed_accounts.nil? || allowed_accounts.empty? || allowed_accounts.include?(account)
|
5
|
+
end
|
6
|
+
|
7
|
+
def account
|
8
|
+
@account ||= sts.get_caller_identity.account
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :sts
|
14
|
+
|
15
|
+
def region
|
16
|
+
@region ||= ENV['AWS_REGION'] || Aws.config[:region] || Aws.shared_config.region || 'us-east-1'
|
17
|
+
end
|
18
|
+
|
19
|
+
def sts
|
20
|
+
@sts ||= Aws::STS::Client.new(region: region)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -5,6 +5,7 @@ module StackMaster
|
|
5
5
|
:template,
|
6
6
|
:tags,
|
7
7
|
:role_arn,
|
8
|
+
:allowed_accounts,
|
8
9
|
:notification_arns,
|
9
10
|
:base_dir,
|
10
11
|
:template_dir,
|
@@ -23,8 +24,10 @@ module StackMaster
|
|
23
24
|
@notification_arns = []
|
24
25
|
@s3 = {}
|
25
26
|
@files = []
|
27
|
+
@allowed_accounts = nil
|
26
28
|
super
|
27
29
|
@template_dir ||= File.join(@base_dir, 'templates')
|
30
|
+
@allowed_accounts = Array(@allowed_accounts)
|
28
31
|
end
|
29
32
|
|
30
33
|
def ==(other)
|
@@ -34,6 +37,7 @@ module StackMaster
|
|
34
37
|
@template == other.template &&
|
35
38
|
@tags == other.tags &&
|
36
39
|
@role_arn == other.role_arn &&
|
40
|
+
@allowed_accounts == other.allowed_accounts &&
|
37
41
|
@notification_arns == other.notification_arns &&
|
38
42
|
@base_dir == other.base_dir &&
|
39
43
|
@secret_file == other.secret_file &&
|
data/lib/stack_master/version.rb
CHANGED
data/lib/stack_master.rb
CHANGED
@@ -38,6 +38,7 @@ module StackMaster
|
|
38
38
|
autoload :PagedResponseAccumulator, 'stack_master/paged_response_accumulator'
|
39
39
|
autoload :StackDefinition, 'stack_master/stack_definition'
|
40
40
|
autoload :TemplateCompiler, 'stack_master/template_compiler'
|
41
|
+
autoload :Identity, 'stack_master/identity'
|
41
42
|
|
42
43
|
autoload :StackDiffer, 'stack_master/stack_differ'
|
43
44
|
autoload :Validator, 'stack_master/validator'
|
@@ -97,6 +98,7 @@ module StackMaster
|
|
97
98
|
NON_INTERACTIVE_DEFAULT = false
|
98
99
|
DEBUG_DEFAULT = false
|
99
100
|
QUIET_DEFAULT = false
|
101
|
+
SKIP_ACCOUNT_CHECK_DEFAULT = false
|
100
102
|
|
101
103
|
def interactive?
|
102
104
|
!non_interactive?
|
@@ -136,6 +138,16 @@ module StackMaster
|
|
136
138
|
|
137
139
|
def reset_flags
|
138
140
|
@quiet = QUIET_DEFAULT
|
141
|
+
@skip_account_check = SKIP_ACCOUNT_CHECK_DEFAULT
|
142
|
+
end
|
143
|
+
|
144
|
+
def skip_account_check!
|
145
|
+
@skip_account_check = true
|
146
|
+
end
|
147
|
+
@skip_account_check = SKIP_ACCOUNT_CHECK_DEFAULT
|
148
|
+
|
149
|
+
def skip_account_check?
|
150
|
+
@skip_account_check
|
139
151
|
end
|
140
152
|
|
141
153
|
attr_accessor :non_interactive_answer
|
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: 1.
|
4
|
+
version: 1.14.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: 2019-03
|
12
|
+
date: 2019-07-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -417,7 +417,6 @@ files:
|
|
417
417
|
- lib/stack_master.rb
|
418
418
|
- lib/stack_master/aws_driver/cloud_formation.rb
|
419
419
|
- lib/stack_master/aws_driver/s3.rb
|
420
|
-
- lib/stack_master/aws_driver/sts.rb
|
421
420
|
- lib/stack_master/change_set.rb
|
422
421
|
- lib/stack_master/cli.rb
|
423
422
|
- lib/stack_master/command.rb
|
@@ -436,6 +435,7 @@ files:
|
|
436
435
|
- lib/stack_master/commands/validate.rb
|
437
436
|
- lib/stack_master/config.rb
|
438
437
|
- lib/stack_master/ctrl_c.rb
|
438
|
+
- lib/stack_master/identity.rb
|
439
439
|
- lib/stack_master/paged_response_accumulator.rb
|
440
440
|
- lib/stack_master/parameter_loader.rb
|
441
441
|
- lib/stack_master/parameter_resolver.rb
|
@@ -479,7 +479,6 @@ files:
|
|
479
479
|
- lib/stack_master/stack_events/streamer.rb
|
480
480
|
- lib/stack_master/stack_states.rb
|
481
481
|
- lib/stack_master/stack_status.rb
|
482
|
-
- lib/stack_master/target_definition.rb
|
483
482
|
- lib/stack_master/template_compiler.rb
|
484
483
|
- lib/stack_master/template_compilers/cfndsl.rb
|
485
484
|
- lib/stack_master/template_compilers/json.rb
|
@@ -488,7 +487,6 @@ files:
|
|
488
487
|
- lib/stack_master/template_utils.rb
|
489
488
|
- lib/stack_master/test_driver/cloud_formation.rb
|
490
489
|
- lib/stack_master/test_driver/s3.rb
|
491
|
-
- lib/stack_master/test_driver/sts.rb
|
492
490
|
- lib/stack_master/testing.rb
|
493
491
|
- lib/stack_master/utils.rb
|
494
492
|
- lib/stack_master/validator.rb
|
@@ -516,7 +514,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
516
514
|
- !ruby/object:Gem::Version
|
517
515
|
version: '0'
|
518
516
|
requirements: []
|
519
|
-
|
517
|
+
rubyforge_project:
|
518
|
+
rubygems_version: 2.7.6
|
520
519
|
signing_key:
|
521
520
|
specification_version: 4
|
522
521
|
summary: StackMaster is a sure-footed way of creating, updating and keeping track
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module StackMaster
|
2
|
-
module AwsDriver
|
3
|
-
class STS
|
4
|
-
def set_region(region)
|
5
|
-
@region = region
|
6
|
-
end
|
7
|
-
|
8
|
-
def get_account_id
|
9
|
-
sts = new_sts_client
|
10
|
-
# This should always work as you cannot allow/deny access to this API
|
11
|
-
identity = sts.get_caller_identity
|
12
|
-
return identity['account']
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def new_sts_client
|
18
|
-
Aws::STS::Client.new(region: @region)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module StackMaster
|
2
|
-
class TargetDefinition
|
3
|
-
attr_reader :region,
|
4
|
-
:account_id,
|
5
|
-
:stack_definition
|
6
|
-
|
7
|
-
def initialize(region:, account_id:, stack_definition:)
|
8
|
-
@region = region
|
9
|
-
@account_id = account_id
|
10
|
-
@stack_definition = stack_definition
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module StackMaster
|
2
|
-
module TestDriver
|
3
|
-
class STS
|
4
|
-
attr_accessor :account_id
|
5
|
-
def initialize
|
6
|
-
reset
|
7
|
-
end
|
8
|
-
|
9
|
-
def reset
|
10
|
-
@account_id = "12345678910"
|
11
|
-
end
|
12
|
-
|
13
|
-
def set_region(region)
|
14
|
-
@region = region
|
15
|
-
end
|
16
|
-
|
17
|
-
def get_account_id
|
18
|
-
@account_id
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|