cfn_monitor 0.1.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.
@@ -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