cfn-guardian 0.1.0 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +1 -0
  3. data/.github/workflows/build-gem.yml +25 -0
  4. data/.github/workflows/release-gem.yml +25 -0
  5. data/.github/workflows/release-image.yml +33 -0
  6. data/.rspec +1 -0
  7. data/Dockerfile +19 -0
  8. data/Gemfile.lock +39 -21
  9. data/README.md +9 -378
  10. data/cfn-guardian.gemspec +7 -5
  11. data/docs/alarm_templates.md +130 -0
  12. data/docs/cli.md +182 -0
  13. data/docs/composite_alarms.md +24 -0
  14. data/docs/custom_checks/azure_file_check.md +28 -0
  15. data/docs/custom_checks/domain_expiry.md +10 -0
  16. data/docs/custom_checks/http.md +59 -0
  17. data/docs/custom_checks/log_group_metric_filters.md +27 -0
  18. data/docs/custom_checks/nrpe.md +29 -0
  19. data/docs/custom_checks/port.md +40 -0
  20. data/docs/custom_checks/sftp.md +73 -0
  21. data/docs/custom_checks/sql.md +44 -0
  22. data/docs/custom_checks/tls.md +25 -0
  23. data/docs/custom_metrics.md +71 -0
  24. data/docs/event_subscriptions.md +67 -0
  25. data/docs/maintenance_mode.md +85 -0
  26. data/docs/notifiers.md +33 -0
  27. data/docs/overview.md +22 -0
  28. data/docs/resources.md +93 -0
  29. data/docs/variables.md +58 -0
  30. data/lib/cfnguardian.rb +325 -37
  31. data/lib/cfnguardian/cloudwatch.rb +132 -0
  32. data/lib/cfnguardian/codecommit.rb +54 -0
  33. data/lib/cfnguardian/codepipeline.rb +138 -0
  34. data/lib/cfnguardian/compile.rb +142 -18
  35. data/lib/cfnguardian/config/defaults.yaml +103 -0
  36. data/lib/cfnguardian/deploy.rb +2 -16
  37. data/lib/cfnguardian/display_formatter.rb +163 -0
  38. data/lib/cfnguardian/drift.rb +79 -0
  39. data/lib/cfnguardian/error.rb +4 -0
  40. data/lib/cfnguardian/log.rb +0 -1
  41. data/lib/cfnguardian/models/alarm.rb +193 -59
  42. data/lib/cfnguardian/models/check.rb +128 -33
  43. data/lib/cfnguardian/models/composite.rb +21 -0
  44. data/lib/cfnguardian/models/event.rb +201 -49
  45. data/lib/cfnguardian/models/event_subscription.rb +96 -0
  46. data/lib/cfnguardian/models/metric_filter.rb +28 -0
  47. data/lib/cfnguardian/resources/amazonmq_rabbitmq.rb +136 -0
  48. data/lib/cfnguardian/resources/application_targetgroup.rb +2 -0
  49. data/lib/cfnguardian/resources/azure_file.rb +20 -0
  50. data/lib/cfnguardian/resources/base.rb +155 -33
  51. data/lib/cfnguardian/resources/ec2_instance.rb +11 -0
  52. data/lib/cfnguardian/resources/ecs_service.rb +2 -2
  53. data/lib/cfnguardian/resources/http.rb +17 -1
  54. data/lib/cfnguardian/resources/internal_http.rb +74 -0
  55. data/lib/cfnguardian/resources/internal_port.rb +33 -0
  56. data/lib/cfnguardian/resources/internal_sftp.rb +58 -0
  57. data/lib/cfnguardian/resources/log_group.rb +26 -0
  58. data/lib/cfnguardian/resources/network_targetgroup.rb +1 -0
  59. data/lib/cfnguardian/resources/port.rb +25 -0
  60. data/lib/cfnguardian/resources/rds_cluster.rb +14 -0
  61. data/lib/cfnguardian/resources/rds_instance.rb +73 -0
  62. data/lib/cfnguardian/resources/redshift_cluster.rb +2 -2
  63. data/lib/cfnguardian/resources/sftp.rb +50 -0
  64. data/lib/cfnguardian/resources/sql.rb +3 -3
  65. data/lib/cfnguardian/resources/tls.rb +66 -0
  66. data/lib/cfnguardian/s3.rb +3 -2
  67. data/lib/cfnguardian/stacks/main.rb +94 -72
  68. data/lib/cfnguardian/stacks/resources.rb +111 -43
  69. data/lib/cfnguardian/string.rb +12 -0
  70. data/lib/cfnguardian/version.rb +1 -1
  71. metadata +133 -10
@@ -0,0 +1,132 @@
1
+ require 'aws-sdk-cloudwatch'
2
+ require 'time'
3
+
4
+ module CfnGuardian
5
+ class CloudWatch
6
+ include Logging
7
+
8
+ def self.get_alarm_name(alarm)
9
+ alarm_id = alarm.resource_name.nil? ? alarm.resource_id : alarm.resource_name
10
+ return "guardian-#{alarm.group}-#{alarm_id}-#{alarm.name}"
11
+ end
12
+
13
+ def self.get_alarms_by_prefix(prefix:, state: nil, action_prefix: nil)
14
+ client = Aws::CloudWatch::Client.new()
15
+ options = {max_records: 100}
16
+ options[:alarm_name_prefix] = prefix
17
+
18
+ unless state.nil?
19
+ options[:state_value] = state
20
+ end
21
+
22
+ unless action_prefix.nil?
23
+ options[:action_prefix] = action_prefix
24
+ end
25
+
26
+ resp = client.describe_alarms(options)
27
+ return resp.metric_alarms
28
+ end
29
+
30
+ def self.get_alarms_by_name(alarm_names:, state: nil, action_prefix: nil)
31
+ client = Aws::CloudWatch::Client.new()
32
+ options = {max_records: 100}
33
+
34
+ unless state.nil?
35
+ options[:state_value] = state
36
+ end
37
+
38
+ unless action_prefix.nil?
39
+ options[:action_prefix] = "arn:aws:sns:#{Aws.config[:region]}:#{aws_account_id()}:#{action_prefix}"
40
+ end
41
+
42
+ metric_alarms = []
43
+ alarm_names.each_slice(100) do |batch|
44
+ options[:alarm_names] = batch
45
+ resp = client.describe_alarms(options)
46
+ metric_alarms.push(*resp.metric_alarms)
47
+ end
48
+
49
+ return metric_alarms
50
+ end
51
+
52
+ def self.filter_alarms(filters:, alarms:)
53
+ return alarms unless filters.is_a?(Hash)
54
+ filters = filters.slice('group', 'resource', 'alarm', 'stack-id')
55
+
56
+ filtered_alarms = []
57
+ alarms.each do |alarm|
58
+ if filters.values.all? {|filter| alarm.alarm_name.include? (filter)}
59
+ filtered_alarms << alarm
60
+ end
61
+ end
62
+
63
+ return filtered_alarms
64
+ end
65
+
66
+ def self.get_alarm_history(alarm_name,type)
67
+ client = Aws::CloudWatch::Client.new()
68
+
69
+ logger.debug "Searching #{type} history for #{alarm_name}"
70
+
71
+ resp = client.describe_alarm_history({
72
+ alarm_name: alarm_name,
73
+ history_item_type: type,
74
+ start_date: (Time.now.utc.to_date - 7),
75
+ end_date: (Time.now.utc.to_date + 1),
76
+ max_records: 100
77
+ })
78
+
79
+ return resp.alarm_history_items
80
+ end
81
+
82
+ def self.get_alarm_names(action_prefix=nil,alarm_name_prefix='guardian')
83
+ alarms = []
84
+ client = Aws::CloudWatch::Client.new
85
+
86
+ options = {
87
+ alarm_types: ["CompositeAlarm","MetricAlarm"],
88
+ alarm_name_prefix: alarm_name_prefix
89
+ }
90
+
91
+ unless action_prefix.nil?
92
+ options[:action_prefix] = "arn:aws:sns:#{Aws.config[:region]}:#{aws_account_id()}:#{action_prefix}"
93
+ end
94
+
95
+ client.describe_alarms(options).each do |response|
96
+ alarms.concat response.composite_alarms.map(&:alarm_name)
97
+ alarms.concat response.metric_alarms.map(&:alarm_name)
98
+ end
99
+
100
+ return alarms
101
+ end
102
+
103
+ def self.disable_alarms(alarms)
104
+ client = Aws::CloudWatch::Client.new
105
+ alarms.each_slice(100) do |batch|
106
+ client.disable_alarm_actions({alarm_names: batch})
107
+ end
108
+ end
109
+
110
+ def self.enable_alarms(alarms)
111
+ client = Aws::CloudWatch::Client.new
112
+ alarms.each_slice(100) do |batch|
113
+ client.enable_alarm_actions({alarm_names: batch})
114
+ end
115
+
116
+ alarms.each do |alarm|
117
+ client.set_alarm_state({
118
+ alarm_name: alarm,
119
+ state_value: "OK",
120
+ state_reason: "End of guardian maintenance peroid"
121
+ })
122
+ end
123
+ end
124
+
125
+ def self.aws_account_id()
126
+ sts = Aws::STS::Client.new
127
+ account = sts.get_caller_identity().account
128
+ return account
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,54 @@
1
+ require 'aws-sdk-codecommit'
2
+ require 'time'
3
+ require 'cfnguardian/log'
4
+
5
+ module CfnGuardian
6
+ class CodeCommit
7
+ include Logging
8
+
9
+ def initialize(repo_name)
10
+ @repo_name = repo_name
11
+ @client = Aws::CodeCommit::Client.new()
12
+ end
13
+
14
+ def get_last_commit(branch='master')
15
+ resp = @client.get_branch({
16
+ repository_name: @repo_name,
17
+ branch_name: branch,
18
+ })
19
+ return resp.branch.commit_id
20
+ end
21
+
22
+ def get_commit_history(branch='master',count=10)
23
+ history = []
24
+ commit = get_last_commit(branch)
25
+
26
+ count.times do
27
+
28
+ resp = @client.get_commit({
29
+ repository_name: @repo_name,
30
+ commit_id: commit
31
+ })
32
+
33
+ time = Time.strptime(resp.commit.committer.date,'%s')
34
+
35
+ history << {
36
+ message: resp.commit.message,
37
+ author: resp.commit.author.name,
38
+ date: time.localtime.strftime("%d/%m/%Y %I:%M %p"),
39
+ id: resp.commit.commit_id
40
+ }
41
+
42
+ if resp.commit.parents.any?
43
+ commit = resp.commit.parents.first
44
+ else
45
+ break
46
+ end
47
+
48
+ end
49
+
50
+ return history
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,138 @@
1
+ require 'aws-sdk-codepipeline'
2
+ require 'time'
3
+ require 'cfnguardian/log'
4
+
5
+ module CfnGuardian
6
+ class CodePipeline
7
+ include Logging
8
+
9
+ def initialize(pipeline_name)
10
+ @pipeline_name = pipeline_name
11
+ client = Aws::CodePipeline::Client.new()
12
+ @pipeline = client.get_pipeline_state({
13
+ name: @pipeline_name
14
+ })
15
+ end
16
+
17
+ def retry()
18
+ resp = client.start_pipeline_execution({
19
+ name: @pipeline_name,
20
+ client_request_token: "ClientRequestToken",
21
+ })
22
+ end
23
+
24
+ def get_stage(stage_name)
25
+ return @pipeline.stage_states.find {|stage| stage.stage_name == stage_name}
26
+ end
27
+
28
+ def colour_rows(rows, status)
29
+ if status == 'Failed'
30
+ rows.map! {|row| row.map! {|r| r.red } }
31
+ elsif status == 'Succeeded'
32
+ rows.map! {|row| row.map! {|r| r.green } }
33
+ elsif status == 'InProgress'
34
+ rows.map! {|row| row.map! {|r| r.blue } }
35
+ elsif ["Stopped", "Stopping"].include? status
36
+ rows.map! {|row| row.map! {|r| r.yellow } }
37
+ end
38
+ end
39
+
40
+ def get_source()
41
+ source_stage = get_stage("Source")
42
+ action = source_stage.action_states.first
43
+ status = source_stage.latest_execution.status
44
+ state = {
45
+ stage: action.action_name,
46
+ rows: [
47
+ ['Status', status],
48
+ ['Commit', action.current_revision.revision_id],
49
+ ['Last Status Change', action.latest_execution.last_status_change.localtime.strftime("%d/%m/%Y %I:%M %p")]
50
+ ]
51
+ }
52
+
53
+ unless action.latest_execution.error_details.nil?
54
+ state[:rows].push(
55
+ ['Error Message', action.latest_execution.error_details.message]
56
+ )
57
+ end
58
+
59
+ colour_rows(state[:rows],status)
60
+
61
+ return state
62
+ end
63
+
64
+ def get_build()
65
+ source_stage = get_stage("Build")
66
+ action = source_stage.action_states.first
67
+ status = source_stage.latest_execution.status
68
+ state = {
69
+ stage: action.action_name,
70
+ rows: [
71
+ ['Status', status],
72
+ ['Build Id', action.latest_execution.external_execution_id],
73
+ ['Last Status Change', action.latest_execution.last_status_change.localtime.strftime("%d/%m/%Y %I:%M %p")],
74
+ ['Logs', action.latest_execution.external_execution_url]
75
+ ]
76
+ }
77
+
78
+ unless action.latest_execution.error_details.nil?
79
+ state[:rows].push(
80
+ ['Error Message', action.latest_execution.error_details.message]
81
+ )
82
+ end
83
+
84
+ colour_rows(state[:rows],status)
85
+
86
+ return state
87
+ end
88
+
89
+ def get_create_changeset()
90
+ source_stage = get_stage("Deploy")
91
+ action = source_stage.action_states.find {|action| action.action_name == "CreateChangeSet"}
92
+ status = source_stage.latest_execution.status
93
+ state = {
94
+ stage: action.action_name,
95
+ rows: [
96
+ ['Status', status],
97
+ ['Summary', action.latest_execution.summary],
98
+ ['Last Status Change', action.latest_execution.last_status_change.localtime.strftime("%d/%m/%Y %I:%M %p")],
99
+ ]
100
+ }
101
+
102
+ unless action.latest_execution.error_details.nil?
103
+ state[:rows].push(
104
+ ['Error Message', action.latest_execution.error_details.message]
105
+ )
106
+ end
107
+
108
+ colour_rows(state[:rows],status)
109
+
110
+ return state
111
+ end
112
+
113
+ def get_deploy_changeset()
114
+ source_stage = get_stage("Deploy")
115
+ action = source_stage.action_states.find {|action| action.action_name == "DeployChangeSet"}
116
+ status = source_stage.latest_execution.status
117
+ state = {
118
+ stage: action.action_name,
119
+ rows: [
120
+ ['Status', status],
121
+ ['Summary', action.latest_execution.summary],
122
+ ['Last Status Change', action.latest_execution.last_status_change.localtime.strftime("%d/%m/%Y %I:%M %p")],
123
+ ]
124
+ }
125
+
126
+ unless action.latest_execution.error_details.nil?
127
+ state[:rows].push(
128
+ ['Error Message', action.latest_execution.error_details.message]
129
+ )
130
+ end
131
+
132
+ colour_rows(state[:rows],status)
133
+
134
+ return state
135
+ end
136
+
137
+ end
138
+ end
@@ -1,7 +1,9 @@
1
1
  require 'yaml'
2
+ require 'fileutils'
2
3
  require 'cfnguardian/string'
3
4
  require 'cfnguardian/stacks/resources'
4
5
  require 'cfnguardian/stacks/main'
6
+ require 'cfnguardian/models/composite'
5
7
  require 'cfnguardian/resources/base'
6
8
  require 'cfnguardian/resources/apigateway'
7
9
  require 'cfnguardian/resources/application_targetgroup'
@@ -18,6 +20,9 @@ require 'cfnguardian/resources/elastic_file_system'
18
20
  require 'cfnguardian/resources/elasticache_replication_group'
19
21
  require 'cfnguardian/resources/elastic_loadbalancer'
20
22
  require 'cfnguardian/resources/http'
23
+ require 'cfnguardian/resources/internal_http'
24
+ require 'cfnguardian/resources/port'
25
+ require 'cfnguardian/resources/internal_port'
21
26
  require 'cfnguardian/resources/nrpe'
22
27
  require 'cfnguardian/resources/lambda'
23
28
  require 'cfnguardian/resources/network_targetgroup'
@@ -26,24 +31,41 @@ require 'cfnguardian/resources/rds_instance'
26
31
  require 'cfnguardian/resources/redshift_cluster'
27
32
  require 'cfnguardian/resources/sql'
28
33
  require 'cfnguardian/resources/sqs_queue'
34
+ require 'cfnguardian/resources/log_group'
35
+ require 'cfnguardian/resources/sftp'
36
+ require 'cfnguardian/resources/internal_sftp'
37
+ require 'cfnguardian/resources/tls'
38
+ require 'cfnguardian/resources/azure_file'
39
+ require 'cfnguardian/resources/amazonmq_rabbitmq'
40
+ require 'cfnguardian/version'
41
+ require 'cfnguardian/error'
29
42
 
30
43
  module CfnGuardian
31
44
  class Compile
32
45
  include Logging
33
46
 
34
- attr_reader :cost, :resources
47
+ attr_reader :cost, :resources, :topics
35
48
 
36
- def initialize(opts,bucket)
37
- @prefix = opts.fetch(:stack_name,'guardian')
38
- @bucket = bucket
39
-
40
- config = YAML.load_file(opts.fetch(:config))
49
+ def initialize(config_file)
50
+ config = YAML.load_file(config_file)
51
+
41
52
  @resource_groups = config.fetch('Resources',{})
53
+ @composites = config.fetch('Composites',{})
42
54
  @templates = config.fetch('Templates',{})
55
+ @topics = config.fetch('Topics',{})
56
+ @maintenance_groups = config.fetch('MaintenaceGroups', {})
57
+ @event_subscriptions = config.fetch('EventSubscriptions', {})
43
58
 
59
+ # Make sure the default topics exist if they aren't supplied in the alarms.yaml
60
+ %w(Critical Warning Task Informational Events).each do |topic|
61
+ @topics[topic] = '' unless @topics.has_key?(topic)
62
+ end
63
+
64
+ @maintenance_group_list = @maintenance_groups.keys.map {|group| "#{group}MaintenanceGroup"}
44
65
  @resources = []
45
66
  @stacks = []
46
67
  @checks = []
68
+ @ssm_parameters = []
47
69
 
48
70
  @cost = 0
49
71
  end
@@ -57,7 +79,7 @@ module CfnGuardian
57
79
  rescue NameError => e
58
80
  if @templates.has_key?(group) && @templates[group].has_key?('Inherit')
59
81
  begin
60
- resource_class = Kernel.const_get("CfnGuardian::Resource::#{@templates[group]['Inherit']}").new(resource)
82
+ resource_class = Kernel.const_get("CfnGuardian::Resource::#{@templates[group]['Inherit']}").new(resource, group)
61
83
  logger.debug "Inheritited resource group #{@templates[group]['Inherit']} for group #{group}"
62
84
  rescue NameError => e
63
85
  logger.warn "'#{@templates[group]['Inherit']}' resource group doesn't exist and is unable to be inherited from"
@@ -69,41 +91,106 @@ module CfnGuardian
69
91
  end
70
92
  end
71
93
 
72
- overides = @templates.has_key?(group) ? @templates[group] : {}
73
- @resources.concat resource_class.get_alarms(overides)
94
+ template_overides = @templates.has_key?(group) ? @templates[group] : {}
95
+ @resources.concat resource_class.get_alarms(group,template_overides)
96
+
97
+ @resources.concat resource_class.get_metric_filters()
74
98
  @resources.concat resource_class.get_events()
99
+
100
+ event_subscriptions = @event_subscriptions.has_key?(group) ? @event_subscriptions[group] : {}
101
+ @resources.concat resource_class.get_event_subscriptions(group,event_subscriptions)
102
+
75
103
  @checks.concat resource_class.get_checks()
76
104
 
77
105
  @cost += resource_class.get_cost
78
106
  end
79
107
  end
108
+
109
+ @maintenance_groups.each do |maintenance_group,resource_groups|
110
+ resource_groups.each do |group, alarms|
111
+ alarms.each do |alarm, resources|
112
+ resources.each do |resource|
113
+
114
+ res = @resources.find {|r|
115
+ (r.type == 'Alarm') &&
116
+ (r.group == group && r.name == alarm) &&
117
+ (r.resource_id == resource['Id'] || r.resource_name == resource['Name'])}
118
+
119
+ unless res.nil?
120
+ res.maintenance_groups.append("#{maintenance_group}MaintenanceGroup")
121
+ end
122
+
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ @composites.each do |name,params|
129
+ @resources.push CfnGuardian::Models::Composite.new(name,params)
130
+ @cost += 0.50
131
+ end
132
+
133
+ @ssm_parameters = @resources.select {|resource| resource.type == 'Event'}.map {|event| event.ssm_parameters}.flatten.uniq
134
+
135
+ validate_resources()
80
136
  end
81
137
 
82
- def split_resources
138
+ def alarms
139
+ @resources.select {|resource| resource.type == 'Alarm'}
140
+ end
141
+
142
+ def validate_resources()
143
+ errors = []
144
+ @resources.each do |resource|
145
+ case resource.type
146
+ when 'Alarm'
147
+ %w(metric_name namespace).each do |property|
148
+ if resource.send(property).nil?
149
+ errors << "Alarm #{resource.name} for resource #{resource.resource_id} has nil value for property #{property.to_camelcase}"
150
+ end
151
+ end
152
+ when 'Check'
153
+ # no validation check yet
154
+ when 'Event'
155
+ # no validation check yet
156
+ when 'Composite'
157
+ # no validation check yet
158
+ when 'EventSubscription'
159
+ # no validation check yet
160
+ when 'MetricFilter'
161
+ # no validation check yet
162
+ end
163
+ end
164
+
165
+ raise CfnGuardian::ValidationError, "#{errors.size} errors found\n[*] #{errors.join("\n[*] ")}" if errors.any?
166
+ end
167
+
168
+ def split_resources(bucket,path)
83
169
  split = @resources.each_slice(200).to_a
84
170
  split.each_with_index do |resources,index|
85
171
  @stacks.push({
86
172
  'Name' => "GuardianStack#{index}",
87
- 'TemplateURL' => "https://#{@bucket}.s3.amazonaws.com/#{@prefix}/guardian-stack-#{index}.compiled.yaml",
173
+ 'TemplateURL' => "https://#{bucket}.s3.amazonaws.com/#{path}/guardian-stack-#{index}.compiled.yaml",
88
174
  'Reference' => index
89
175
  })
90
176
  end
91
177
  return split
92
178
  end
93
179
 
94
- def compile_templates
180
+ def compile_templates(bucket,path)
95
181
  clean_out_directory()
96
- resources = split_resources()
182
+ resources = split_resources(bucket,path)
97
183
 
98
184
  main_stack = CfnGuardian::Stacks::Main.new()
99
- template = main_stack.build_template(@stacks,@checks)
100
- valid = template.validate
185
+ main_stack.build_template(@stacks,@checks,@topics,@maintenance_group_list,@ssm_parameters)
186
+ valid = main_stack.template.validate
187
+ FileUtils.mkdir_p 'out'
101
188
  File.write("out/guardian.compiled.yaml", JSON.parse(valid.to_json).to_yaml)
102
189
 
103
190
  resources.each_with_index do |resources,index|
104
- stack = CfnGuardian::Stacks::Resources.new()
105
- template = stack.build_template(resources)
106
- valid = template.validate
191
+ stack = CfnGuardian::Stacks::Resources.new(main_stack.parameters,index)
192
+ stack.build_template(resources)
193
+ valid = stack.template.validate
107
194
  File.write("out/guardian-stack-#{index}.compiled.yaml", JSON.parse(valid.to_json).to_yaml)
108
195
  end
109
196
  end
@@ -111,6 +198,43 @@ module CfnGuardian
111
198
  def clean_out_directory
112
199
  Dir["out/*.yaml"].each {|file| File.delete(file)}
113
200
  end
201
+
202
+ def load_parameters(options)
203
+ parameters = {}
204
+ # Load sns topic parameters in order of preference
205
+ @topics.each do |key, value|
206
+ # if parameter is passed in as a command line option
207
+ if options.has_key?("sns_#{key.downcase}")
208
+ parameters[key.to_sym] = options["sns_#{key.downcase}"]
209
+ # if parameter is in config
210
+ elsif !value.empty?
211
+ parameters[key.to_sym] = value
212
+ # if parameter is set as environment variable
213
+ elsif ENV.has_key?("GUARDIAN_TOPIC_#{key.upcase}")
214
+ parameters[key.to_sym] = ENV["GUARDIAN_TOPIC_#{key.upcase}"]
215
+ end
216
+ end
217
+
218
+ return parameters
219
+ end
220
+
221
+ def genrate_template_config(parameters)
222
+ template = {
223
+ Tags: {
224
+ 'guardian:version': CfnGuardian::VERSION
225
+ }
226
+ }
227
+
228
+ if ENV.has_key?('CODEBUILD_RESOLVED_SOURCE_VERSION')
229
+ template[:Tags][:'guardian:config:commit'] = ENV['CODEBUILD_RESOLVED_SOURCE_VERSION']
230
+ end
231
+
232
+ unless parameters.empty?
233
+ template[:Parameters] = parameters
234
+ end
235
+
236
+ File.write("out/template-config.guardian.json", template.to_json)
237
+ end
114
238
 
115
239
  end
116
240
  end