stack_master 2.7.0 → 2.12.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: f94d2bab69225428425d5e3bc426ca572c781aa5d9e069420c198c86640f84c5
4
- data.tar.gz: f4f164e4390d568e9a7511013a33796ad04862d18510ec5683c2024b9952781f
3
+ metadata.gz: 02eda4742884cf2aa48568ad0e330b375f7548f12f3077be3167bd782a51868d
4
+ data.tar.gz: 6f3159eb1b681bda06e4c128d4746f05484a75f91c1a2119d9c02523f912b48f
5
5
  SHA512:
6
- metadata.gz: 81122997265f0aa25d24f54ee3ccbb5a0a3689b362b37445bd0c12435dc0fcbb24e0d8ce277c949c1e3af5c01d3b5ff938227f6b6f051b97cc8da66f400519d6
7
- data.tar.gz: b5f8114a90fc7cc402e07e34676b7142b3849c4e8eecdc43cb67651358e5e7e6228ab04b68ef5b769b98ff91e7efd2ec9667a5c197699bccbfb96d49848585ed
6
+ metadata.gz: 4670acd897af5bd58083f65187c23c7e7add969ca1d906899ce18065bcc24287d78854bd67baa82a1225dab4277b3392fa03cc3a5ffe49c13cffd3665ca2ea99
7
+ data.tar.gz: 8ea47d21747c13e9685f0f5cacbb12663c28df649823981c5e1a94e982bc7798bd2b93828be921e7e2e4267e3c3aa3d1f246aeb8f99f0eb91d34d2ce24594263
data/README.md CHANGED
@@ -143,7 +143,8 @@ stacks:
143
143
  ## Templates
144
144
 
145
145
  StackMaster supports CloudFormation templates in plain JSON or YAML. Any `.yml` or `.yaml` file will be processed as
146
- YAML, while any `.json` file will be processed as JSON.
146
+ YAML, while any `.json` file will be processed as JSON. Additionally, YAML files can be pre-processed using ERB and
147
+ compile-time parameters.
147
148
 
148
149
  ### Ruby DSLs
149
150
  By default, any template ending with `.rb` will be processed as a [SparkleFormation](https://github.com/sparkleformation/sparkle_formation)
@@ -199,12 +200,13 @@ stacks:
199
200
 
200
201
  ### Compile Time Parameters
201
202
 
202
- Compile time parameters can be used for [SparkleFormation](http://www.sparkleformation.io) templates. It conforms and
203
- allows you to use the [Compile Time Parameters](http://www.sparkleformation.io/docs/sparkle_formation/compile-time-parameters.html) feature.
203
+ Compile time parameters can be defined in a stack's parameters file, using the key `compile_time_parameters`. Keys in
204
+ parameter files are automatically converted to camel case.
204
205
 
205
- A simple example looks like this
206
+ As an example:
206
207
 
207
208
  ```yaml
209
+ # parameters/some_stack.yml
208
210
  vpc_cidr: 10.0.0.0/16
209
211
  compile_time_parameters:
210
212
  subnet_cidrs:
@@ -212,7 +214,37 @@ compile_time_parameters:
212
214
  - 10.0.2.0/28
213
215
  ```
214
216
 
215
- Keys in parameter files are automatically converted to camel case.
217
+ #### SparkleFormation
218
+
219
+ Compile time parameters can be used for [SparkleFormation](http://www.sparkleformation.io) templates. It conforms and
220
+ allows you to use the [Compile Time Parameters](http://www.sparkleformation.io/docs/sparkle_formation/compile-time-parameters.html) feature.
221
+
222
+ #### CloudFormation YAML ERB
223
+
224
+ Compile time parameters can be used to pre-process YAML CloudFormation templates. An example template:
225
+
226
+ ```yaml
227
+ # templates/some_stack_template.yml.erb
228
+ Parameters:
229
+ VpcCidr:
230
+ Type: String
231
+ Resources:
232
+ Vpc:
233
+ Type: AWS::EC2::VPC
234
+ Properties:
235
+ CidrBlock: !Ref VpcCidr
236
+ # Given the two subnet_cidrs parameters, this creates two resources:
237
+ # SubnetPrivate0 with a CidrBlock of 10.0.0.0/28, and
238
+ # SubnetPrivate1 with a CidrBlock of 10.0.2.0/28
239
+ <% params["SubnetCidrs"].each_with_index do |cidr, index| %>
240
+ SubnetPrivate<%= index %>:
241
+ Type: AWS::EC2::Subnet
242
+ Properties:
243
+ VpcId: !Ref Vpc
244
+ AvailabilityZone: ap-southeast-2
245
+ CidrBlock: <%= cidr %>
246
+ <% end %>
247
+ ```
216
248
 
217
249
  ## Parameter Resolvers
218
250
 
@@ -688,6 +720,7 @@ stacks:
688
720
 
689
721
  In the cases where you want to bypass the account check, there is the StackMaster flag `--skip-account-check` that can be used.
690
722
 
723
+
691
724
  ## Commands
692
725
 
693
726
  ```bash
@@ -701,15 +734,20 @@ stack_master apply # Create or update all stacks
701
734
  stack_master --changed apply # Create or update all stacks that have changed
702
735
  stack_master --yes apply [region-or-alias] [stack-name] # Create or update a stack non-interactively (forcing yes)
703
736
  stack_master diff [region-or-alias] [stack-name] # Display a stack template and parameter diff
737
+ stack_master drift [region-or-alias] [stack-name] # Detects and displays stack drift using the CloudFormation Drift API
704
738
  stack_master delete [region-or-alias] [stack-name] # Delete a stack
705
739
  stack_master events [region-or-alias] [stack-name] # Display events for a stack
706
740
  stack_master outputs [region-or-alias] [stack-name] # Display outputs for a stack
707
741
  stack_master resources [region-or-alias] [stack-name] # Display outputs for a stack
708
742
  stack_master status # Displays the status of each stack
709
743
  stack_master tidy # Find missing or extra templates or parameter files
744
+ stack_master compile # Print the compiled version of a given stack
745
+ stack_master validate # Validate a template
746
+ stack_master lint # Check the stack definition locally using cfn-lint
747
+ stack_master nag # Check the stack template with cfn_nag
710
748
  ```
711
749
 
712
- ## Applying updates
750
+ ## Applying updates - `stack_master apply`
713
751
 
714
752
  The apply command does the following:
715
753
 
@@ -726,6 +764,18 @@ Demo:
726
764
 
727
765
  ![Apply Demo](/apply_demo.gif?raw=true)
728
766
 
767
+ ## Drift Detection - `stack_master drift`
768
+
769
+ `stack_master drift us-east-1 mystack` uses the CloudFormation APIs to trigger drift detection and display resources
770
+ that have changed outside of the CloudFormation stack. This can happen if a resource has been updated via the console or
771
+ CLI directly rather than via a stack update.
772
+
773
+ ## Diff - `stack_master diff`
774
+
775
+ `stack_master diff us-east-1 mystack` displays whether the computed parameters or template differ to what was last
776
+ applied in CloudFormation. This can happen if the template or computed parameters have changed in code and the change
777
+ hasn't been applied to this stack.
778
+
729
779
  ## Maintainers
730
780
 
731
781
  - [Steve Hodgkiss](https://github.com/stevehodgkiss)
@@ -24,6 +24,7 @@ module StackMaster
24
24
  autoload :CLI, 'stack_master/cli'
25
25
  autoload :CtrlC, 'stack_master/ctrl_c'
26
26
  autoload :Command, 'stack_master/command'
27
+ autoload :Diff, 'stack_master/diff'
27
28
  autoload :VERSION, 'stack_master/version'
28
29
  autoload :Stack, 'stack_master/stack'
29
30
  autoload :Prompter, 'stack_master/prompter'
@@ -51,11 +52,13 @@ module StackMaster
51
52
  require 'stack_master/template_compilers/sparkle_formation'
52
53
  require 'stack_master/template_compilers/json'
53
54
  require 'stack_master/template_compilers/yaml'
55
+ require 'stack_master/template_compilers/yaml_erb'
54
56
  require 'stack_master/template_compilers/cfndsl'
55
57
 
56
58
  module Commands
57
59
  autoload :TerminalHelper, 'stack_master/commands/terminal_helper'
58
60
  autoload :Apply, 'stack_master/commands/apply'
61
+ autoload :Drift, 'stack_master/commands/drift'
59
62
  autoload :Events, 'stack_master/commands/events'
60
63
  autoload :Outputs, 'stack_master/commands/outputs'
61
64
  autoload :Init, 'stack_master/commands/init'
@@ -68,6 +71,7 @@ module StackMaster
68
71
  autoload :Delete, 'stack_master/commands/delete'
69
72
  autoload :Status, 'stack_master/commands/status'
70
73
  autoload :Tidy, 'stack_master/commands/tidy'
74
+ autoload :Nag, 'stack_master/commands/nag'
71
75
  end
72
76
 
73
77
  module ParameterResolvers
@@ -198,4 +202,16 @@ module StackMaster
198
202
  def stderr=(io)
199
203
  @stderr = io
200
204
  end
205
+
206
+ def colorize(text, color)
207
+ if colorize?
208
+ Rainbow(text).color(color)
209
+ else
210
+ text
211
+ end
212
+ end
213
+
214
+ def colorize?
215
+ ENV.fetch('COLORIZE') { 'true' } == 'true'
216
+ end
201
217
  end
@@ -28,7 +28,10 @@ module StackMaster
28
28
  :update_stack,
29
29
  :create_stack,
30
30
  :validate_template,
31
- :describe_stacks
31
+ :describe_stacks,
32
+ :detect_stack_drift,
33
+ :describe_stack_drift_detection_status,
34
+ :describe_stack_resource_drifts
32
35
 
33
36
  private
34
37
 
@@ -146,6 +146,17 @@ module StackMaster
146
146
  end
147
147
  end
148
148
 
149
+ command :nag do |c|
150
+ c.syntax = 'stack_master nag [region_or_alias] [stack_name]'
151
+ c.summary = "Check this stack's template with cfn_nag"
152
+ c.description = "Runs SAST scan cfn_nag on the template"
153
+ c.example 'run cfn_nag on stack myapp-vpc with us-east-1 settings', 'stack_master nag us-east-1 myapp-vpc'
154
+ c.action do |args, options|
155
+ options.default config: default_config_file
156
+ execute_stacks_command(StackMaster::Commands::Nag, args, options)
157
+ end
158
+ end
159
+
149
160
  command :compile do |c|
150
161
  c.syntax = 'stack_master compile [region_or_alias] [stack_name]'
151
162
  c.summary = "Print the compiled version of a given stack"
@@ -215,6 +226,18 @@ module StackMaster
215
226
  end
216
227
  end
217
228
 
229
+ command :drift do |c|
230
+ c.syntax = 'stack_master drift [region_or_alias] [stack_name]'
231
+ c.summary = 'Detects and displays stack drift using the CloudFormation Drift API'
232
+ c.description = 'Detects and displays stack drift'
233
+ c.option '--timeout SECONDS', Integer, "The number of seconds to wait for drift detection to complete"
234
+ c.example 'view stack drift for a stack named myapp-vpc in us-east-1', 'stack_master drift us-east-1 myapp-vpc'
235
+ c.action do |args, options|
236
+ options.default config: default_config_file, timeout: 120
237
+ execute_stacks_command(StackMaster::Commands::Drift, args, options)
238
+ end
239
+ end
240
+
218
241
  run!
219
242
  end
220
243
 
@@ -242,6 +265,7 @@ module StackMaster
242
265
  stack_definitions = config.filter(region, stack_name)
243
266
  if stack_definitions.empty?
244
267
  StackMaster.stdout.puts "Could not find stack definition #{stack_name} in region #{region}"
268
+ show_other_region_candidates(config, stack_name)
245
269
  success = false
246
270
  end
247
271
  stack_definitions = stack_definitions.select do |stack_definition|
@@ -258,6 +282,13 @@ module StackMaster
258
282
  @kernel.exit false unless success
259
283
  end
260
284
 
285
+ def show_other_region_candidates(config, stack_name)
286
+ candidates = config.filter(region="", stack_name=stack_name)
287
+ return if candidates.empty?
288
+
289
+ StackMaster.stdout.puts "Stack name #{stack_name} exists in regions: #{candidates.map(&:region).join(', ')}"
290
+ end
291
+
261
292
  def execute_if_allowed_account(allowed_accounts, &block)
262
293
  raise ArgumentError, "Block required to execute this method" unless block_given?
263
294
  if running_in_allowed_account?(allowed_accounts)
@@ -0,0 +1,118 @@
1
+ require 'diffy'
2
+
3
+ module StackMaster
4
+ module Commands
5
+ class Drift
6
+ include Command
7
+ include Commander::UI
8
+
9
+ DETECTION_COMPLETE_STATES = [
10
+ 'DETECTION_COMPLETE',
11
+ 'DETECTION_FAILED'
12
+ ]
13
+
14
+ def perform
15
+ detect_stack_drift_result = cf.detect_stack_drift(stack_name: stack_name)
16
+ drift_results = wait_for_drift_results(detect_stack_drift_result.stack_drift_detection_id)
17
+
18
+ puts colorize("Drift Status: #{drift_results.stack_drift_status}", stack_drift_status_color(drift_results.stack_drift_status))
19
+ return if drift_results.stack_drift_status == 'IN_SYNC'
20
+
21
+ failed
22
+
23
+ resp = cf.describe_stack_resource_drifts(stack_name: stack_name)
24
+ resp.stack_resource_drifts.each do |drift|
25
+ display_drift(drift)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def cf
32
+ @cf ||= StackMaster.cloud_formation_driver
33
+ end
34
+
35
+ def display_drift(drift)
36
+ color = drift_color(drift)
37
+ puts colorize([drift.stack_resource_drift_status,
38
+ drift.resource_type,
39
+ drift.logical_resource_id,
40
+ drift.physical_resource_id].join(' '), color)
41
+ return unless drift.stack_resource_drift_status == 'MODIFIED'
42
+
43
+ unless drift.property_differences.empty?
44
+ puts colorize(' Property differences:', color)
45
+ end
46
+ drift.property_differences.each do |property_difference|
47
+ puts colorize(" - #{property_difference.difference_type} #{property_difference.property_path}", color)
48
+ end
49
+ puts colorize(' Resource diff:', color)
50
+ display_resource_drift(drift)
51
+ end
52
+
53
+ def display_resource_drift(drift)
54
+ diff = ::StackMaster::Diff.new(before: prettify_json(drift.expected_properties),
55
+ after: prettify_json(drift.actual_properties))
56
+ diff.display_colorized_diff
57
+ end
58
+
59
+ def prettify_json(string)
60
+ JSON.pretty_generate(JSON.parse(string)) + "\n"
61
+ rescue StandardError => e
62
+ puts "Failed to prettify drifted resource: #{e.message}"
63
+ string
64
+ end
65
+
66
+ def stack_drift_status_color(stack_drift_status)
67
+ case stack_drift_status
68
+ when 'IN_SYNC'
69
+ :green
70
+ when 'DRIFTED'
71
+ :yellow
72
+ else
73
+ :blue
74
+ end
75
+ end
76
+
77
+ def drift_color(drift)
78
+ case drift.stack_resource_drift_status
79
+ when 'IN_SYNC'
80
+ :green
81
+ when 'MODIFIED'
82
+ :yellow
83
+ when 'DELETED'
84
+ :red
85
+ else
86
+ :blue
87
+ end
88
+ end
89
+
90
+ def wait_for_drift_results(detection_id)
91
+ resp = nil
92
+ start_time = Time.now
93
+ loop do
94
+ resp = cf.describe_stack_drift_detection_status(stack_drift_detection_id: detection_id)
95
+ break if DETECTION_COMPLETE_STATES.include?(resp.detection_status)
96
+
97
+ elapsed_time = Time.now - start_time
98
+ if elapsed_time > @options.timeout
99
+ raise "Timeout waiting for stack drift detection"
100
+ end
101
+
102
+ sleep SLEEP_SECONDS
103
+ end
104
+ resp
105
+ end
106
+
107
+ def puts(string)
108
+ StackMaster.stdout.puts(string)
109
+ end
110
+
111
+ extend Forwardable
112
+ def_delegators :@stack_definition, :stack_name, :region
113
+ def_delegators :StackMaster, :colorize
114
+
115
+ SLEEP_SECONDS = 1
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,30 @@
1
+ module StackMaster
2
+ module Commands
3
+ class Nag
4
+ include Command
5
+ include Commander::UI
6
+
7
+ def perform
8
+ rv = Tempfile.open(['stack', "___#{stack_definition.stack_name}.#{proposed_stack.template_format}"]) do |f|
9
+ f.write(proposed_stack.template_body)
10
+ f.flush
11
+ system('cfn_nag', f.path)
12
+ $?.exitstatus
13
+ end
14
+
15
+ failed!("cfn_nag check failed with exit status #{rv}") if rv > 0
16
+ end
17
+
18
+ private
19
+
20
+ def stack_definition
21
+ @stack_definition ||= @config.find_stack(@region, @stack_name)
22
+ end
23
+
24
+ def proposed_stack
25
+ @proposed_stack ||= Stack.generate(stack_definition, @config)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -92,6 +92,7 @@ module StackMaster
92
92
  json: :json,
93
93
  yml: :yaml,
94
94
  yaml: :yaml,
95
+ erb: :yaml_erb,
95
96
  }
96
97
  end
97
98
 
@@ -0,0 +1,45 @@
1
+ module StackMaster
2
+ class Diff
3
+ def initialize(name: nil, before:, after:, context: 10_000)
4
+ @name = name
5
+ @before = before
6
+ @after = after
7
+ @context = context
8
+ end
9
+
10
+ def display
11
+ stdout.print "#{@name} diff: "
12
+ if diff == ''
13
+ stdout.puts "No changes"
14
+ else
15
+ stdout.puts
16
+ display_colorized_diff
17
+ end
18
+ end
19
+
20
+ def display_colorized_diff
21
+ diff.each_line do |line|
22
+ if line.start_with?('+')
23
+ stdout.print colorize(line, :green)
24
+ elsif line.start_with?('-')
25
+ stdout.print colorize(line, :red)
26
+ else
27
+ stdout.print line
28
+ end
29
+ end
30
+ end
31
+
32
+ def different?
33
+ diff != ''
34
+ end
35
+
36
+ private
37
+
38
+ def diff
39
+ @diff ||= Diffy::Diff.new(@before, @after, context: @context).to_s
40
+ end
41
+
42
+ extend Forwardable
43
+ def_delegators :StackMaster, :colorize, :stdout
44
+ end
45
+ end
@@ -19,7 +19,7 @@ module StackMaster
19
19
 
20
20
  def has_invalid_values?
21
21
  values = build_values(@definition, @parameter)
22
- values.include?(nil) || values.include?('')
22
+ values.include?(nil)
23
23
  end
24
24
 
25
25
  def create_error
@@ -10,13 +10,13 @@ module StackMaster
10
10
 
11
11
  def proposed_template
12
12
  return @proposed_stack.template_body unless @proposed_stack.template_format == :json
13
- JSON.pretty_generate(JSON.parse(@proposed_stack.template_body))
13
+ JSON.pretty_generate(JSON.parse(@proposed_stack.template_body)) + "\n"
14
14
  end
15
15
 
16
16
  def current_template
17
17
  return '' unless @current_stack
18
18
  return @current_stack.template_body unless @current_stack.template_format == :json
19
- JSON.pretty_generate(TemplateUtils.template_hash(@current_stack.template_body))
19
+ JSON.pretty_generate(TemplateUtils.template_hash(@current_stack.template_body)) + "\n"
20
20
  end
21
21
 
22
22
  def current_parameters
@@ -39,24 +39,30 @@ module StackMaster
39
39
  end
40
40
 
41
41
  def body_different?
42
- body_diff != ''
42
+ body_diff.different?
43
43
  end
44
44
 
45
45
  def body_diff
46
- @body_diff ||= Diffy::Diff.new(current_template, proposed_template, context: 7, include_diff_info: true).to_s
46
+ @body_diff ||= Diff.new(name: 'Stack',
47
+ before: current_template,
48
+ after: proposed_template,
49
+ context: 7)
47
50
  end
48
51
 
49
52
  def params_different?
50
- params_diff != ''
53
+ parameters_diff.different?
51
54
  end
52
55
 
53
- def params_diff
54
- @param_diff ||= Diffy::Diff.new(current_parameters, proposed_parameters, {}).to_s
56
+ def parameters_diff
57
+ @param_diff ||= Diff.new(name: 'Parameters',
58
+ before: current_parameters,
59
+ after: proposed_parameters)
55
60
  end
56
61
 
57
62
  def output_diff
58
- display_diff('Stack', body_diff)
59
- display_diff('Parameters', params_diff)
63
+ body_diff.display
64
+ parameters_diff.display
65
+
60
66
  unless noecho_keys.empty?
61
67
  StackMaster.stdout.puts " * can not tell if NoEcho parameters are different."
62
68
  end
@@ -83,38 +89,8 @@ module StackMaster
83
89
 
84
90
  private
85
91
 
86
- def display_diff(thing, diff)
87
- StackMaster.stdout.print "#{thing} diff: "
88
- if diff == ''
89
- StackMaster.stdout.puts "No changes"
90
- else
91
- StackMaster.stdout.puts
92
- diff.each_line do |line|
93
- if line.start_with?('+')
94
- StackMaster.stdout.print colorize(line, :green)
95
- elsif line.start_with?('-')
96
- StackMaster.stdout.print colorize(line, :red)
97
- else
98
- StackMaster.stdout.print line
99
- end
100
- end
101
- end
102
- end
103
-
104
92
  def sort_params(hash)
105
93
  hash.sort.to_h
106
94
  end
107
-
108
- def colorize(text, color)
109
- if colorize?
110
- Rainbow(text).color(color)
111
- else
112
- text
113
- end
114
- end
115
-
116
- def colorize?
117
- ENV.fetch('COLORIZE') { 'true' } == 'true'
118
- end
119
95
  end
120
96
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StackMaster::TemplateCompilers
4
+ class YamlErb
5
+ def self.require_dependencies
6
+ require 'erubis'
7
+ require 'yaml'
8
+ end
9
+
10
+ def self.compile(template_dir, template, compile_time_parameters, _compiler_options = {})
11
+ template_file_path = File.join(template_dir, template)
12
+ template = Erubis::Eruby.new(File.read(template_file_path))
13
+ template.filename = template_file_path
14
+
15
+ template.result(params: compile_time_parameters)
16
+ end
17
+
18
+ StackMaster::TemplateCompiler.register(:yaml_erb, self)
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module StackMaster
2
- VERSION = "2.7.0"
2
+ VERSION = "2.12.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.7.0
4
+ version: 2.12.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-06-15 00:00:00.000000000 Z
12
+ date: 2020-10-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -437,6 +437,20 @@ dependencies:
437
437
  - - ">="
438
438
  - !ruby/object:Gem::Version
439
439
  version: '0'
440
+ - !ruby/object:Gem::Dependency
441
+ name: cfn-nag
442
+ requirement: !ruby/object:Gem::Requirement
443
+ requirements:
444
+ - - "~>"
445
+ - !ruby/object:Gem::Version
446
+ version: 0.6.7
447
+ type: :runtime
448
+ prerelease: false
449
+ version_requirements: !ruby/object:Gem::Requirement
450
+ requirements:
451
+ - - "~>"
452
+ - !ruby/object:Gem::Version
453
+ version: 0.6.7
440
454
  description: ''
441
455
  email:
442
456
  - steve@hodgkiss.me
@@ -459,10 +473,12 @@ files:
459
473
  - lib/stack_master/commands/compile.rb
460
474
  - lib/stack_master/commands/delete.rb
461
475
  - lib/stack_master/commands/diff.rb
476
+ - lib/stack_master/commands/drift.rb
462
477
  - lib/stack_master/commands/events.rb
463
478
  - lib/stack_master/commands/init.rb
464
479
  - lib/stack_master/commands/lint.rb
465
480
  - lib/stack_master/commands/list_stacks.rb
481
+ - lib/stack_master/commands/nag.rb
466
482
  - lib/stack_master/commands/outputs.rb
467
483
  - lib/stack_master/commands/resources.rb
468
484
  - lib/stack_master/commands/status.rb
@@ -471,6 +487,7 @@ files:
471
487
  - lib/stack_master/commands/validate.rb
472
488
  - lib/stack_master/config.rb
473
489
  - lib/stack_master/ctrl_c.rb
490
+ - lib/stack_master/diff.rb
474
491
  - lib/stack_master/identity.rb
475
492
  - lib/stack_master/paged_response_accumulator.rb
476
493
  - lib/stack_master/parameter_loader.rb
@@ -522,6 +539,7 @@ files:
522
539
  - lib/stack_master/template_compilers/json.rb
523
540
  - lib/stack_master/template_compilers/sparkle_formation.rb
524
541
  - lib/stack_master/template_compilers/yaml.rb
542
+ - lib/stack_master/template_compilers/yaml_erb.rb
525
543
  - lib/stack_master/template_utils.rb
526
544
  - lib/stack_master/test_driver/cloud_formation.rb
527
545
  - lib/stack_master/test_driver/s3.rb
@@ -539,8 +557,8 @@ licenses:
539
557
  metadata:
540
558
  bug_tracker_uri: https://github.com/envato/stack_master/issues
541
559
  changelog_uri: https://github.com/envato/stack_master/blob/master/CHANGELOG.md
542
- documentation_uri: https://www.rubydoc.info/gems/stack_master/2.7.0
543
- source_code_uri: https://github.com/envato/stack_master/tree/v2.7.0
560
+ documentation_uri: https://www.rubydoc.info/gems/stack_master/2.12.0
561
+ source_code_uri: https://github.com/envato/stack_master/tree/v2.12.0
544
562
  post_install_message:
545
563
  rdoc_options: []
546
564
  require_paths:
@@ -556,7 +574,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
556
574
  - !ruby/object:Gem::Version
557
575
  version: '0'
558
576
  requirements: []
559
- rubygems_version: 3.0.3
577
+ rubygems_version: 3.1.2
560
578
  signing_key:
561
579
  specification_version: 4
562
580
  summary: StackMaster is a sure-footed way of creating, updating and keeping track