cfn-guardian 0.1.0 → 0.6.3

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.
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