cfn_monitor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,178 @@
1
+ require 'cfndsl'
2
+ require_relative '../ext/alarms'
3
+
4
+ CloudFormation do
5
+ Description("CloudWatch Alarms #{template_number}")
6
+
7
+ Parameter("MonitoredStack"){
8
+ Type 'String'
9
+ }
10
+ Parameter("SnsTopicCrit"){
11
+ Type 'String'
12
+ }
13
+ Parameter("SnsTopicWarn"){
14
+ Type 'String'
15
+ }
16
+ Parameter("SnsTopicTask"){
17
+ Type 'String'
18
+ }
19
+ Parameter("MonitoringDisabled"){
20
+ Type 'String'
21
+ }
22
+ Parameter("EnvironmentType"){
23
+ Type 'String'
24
+ }
25
+ Parameter("EnvironmentName"){
26
+ Type 'String'
27
+ }
28
+
29
+ Condition('MonitoringDisabled', FnEquals(Ref("MonitoringDisabled"),'true'))
30
+ Condition('CritSNS', FnNot(FnEquals(Ref("SnsTopicCrit"),'')))
31
+ Condition('WarnSNS', FnNot(FnEquals(Ref("SnsTopicWarn"),'')))
32
+ Condition('TaskSNS', FnNot(FnEquals(Ref("SnsTopicTask"),'')))
33
+
34
+ actionsEnabledMap = {
35
+ crit: FnIf('CritSNS',[ Ref('SnsTopicCrit') ], [ ]),
36
+ warn: FnIf('WarnSNS',[ Ref('SnsTopicWarn') ], [ ]),
37
+ task: FnIf('TaskSNS',[ Ref('SnsTopicTask') ], [ ])
38
+ }
39
+
40
+ alarms.each do |alarm|
41
+
42
+ # Should create or disable the alarms?
43
+ next if (defined? alarm[:parameters]['CreateAlarm']) and alarm[:parameters]['CreateAlarm'] == false
44
+
45
+ if (defined? alarm[:parameters]['DisableAlarm']) and alarm[:parameters]['DisableAlarm'] == true then
46
+ alarm[:parameters]['ActionsEnabled'] = 'false'
47
+ alarm[:parameters].delete('DisableAlarm') # Remove the key from the CFN output
48
+ end
49
+
50
+ resourceGroup = alarm[:resource]
51
+ resources = resourceGroup.split('/')
52
+ type = alarm[:type]
53
+ template = alarm[:template]
54
+ name = alarm[:alarm]
55
+ params = alarm[:parameters]
56
+ cmd = params['cmd']
57
+
58
+ alarmHash = Digest::MD5.hexdigest "#{resourceGroup}#{template}#{name}#{cmd}"
59
+
60
+ # Set defaults for optional parameters
61
+ params['TreatMissingData'] ||= 'missing'
62
+ params['AlarmDescription'] ||= FnJoin(' ', [ Ref('MonitoredStack'), "#{template}", "#{name}", FnSub(resourceGroup, env: Ref('EnvironmentName')) ])
63
+ params['ActionsEnabled'] = 'true' if !params.key?('ActionsEnabled')
64
+ params['Period'] ||= 60
65
+
66
+ # Replace variables in parameters
67
+ params.each do |k,v|
68
+ replace_vars(params[k],'${name}',resourceGroup)
69
+ replace_vars(params[k],'${metric}',resourceGroup)
70
+ replace_vars(params[k],'${resource}',resourceGroup)
71
+ replace_vars(params[k],'${endpoint}',resourceGroup)
72
+ replace_vars(params[k],'${templateName}',template)
73
+ replace_vars(params[k],'${alarmName}',name)
74
+ replace_vars(params[k],'${cmd}',cmd)
75
+ end
76
+
77
+ # Alarm action defaults
78
+ if !params['AlarmActions'].nil?
79
+ alarmActions = actionsEnabledMap[ params['AlarmActions'].downcase.to_sym ]
80
+ else
81
+ alarmActions = actionsEnabledMap['crit']
82
+ end
83
+
84
+ if !params['InsufficientDataActions'].nil?
85
+ insufficientDataActions = actionsEnabledMap[ params['InsufficientDataActions'].downcase.to_sym ]
86
+ elsif !params['AlarmActions'].nil? && params['AlarmActions'].downcase.to_sym == :task
87
+ insufficientDataActions = []
88
+ elsif !params['AlarmActions'].nil?
89
+ insufficientDataActions = actionsEnabledMap[ params['AlarmActions'].downcase.to_sym ]
90
+ else
91
+ insufficientDataActions = actionsEnabledMap['crit']
92
+ end
93
+
94
+ if !params['OKActions'].nil?
95
+ oKActions = actionsEnabledMap[ params['OKActions'].downcase.to_sym ]
96
+ elsif !params['AlarmActions'].nil? && params['AlarmActions'].downcase.to_sym == :task
97
+ oKActions = []
98
+ elsif !params['AlarmActions'].nil?
99
+ oKActions = actionsEnabledMap[ params['AlarmActions'].downcase.to_sym ]
100
+ else
101
+ oKActions = actionsEnabledMap['crit']
102
+ end
103
+
104
+ conditions = []
105
+
106
+ # Configure resource parameters
107
+ if type == 'resource'
108
+ # Configure physical resource inputs
109
+ dimensionsNames = params['DimensionsName'].split('/')
110
+ dimensions = []
111
+ resources.each_with_index do |resource,index|
112
+ resourceHash = Digest::MD5.hexdigest resource
113
+ # Create parameters for incoming physical resource IDs
114
+ Parameter("GetPhysicalId#{resourceHash}") do
115
+ Type 'String'
116
+ end
117
+ # Transform physical resource IDs into dimension values if required
118
+ dimensionValue = Ref("GetPhysicalId#{resourceHash}")
119
+ dimensionValue = FnSelect('5',FnSplit(':',Ref("GetPhysicalId#{resourceHash}"))) if dimensionsNames[index] == 'TargetGroup'
120
+ dimensionValue = FnSelect('1',FnSplit('loadbalancer/',Ref("GetPhysicalId#{resourceHash}"))) if dimensionsNames[index] == 'LoadBalancer'
121
+ dimensionValue = FnSelect('1',FnSplit('service/',Ref("GetPhysicalId#{resourceHash}"))) if dimensionsNames[index] == 'ServiceName'
122
+ dimensionValue = FnJoin('', [Ref("GetPhysicalId#{resourceHash}"),'-001']) if dimensionsNames[index] == 'CacheClusterId'
123
+ # Prepare conditions based on physical resource ID values
124
+ conditions << FnNot(FnEquals(Ref("GetPhysicalId#{resourceHash}"),'null'))
125
+ dimensions << { Name: dimensionsNames[index], Value: dimensionValue }
126
+ end
127
+ params['Dimensions'] = dimensions
128
+ end
129
+
130
+ # Add environment conditions if needed
131
+ if alarm[:environments] != ['all']
132
+ envConditions = []
133
+ alarm[:environments].each do | env |
134
+ envConditions << FnEquals(Ref("EnvironmentName"),env)
135
+ end
136
+ if envConditions.length > 1
137
+ conditions << FnOr(envConditions)
138
+ elsif envConditions.length == 1
139
+ conditions << envConditions[0]
140
+ end
141
+ end
142
+
143
+ # Create conditions
144
+ if conditions.length > 1
145
+ Condition("Condition#{alarmHash}", FnAnd(conditions))
146
+ elsif conditions.length == 1
147
+ Condition("Condition#{alarmHash}", conditions[0])
148
+ end
149
+
150
+ # Create parameter mappings
151
+ create_param_mappings(params,template_envs,alarmHash)
152
+
153
+ # Create alarms
154
+ Resource("Alarm#{alarmHash}") do
155
+ Condition "Condition#{alarmHash}" if conditions.length > 0
156
+ Type('AWS::CloudWatch::Alarm')
157
+ Property('ActionsEnabled', FnIf('MonitoringDisabled', false, FnFindInMap("#{alarmHash}",'ActionsEnabled',Ref('EnvironmentType'))))
158
+ Property('AlarmActions', alarmActions)
159
+ Property('AlarmDescription', params['AlarmDescription'])
160
+ Property('ComparisonOperator', FnFindInMap("#{alarmHash}",'ComparisonOperator',Ref('EnvironmentType')))
161
+ Property('Dimensions', params['Dimensions'])
162
+ Property('EvaluateLowSampleCountPercentile', params['EvaluateLowSampleCountPercentile']) unless params['EvaluateLowSampleCountPercentile'].nil?
163
+ Property('EvaluationPeriods', FnFindInMap("#{alarmHash}",'EvaluationPeriods',Ref('EnvironmentType')))
164
+ Property('ExtendedStatistic', params['ExtendedStatistic']) unless params['ExtendedStatistic'].nil?
165
+ Property('InsufficientDataActions', insufficientDataActions)
166
+ Property('MetricName', FnFindInMap("#{alarmHash}",'MetricName',Ref('EnvironmentType')))
167
+ Property('Namespace', FnFindInMap("#{alarmHash}",'Namespace',Ref('EnvironmentType')))
168
+ Property('OKActions', oKActions)
169
+ Property('Period', FnFindInMap("#{alarmHash}",'Period',Ref('EnvironmentType')))
170
+ Property('Statistic', FnFindInMap("#{alarmHash}",'Statistic',Ref('EnvironmentType')))
171
+ Property('Threshold', FnFindInMap("#{alarmHash}",'Threshold',Ref('EnvironmentType')))
172
+ Property('TreatMissingData',FnFindInMap("#{alarmHash}",'TreatMissingData',Ref('EnvironmentType')))
173
+ Property('Unit', params['Unit']) unless params['Unit'].nil?
174
+ end
175
+
176
+ end
177
+
178
+ end
@@ -0,0 +1,66 @@
1
+ require 'cfndsl'
2
+
3
+ CloudFormation do
4
+ Description("CloudWatch Endpoints")
5
+
6
+ Parameter("MonitoredStack"){
7
+ Type 'String'
8
+ }
9
+ Parameter("EnvironmentName"){
10
+ Type 'String'
11
+ }
12
+ Parameter("HttpCheckFunctionArn"){
13
+ Type 'String'
14
+ }
15
+
16
+ alarms.each do |alarm|
17
+ if alarm[:type] == 'endpoint'
18
+ endpointHash = Digest::MD5.hexdigest alarm[:resource]
19
+
20
+ # Conditionally create shedule based on environments attribute
21
+ if alarm[:environments] != ['all']
22
+ conditions = []
23
+ alarm[:environments].each do | env |
24
+ conditions << FnEquals(Ref("EnvironmentName"),env)
25
+ end
26
+ if conditions.length > 1
27
+ Condition("Condition#{endpointHash}", FnOr(conditions))
28
+ else
29
+ Condition("Condition#{endpointHash}", conditions[0])
30
+ end
31
+ end
32
+
33
+ ep = alarm[:parameters]
34
+
35
+ # Set defaults
36
+ ep['scheduleExpression'] ||= "* * * * ? *"
37
+
38
+ # Create payload
39
+ payload = {}
40
+ payload['TIMEOUT'] = ep['timeOut'] || 120
41
+ payload['STATUS_CODE_MATCH'] = ep['statusCode'] || 200
42
+ payload['ENDPOINT'] = alarm[:resource]
43
+ payload['BODY_REGEX_MATCH'] = ep['bodyRegex'] if !ep['bodyRegex'].nil?
44
+ payload['HEADERS'] = ep['headers'] if !ep['headers'].nil?
45
+ payload['METHOD'] = ep['method'] || "GET"
46
+ payload['PAYLOAD'] = ep['payload'] if !ep['payload'].nil?
47
+
48
+ endpointHash = Digest::MD5.hexdigest alarm[:resource]
49
+ Resource("HttpCheckSchedule#{endpointHash}") do
50
+ Condition "Condition#{endpointHash}" if alarm[:environments] != ['all']
51
+ Type 'AWS::Events::Rule'
52
+ Property('Description', FnSub( payload['ENDPOINT'], env: Ref('EnvironmentName') ) )
53
+ Property('ScheduleExpression', "cron(#{ep['scheduleExpression']})")
54
+ Property('State', 'ENABLED')
55
+ Property('Targets', [
56
+ {
57
+ Arn: Ref('HttpCheckFunctionArn'),
58
+ Id: endpointHash,
59
+ Input: FnSub( payload.to_json, env: Ref('EnvironmentName') )
60
+ }
61
+ ])
62
+ end
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,126 @@
1
+ require 'cfndsl'
2
+
3
+ CloudFormation do
4
+ Description("CloudWatch Hosts")
5
+
6
+ Parameter("EnvironmentName"){
7
+ Type 'String'
8
+ }
9
+
10
+ alarms.each do |alarm|
11
+ if alarm[:type] == 'host'
12
+ hostHash = Digest::MD5.hexdigest alarm[:resource]
13
+
14
+ # Conditionally create shedule based on environments attribute
15
+ if alarm[:environments] != ['all']
16
+ conditions = []
17
+ alarm[:environments].each do | env |
18
+ conditions << FnEquals(Ref("EnvironmentName"),env)
19
+ end
20
+ if conditions.length > 1
21
+ Condition("Condition#{hostHash}", FnOr(conditions))
22
+ else
23
+ Condition("Condition#{hostHash}", conditions[0])
24
+ end
25
+ end
26
+
27
+ # Set defaults
28
+ host = alarm[:parameters]
29
+ host['scheduleExpression'] ||= "* * * * ? *"
30
+
31
+ Resource("NrpeLambdaExecutionRole#{hostHash}") do
32
+ Condition "Condition#{hostHash}" if alarm[:environments] != ['all']
33
+ Type 'AWS::IAM::Role'
34
+ Property('AssumeRolePolicyDocument', {
35
+ Version: '2012-10-17',
36
+ Statement: [{
37
+ Effect: 'Allow',
38
+ Principal: { Service: [ 'lambda.amazonaws.com' ] },
39
+ Action: [ 'sts:AssumeRole' ]
40
+ }]
41
+ })
42
+ Property('Path','/')
43
+ Property('Policies', [
44
+ PolicyName: 'NRPE',
45
+ PolicyDocument: {
46
+ Version: '2012-10-17',
47
+ Statement: [{
48
+ Effect: 'Allow',
49
+ Action: [ 'logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents' ],
50
+ Resource: 'arn:aws:logs:*:*:*'
51
+ },
52
+ {
53
+ Effect: 'Allow',
54
+ Action: [ 'cloudwatch:PutMetricData' ],
55
+ Resource: '*'
56
+ },
57
+ {
58
+ Effect: 'Allow',
59
+ Action: [ 'ec2:CreateNetworkInterface', 'ec2:DescribeNetworkInterfaces', 'ec2:DeleteNetworkInterface' ],
60
+ Resource: '*'
61
+ }]
62
+ }
63
+ ])
64
+ end
65
+
66
+ Resource("SecurityGroup#{hostHash}") {
67
+ Condition "Condition#{hostHash}" if alarm[:environments] != ['all']
68
+ Type 'AWS::EC2::SecurityGroup'
69
+ Property('VpcId', host['vpcId'])
70
+ Property('GroupDescription', "Monitoring Security Group #{hostHash}")
71
+ }
72
+
73
+ Resource("NrpeCheckFunction#{hostHash}") do
74
+ Condition "Condition#{hostHash}" if alarm[:environments] != ['all']
75
+ Type 'AWS::Lambda::Function'
76
+ Property('Code', { S3Bucket: FnJoin('.',['base2.lambda',Ref('AWS::Region')]), S3Key: 'nrpe.zip' })
77
+ Property('Handler', 'nrpe')
78
+ Property('MemorySize', 128)
79
+ Property('Runtime', 'go1.x')
80
+ Property('Timeout', 300)
81
+ Property('Role', FnGetAtt("NrpeLambdaExecutionRole#{hostHash}",'Arn'))
82
+ Property('VpcConfig', {
83
+ SecurityGroupIds: [ Ref("SecurityGroup#{hostHash}") ],
84
+ SubnetIds: host['subnetIds']
85
+ })
86
+ end
87
+
88
+ Resource("NrpeCheckPermissions#{hostHash}") do
89
+ Condition "Condition#{hostHash}" if alarm[:environments] != ['all']
90
+ Type 'AWS::Lambda::Permission'
91
+ Property('FunctionName', Ref("NrpeCheckFunction#{hostHash}"))
92
+ Property('Action', 'lambda:InvokeFunction')
93
+ Property('Principal', 'events.amazonaws.com')
94
+ end
95
+
96
+ cmds = host['cmds']
97
+ if !cmds.kind_of?(Array) then cmds = cmds.split end
98
+ cmds.each do |cmd|
99
+
100
+ # Create payload
101
+ payload = {}
102
+ payload['host'] = alarm[:resource]
103
+ payload['environment'] = "${env}"
104
+ payload['region'] = "${region}"
105
+ payload['cmd'] = cmd
106
+
107
+ cmdHash = Digest::MD5.hexdigest cmd
108
+ Resource("NrpeCheckSchedule#{hostHash}#{cmdHash}") do
109
+ Condition "Condition#{hostHash}" if alarm[:environments] != ['all']
110
+ Type 'AWS::Events::Rule'
111
+ Property('Description', FnSub( "${env}-#{alarm[:resource]} #{cmd}", env: Ref('EnvironmentName') ) )
112
+ Property('ScheduleExpression', "cron(#{host['scheduleExpression']})")
113
+ Property('State', 'ENABLED')
114
+ Property('Targets', [
115
+ {
116
+ Arn: FnGetAtt("NrpeCheckFunction#{hostHash}",'Arn'),
117
+ Id: hostHash,
118
+ Input: FnSub( payload.to_json, env: Ref('EnvironmentName'), region: Ref("AWS::Region") )
119
+ }
120
+ ])
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ end
@@ -0,0 +1,213 @@
1
+ require 'cfndsl'
2
+ require 'cfn_monitor/version'
3
+
4
+ src_lambda_dir = File.expand_path("../../lambda", __FILE__)
5
+
6
+ CloudFormation do
7
+
8
+ Description("CloudWatch Alarms Master")
9
+
10
+ Parameter("MonitoredStack"){
11
+ Type 'String'
12
+ }
13
+ Parameter("SnsTopicCrit"){
14
+ Type 'String'
15
+ }
16
+ Parameter("SnsTopicWarn"){
17
+ Type 'String'
18
+ }
19
+ Parameter("SnsTopicTask"){
20
+ Type 'String'
21
+ }
22
+ Parameter("MonitoringDisabled"){
23
+ Type 'String'
24
+ Default false
25
+ AllowedValues [true,false]
26
+ }
27
+ Parameter("EnvironmentType"){
28
+ Type 'String'
29
+ AllowedValues template_envs
30
+ Default 'production'
31
+ }
32
+ Parameter("ConfigToggle"){
33
+ Type 'String'
34
+ AllowedValues ['up','down']
35
+ Default 'up'
36
+ }
37
+
38
+ Resource("CFLambdaExecutionRole") do
39
+ Type 'AWS::IAM::Role'
40
+ Property('AssumeRolePolicyDocument', {
41
+ Version: '2012-10-17',
42
+ Statement: [{
43
+ Effect: 'Allow',
44
+ Principal: { Service: [ 'lambda.amazonaws.com' ] },
45
+ Action: [ 'sts:AssumeRole' ]
46
+ }]
47
+ })
48
+ Property('Path','/')
49
+ Property('Policies', [
50
+ PolicyName: 'CloudFormationReadOnly',
51
+ PolicyDocument: {
52
+ Version: '2012-10-17',
53
+ Statement: [{
54
+ Effect: 'Allow',
55
+ Action: [ 'logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents' ],
56
+ Resource: 'arn:aws:logs:*:*:*'
57
+ },
58
+ {
59
+ Effect: 'Allow',
60
+ Action: [ 'cloudformation:Describe*', 'cloudformation:Get*', 'cloudformation:List*' ],
61
+ Resource: '*'
62
+ }]
63
+ }
64
+ ])
65
+ end
66
+
67
+ Resource("HttpLambdaExecutionRole") do
68
+ Type 'AWS::IAM::Role'
69
+ Property('AssumeRolePolicyDocument', {
70
+ Version: '2012-10-17',
71
+ Statement: [{
72
+ Effect: 'Allow',
73
+ Principal: { Service: [ 'lambda.amazonaws.com' ] },
74
+ Action: [ 'sts:AssumeRole' ]
75
+ }]
76
+ })
77
+ Property('Path','/')
78
+ Property('Policies', [
79
+ PolicyName: 'CloudFormationReadOnly',
80
+ PolicyDocument: {
81
+ Version: '2012-10-17',
82
+ Statement: [{
83
+ Effect: 'Allow',
84
+ Action: [ 'logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents' ],
85
+ Resource: 'arn:aws:logs:*:*:*'
86
+ },
87
+ {
88
+ Effect: 'Allow',
89
+ Action: [ 'cloudwatch:PutMetricData' ],
90
+ Resource: '*'
91
+ }]
92
+ }
93
+ ])
94
+ end
95
+
96
+ Resource("GetPhysicalIdFunction") do
97
+ Type 'AWS::Lambda::Function'
98
+ Property('Code', { ZipFile: FnJoin("", IO.readlines("#{src_lambda_dir}/getPhysicalId.py").each { |line| "\"#{line}\"," }) })
99
+ Property('Handler', 'index.handler')
100
+ Property('MemorySize', 128)
101
+ Property('Runtime', 'python2.7')
102
+ Property('Timeout', 300)
103
+ Property('Role', FnGetAtt('CFLambdaExecutionRole','Arn'))
104
+ end
105
+
106
+ Resource("GetEnvironmentNameFunction") do
107
+ Type 'AWS::Lambda::Function'
108
+ Property('Code', { ZipFile: FnJoin("", IO.readlines("#{src_lambda_dir}/getEnvironmentName.py").each { |line| "\"#{line}\"," }) })
109
+ Property('Handler', 'index.handler')
110
+ Property('MemorySize', 128)
111
+ Property('Runtime', 'python2.7')
112
+ Property('Timeout', 300)
113
+ Property('Role', FnGetAtt('CFLambdaExecutionRole','Arn'))
114
+ end
115
+
116
+ Resource("GetEnvironmentName") do
117
+ Type 'Custom::GetEnvironmentName'
118
+ Property('ServiceToken',FnGetAtt('GetEnvironmentNameFunction','Arn'))
119
+ Property('StackName', Ref('MonitoredStack'))
120
+ Property('Region', Ref('AWS::Region'))
121
+ Property('ConfigToggle', Ref('ConfigToggle'))
122
+ end
123
+
124
+ Resource("HttpCheckFunction") do
125
+ Type 'AWS::Lambda::Function'
126
+ Property('Code', { S3Bucket: FnJoin('.',[Ref('AWS::Region'),'aws-lambda-http-check']), S3Key: 'httpCheck-v2.zip' })
127
+ Property('Handler', 'handler.http_check')
128
+ Property('MemorySize', 128)
129
+ Property('Runtime', 'python3.6')
130
+ Property('Timeout', 300)
131
+ Property('Role', FnGetAtt('HttpLambdaExecutionRole','Arn'))
132
+ end
133
+
134
+ Resource("HttpCheckPermissions") do
135
+ Type 'AWS::Lambda::Permission'
136
+ Property('FunctionName', Ref('HttpCheckFunction'))
137
+ Property('Action', 'lambda:InvokeFunction')
138
+ Property('Principal', 'events.amazonaws.com')
139
+ end
140
+
141
+ params = {
142
+ MonitoredStack: Ref('MonitoredStack'),
143
+ SnsTopicCrit: Ref('SnsTopicCrit'),
144
+ SnsTopicWarn: Ref('SnsTopicWarn'),
145
+ SnsTopicTask: Ref('SnsTopicTask'),
146
+ MonitoringDisabled: Ref('MonitoringDisabled'),
147
+ EnvironmentType: Ref('EnvironmentType'),
148
+ GetPhysicalIdFunctionArn: FnGetAtt('GetPhysicalIdFunction','Arn'),
149
+ EnvironmentName: FnGetAtt('GetEnvironmentName', 'EnvironmentName' ),
150
+ ConfigToggle: Ref('ConfigToggle')
151
+ }
152
+
153
+ templateCount.times do |i|
154
+ Resource("ResourcesStack#{i}") do
155
+ Type 'AWS::CloudFormation::Stack'
156
+ Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/#{upload_path}/resources#{i}.json")
157
+ Property('TimeoutInMinutes', 5)
158
+ Property('Parameters', params)
159
+ end
160
+ end
161
+
162
+ endpointParams = {
163
+ MonitoredStack: Ref('MonitoredStack'),
164
+ HttpCheckFunctionArn: FnGetAtt('HttpCheckFunction','Arn'),
165
+ EnvironmentName: FnGetAtt('GetEnvironmentName', 'EnvironmentName' )
166
+ }
167
+
168
+ endpoints ||= {}
169
+ if !endpoints.empty?
170
+ Resource("EndpointsStack") do
171
+ Type 'AWS::CloudFormation::Stack'
172
+ Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/#{upload_path}/endpoints.json")
173
+ Property('TimeoutInMinutes', 5)
174
+ Property('Parameters', endpointParams)
175
+ end
176
+ end
177
+
178
+ hostParams = {
179
+ EnvironmentName: FnGetAtt('GetEnvironmentName', 'EnvironmentName' )
180
+ }
181
+
182
+ hosts ||= {}
183
+ if !hosts.empty?
184
+ Resource("HostsStack") do
185
+ Type 'AWS::CloudFormation::Stack'
186
+ Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/#{upload_path}/hosts.json")
187
+ Property('TimeoutInMinutes', 5)
188
+ Property('Parameters', hostParams)
189
+ end
190
+ end
191
+
192
+ servicesParams = {
193
+ EnvironmentName: FnGetAtt('GetEnvironmentName', 'EnvironmentName' )
194
+ }
195
+
196
+ services ||= {}
197
+ if !services.empty?
198
+ Resource("ServicesStack") do
199
+ Type 'AWS::CloudFormation::Stack'
200
+ Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/#{upload_path}/services.json")
201
+ Property('TimeoutInMinutes', 5)
202
+ Property('Parameters', servicesParams)
203
+ end
204
+ end
205
+
206
+ Output("CfnMonitorVersion") { Value(CfnMonitor::VERSION) }
207
+ Output("RenderDate") { Value(Time.now.strftime("%Y-%m-%d")) }
208
+ Output("MonitoredStack") { Value(Ref("MonitoredStack")) }
209
+ Output("StackName") { Value(Ref("AWS::StackName")) }
210
+ Output("Region") { Value(Ref("AWS::Region")) }
211
+ Output("MonitoringDisabled") { Value(Ref("MonitoringDisabled")) }
212
+
213
+ end
@@ -0,0 +1,82 @@
1
+ require 'cfndsl'
2
+
3
+ CloudFormation do
4
+ Description("CloudWatch Resources #{template_number}")
5
+
6
+ Parameter("MonitoredStack"){
7
+ Type 'String'
8
+ }
9
+ Parameter("SnsTopicCrit"){
10
+ Type 'String'
11
+ }
12
+ Parameter("SnsTopicWarn"){
13
+ Type 'String'
14
+ }
15
+ Parameter("SnsTopicTask"){
16
+ Type 'String'
17
+ }
18
+ Parameter("MonitoringDisabled"){
19
+ Type 'String'
20
+ }
21
+ Parameter("EnvironmentType"){
22
+ Type 'String'
23
+ }
24
+ Parameter("GetPhysicalIdFunctionArn"){
25
+ Type 'String'
26
+ }
27
+ Parameter("EnvironmentName"){
28
+ Type 'String'
29
+ }
30
+ Parameter("ConfigToggle"){
31
+ Type 'String'
32
+ }
33
+
34
+ Condition('MonitoringDisabled', FnEquals(Ref("MonitoringDisabled"),'true'))
35
+
36
+ # Create CloudFormation Custom Resources
37
+ customResources = []
38
+ alarms.each do |alarm|
39
+ type = alarm[:type]
40
+ if type == 'resource'
41
+ # Split resources for multi-dimension alarms
42
+ resources = alarm[:resource].split('/')
43
+ resources.each_with_index do |resource,index|
44
+ resourceHash = Digest::MD5.hexdigest resource
45
+ Resource("GetPhysicalId#{resourceHash}") do
46
+ Type 'Custom::GetResourcePhysicalId'
47
+ Property('ServiceToken', Ref('GetPhysicalIdFunctionArn'))
48
+ Property('StackName', Ref('MonitoredStack'))
49
+ Property('LogicalResourceId', FnJoin( '.', [ Ref('MonitoredStack'), resource ] ))
50
+ Property('Region', Ref('AWS::Region'))
51
+ Property('ConfigToggle', Ref('ConfigToggle'))
52
+ end
53
+ customResources << "GetPhysicalId#{resourceHash}"
54
+ # Create outputs for user reference
55
+ Output("#{resource.delete('.')}") { Value(FnGetAtt("GetPhysicalId#{resourceHash}",'PhysicalResourceId')) }
56
+ end
57
+ end
58
+ end
59
+
60
+ params = {
61
+ MonitoredStack: Ref('MonitoredStack'),
62
+ SnsTopicCrit: Ref('SnsTopicCrit'),
63
+ SnsTopicWarn: Ref('SnsTopicWarn'),
64
+ SnsTopicTask: Ref('SnsTopicTask'),
65
+ MonitoringDisabled: Ref('MonitoringDisabled'),
66
+ EnvironmentType: Ref('EnvironmentType'),
67
+ EnvironmentName: Ref('EnvironmentName')
68
+ }
69
+
70
+ # Add custom resources to nested stack params
71
+ customResources.each do |cr|
72
+ params.merge!( cr => FnGetAtt(cr, 'PhysicalResourceId' ))
73
+ end
74
+
75
+ Resource("AlarmsStack#{template_number}") do
76
+ Type 'AWS::CloudFormation::Stack'
77
+ Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/#{upload_path}/alarms#{template_number}.json")
78
+ Property('TimeoutInMinutes', 5)
79
+ Property('Parameters', params)
80
+ end
81
+
82
+ end