cfn_monitor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.travis.yml +18 -0
- data/Dockerfile +7 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +424 -0
- data/Rakefile +9 -0
- data/cfn_monitor.gemspec +39 -0
- data/exe/cfn_monitor +4 -0
- data/lib/cfn_monitor/deploy.rb +47 -0
- data/lib/cfn_monitor/generate.rb +186 -0
- data/lib/cfn_monitor/query.rb +135 -0
- data/lib/cfn_monitor/utils.rb +42 -0
- data/lib/cfn_monitor/version.rb +3 -0
- data/lib/cfn_monitor.rb +66 -0
- data/lib/config/config.yml +14 -0
- data/lib/config/templates.yml +428 -0
- data/lib/ext/alarms.rb +33 -0
- data/lib/lambda/getEnvironmentName.py +39 -0
- data/lib/lambda/getPhysicalId.py +67 -0
- data/lib/templates/alarms.rb +178 -0
- data/lib/templates/endpoints.rb +66 -0
- data/lib/templates/hosts.rb +126 -0
- data/lib/templates/master.rb +213 -0
- data/lib/templates/resources.rb +82 -0
- data/lib/templates/services.rb +138 -0
- metadata +250 -0
@@ -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
|