stack_master 1.13.1-x64-mingw32 → 1.14.0-x64-mingw32

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
  SHA1:
3
- metadata.gz: 0755258ff7cba30456cd24938695d9a77c3adecd
4
- data.tar.gz: e4b85c2320e76fdcbf1fb9a4fd1776abf37b6850
3
+ metadata.gz: a0c3168b5e279ae1b4944be5eb01d4fa799e5794
4
+ data.tar.gz: d4da17e1b54e40434c60060204a4d9e96f720833
5
5
  SHA512:
6
- metadata.gz: 0f3016ebb8e05399a46ef049fc205e7a7e279454f065a9a4c69bb9242b0ab8365c23daea09b23d3f874026b4a27ec6a314d62940417ab9313d4fb57119747f83
7
- data.tar.gz: 39a3377087ada41dff9b5e1072d3bdb7905b8e64e5231e82b214a37330e1f34208115ccf70385a5b84c7a72e11df053f97208427e19afe08c352ea8478b76992
6
+ metadata.gz: d2b34d6a2f518904738c2cbfb18c1f57bbbe89637fb322f7e4706ff3683f8986f700ff440196168664689d5fd4082e200109df6d89e13e3a9928565fe3c5082c
7
+ data.tar.gz: 5797d1e8cc5e595ff7f91b86ba97296bdcd68862c30e0e64ab3ca5ac11bd9d4a68417b814831703aa292c48d7cb7ec3f40d1199ed5553b2bb0d3618edbb27f09
data/README.md CHANGED
@@ -31,10 +31,18 @@ etc.
31
31
 
32
32
  ## Installation
33
33
 
34
- System-wide: `gem install stack_master`
34
+ ### System-wide
35
35
 
36
- With bundler:
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.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
@@ -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
- stack_name = Utils.underscore_to_hyphen(args[1])
190
-
191
- StackMaster.cloud_formation_driver.set_region(region)
192
- StackMaster::Commands::Delete.perform(region, stack_name)
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 = false unless command.perform(config, stack_definition, options).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! "Failed to run cfn-lint, do you have it installed and available in $PATH?"
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
@@ -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 &&
@@ -1,3 +1,3 @@
1
1
  module StackMaster
2
- VERSION = "1.13.1"
2
+ VERSION = "1.14.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: 1.13.1
4
+ version: 1.14.0
5
5
  platform: x64-mingw32
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-20 00:00:00.000000000 Z
12
+ date: 2019-07-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -435,6 +435,7 @@ files:
435
435
  - lib/stack_master/commands/validate.rb
436
436
  - lib/stack_master/config.rb
437
437
  - lib/stack_master/ctrl_c.rb
438
+ - lib/stack_master/identity.rb
438
439
  - lib/stack_master/paged_response_accumulator.rb
439
440
  - lib/stack_master/parameter_loader.rb
440
441
  - lib/stack_master/parameter_resolver.rb