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,103 @@
1
+ Resources:
2
+ AmazonMQBroker:
3
+ - Id: Default
4
+ AmazonMQRabbitMQBroker:
5
+ - Id: Default
6
+ AmazonMQRabbitMQNode:
7
+ - Id: Default
8
+ Node: Default
9
+ AmazonMQRabbitMQQueue:
10
+ - Id: Default
11
+ Queue: Default
12
+ Vhost: Default
13
+ ApiGateway:
14
+ - Id: Default
15
+ ApplicationTargetGroup:
16
+ - Id: Default
17
+ LoadBalancer: Default
18
+ AutoScalingGroup:
19
+ - Id: Default
20
+ CloudFrontDistribution:
21
+ - Id: Default
22
+ DomainExpiry:
23
+ - Id: Default
24
+ DynamoDBTable:
25
+ - Id: Default
26
+ Ec2Instance:
27
+ - Id: Default
28
+ ECSCluster:
29
+ - Id: Default
30
+ ECSService:
31
+ - Id: Default
32
+ Cluster: Default
33
+ ElasticFileSystem:
34
+ - Id: Default
35
+ ElasticLoadBalancer:
36
+ - Id: Default
37
+ ElastiCacheReplicationGroup:
38
+ - Id: Default
39
+ Http:
40
+ - Id: Default
41
+ StatusCode: 200
42
+ Ssl: true
43
+ BodyRegex: Default
44
+ InternalHttp:
45
+ - Environment: Default
46
+ VpcId: vpc-default
47
+ Subnets:
48
+ - subnet-default
49
+ Hosts:
50
+ - Id: Default
51
+ StatusCode: 200
52
+ BodyRegex: Default
53
+ Port:
54
+ - Id: Default
55
+ Port: 0
56
+ InternalPort:
57
+ - Environment: Default
58
+ VpcId: vpc-default
59
+ Subnets:
60
+ - subnet-default
61
+ Hosts:
62
+ - Id: Default
63
+ Port: 0
64
+ Lambda:
65
+ - Id: Default
66
+ LogGroup:
67
+ - Id: Default
68
+ MetricFilters:
69
+ - MetricName: Default
70
+ Pattern: Default
71
+ NetworkTargetGroup:
72
+ - Id: Default
73
+ LoadBalancer: Default
74
+ Nrpe:
75
+ - Environment: Default
76
+ VpcId: vpc-default
77
+ Subnets:
78
+ - subnet-default
79
+ Hosts:
80
+ - Id: Default
81
+ Commands:
82
+ - default
83
+ RDSClusterInstance:
84
+ - Id: Default
85
+ RDSInstance:
86
+ - Id: Default
87
+ RedshiftCluster:
88
+ - Id: Default
89
+ Sql:
90
+ - Environment: Default
91
+ VpcId: vpc-default
92
+ Subnets:
93
+ - subnet-default
94
+ Hosts:
95
+ - Id: Default
96
+ SecretId: Default
97
+ Engine: default
98
+ Queries:
99
+ - MetricName: Default
100
+ Query: Default
101
+ SQSQueue:
102
+ - Id: Default
103
+
@@ -7,27 +7,13 @@ module CfnGuardian
7
7
  class Deploy
8
8
  include Logging
9
9
 
10
- def initialize(opts,bucket)
10
+ def initialize(opts,bucket,parameters)
11
11
  @stack_name = opts.fetch(:stack_name,'guardian')
12
12
  @bucket = bucket
13
13
  @prefix = @stack_name
14
14
  @template_path = "out/guardian.compiled.yaml"
15
15
  @template_url = "https://#{@bucket}.s3.amazonaws.com/#{@prefix}/guardian.compiled.yaml"
16
- @parameters = {}
17
-
18
- config = YAML.load_file(opts[:config])
19
- if config.has_key?('Topics')
20
- @parameters['Critical'] = config['Topics'].fetch('Critical','')
21
- @parameters['Warning'] = config['Topics'].fetch('Warning','')
22
- @parameters['Task'] = config['Topics'].fetch('Task','')
23
- @parameters['Informational'] = config['Topics'].fetch('Informational','')
24
- end
25
-
26
- @parameters['Critical'] = opts.fetch(:sns_critical,@parameters['Critical'])
27
- @parameters['Warning'] = opts.fetch(:sns_warning,@parameters['Warning'])
28
- @parameters['Task'] = opts.fetch(:sns_task,@parameters['Task'])
29
- @parameters['Informational'] = opts.fetch(:sns_informational,@parameters['Informational'])
30
-
16
+ @parameters = parameters
31
17
  @client = Aws::CloudFormation::Client.new()
32
18
  end
33
19
 
@@ -0,0 +1,163 @@
1
+ require 'cfnguardian/cloudwatch'
2
+ require 'cfnguardian/string'
3
+ require 'time'
4
+
5
+ module CfnGuardian
6
+ class DisplayFormatter
7
+
8
+ def initialize(alarms=[])
9
+ @alarms = alarms
10
+ end
11
+
12
+ def alarms()
13
+ resp = []
14
+
15
+ @alarms.each do |alarm|
16
+ alarm_name = CfnGuardian::CloudWatch.get_alarm_name(alarm)
17
+ rows = [
18
+ ['ResourceId', alarm.resource_id],
19
+ ['ResourceHash', alarm.resource_hash],
20
+ ['ResourceName', alarm.resource_name],
21
+ ['Enabled', alarm.enabled],
22
+ ['MetricName', alarm.metric_name],
23
+ ['Dimensions', alarm.dimensions],
24
+ ['Threshold', alarm.threshold],
25
+ ['Period', alarm.period],
26
+ ['EvaluationPeriods', alarm.evaluation_periods],
27
+ ['ComparisonOperator', alarm.comparison_operator],
28
+ ['Statistic', alarm.statistic],
29
+ ['ActionsEnabled', alarm.actions_enabled],
30
+ ['DatapointsToAlarm', alarm.datapoints_to_alarm],
31
+ ['ExtendedStatistic', alarm.extended_statistic],
32
+ ['EvaluateLowSampleCountPercentile', alarm.evaluate_low_sample_count_percentile],
33
+ ['Unit', alarm.unit],
34
+ ['AlarmAction', alarm.alarm_action],
35
+ ['TreatMissingData', alarm.treat_missing_data]
36
+ ]
37
+
38
+ rows.select! {|row| !row[1].nil?}
39
+
40
+ resp << {
41
+ title: "#{alarm.group}::#{alarm.name}".green + "\n" + alarm_name.green,
42
+ rows: rows
43
+ }
44
+ end
45
+
46
+ return resp
47
+ end
48
+
49
+ def compare_alarms(metric_alarms)
50
+ resp = []
51
+
52
+ @alarms.each do |alarm|
53
+ alarm_name = CfnGuardian::CloudWatch.get_alarm_name(alarm)
54
+ metric_alarm = metric_alarms.find {|ma| ma.alarm_name.include? alarm_name}
55
+ dimensions = metric_alarm.dimensions.map {|dim| {dim.name.to_sym => dim.value}}.inject(:merge)
56
+
57
+ rows = [
58
+ ['ResourceId', alarm.resource_id, alarm.resource_id],
59
+ ['ResourceHash', alarm.resource_hash, alarm.resource_hash],
60
+ ['ResourceName', alarm.resource_name, alarm.resource_name],
61
+ ['Enabled', alarm.enabled, true],
62
+ ['MetricName', alarm.metric_name, metric_alarm.metric_name],
63
+ ['Dimensions', alarm.dimensions, dimensions],
64
+ ['Threshold', alarm.threshold.to_f, metric_alarm.threshold],
65
+ ['Period', alarm.period, metric_alarm.period],
66
+ ['EvaluationPeriods', alarm.evaluation_periods, metric_alarm.evaluation_periods],
67
+ ['ComparisonOperator', alarm.comparison_operator, metric_alarm.comparison_operator],
68
+ ['Statistic', alarm.statistic, metric_alarm.statistic],
69
+ ['ActionsEnabled', alarm.actions_enabled, metric_alarm.actions_enabled],
70
+ ['DatapointsToAlarm', alarm.datapoints_to_alarm, metric_alarm.datapoints_to_alarm],
71
+ ['ExtendedStatistic', alarm.extended_statistic, metric_alarm.extended_statistic],
72
+ ['EvaluateLowSampleCountPercentile', alarm.evaluate_low_sample_count_percentile, metric_alarm.evaluate_low_sample_count_percentile],
73
+ ['Unit', alarm.unit, metric_alarm.unit],
74
+ ['TreatMissingData', alarm.treat_missing_data, metric_alarm.treat_missing_data],
75
+ ['AlarmAction', alarm.alarm_action, alarm.alarm_action]
76
+ ]
77
+
78
+ rows.select! {|row| !row[1].nil?}.each {|row| colour_compare_row(row)}
79
+
80
+ if has_config_difference?(rows)
81
+ resp << {
82
+ title: "#{alarm.group}::#{alarm.name}".green + "\n" + alarm_name.green,
83
+ rows: rows
84
+ }
85
+ end
86
+ end
87
+
88
+ return resp
89
+ end
90
+
91
+ def alarm_state(metric_alarms)
92
+ rows = []
93
+
94
+ metric_alarms.each do |ma|
95
+ if ma.state_value == 'ALARM'
96
+ state_value = ma.state_value.to_s.red
97
+ elsif ma.state_value == 'INSUFFICIENT_DATA'
98
+ state_value = ma.state_value.to_s.yellow
99
+ else
100
+ state_value = ma.state_value.to_s.green
101
+ end
102
+
103
+ rows << [
104
+ ma.alarm_name,
105
+ state_value,
106
+ ma.state_updated_timestamp.localtime,
107
+ ma.actions_enabled ? 'ENABLED'.green : 'DISABLED'.red
108
+ ]
109
+ end
110
+ # sort by state_value
111
+ return rows.sort_by {|r| r[3]}
112
+ end
113
+
114
+ def alarm_history(history,type)
115
+ rows = []
116
+ line_width = 100
117
+
118
+ history.each do |item|
119
+ data = JSON.load(item.history_data)
120
+
121
+ case type
122
+ when "StateUpdate"
123
+ rows << [
124
+ item.timestamp.localtime,
125
+ item.history_summary,
126
+ data['newState']['stateReason'].word_wrap
127
+ ]
128
+ when "ConfigurationUpdate"
129
+ updated = []
130
+ if data['type'] == 'Update'
131
+ data['originalUpdatedFields'].each do |k,v|
132
+ unless k == 'alarmConfigurationUpdatedTimestamp'
133
+ updated << "#{k}: #{v} -> #{data['updatedAlarm'][k]}"
134
+ end
135
+ end
136
+ end
137
+ rows << [
138
+ item.timestamp.localtime,
139
+ data['type'],
140
+ updated.join("\n").word_wrap
141
+ ]
142
+ end
143
+ end
144
+
145
+ return rows
146
+ end
147
+
148
+ private
149
+
150
+ def has_config_difference?(rows)
151
+ rows.each do |row|
152
+ unless row[1].eql?(row[2])
153
+ return true
154
+ end
155
+ end
156
+ return false
157
+ end
158
+
159
+ def colour_compare_row(row)
160
+ return row[1].eql?(row[2]) ? row.map! {|r| r.to_s.green} : row.map! {|r| r.to_s.red}
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,79 @@
1
+ require 'aws-sdk-cloudformation'
2
+
3
+ module CfnGuardian
4
+ class Drift
5
+
6
+ def initialize(stack)
7
+ @stack = stack
8
+ @client = Aws::CloudFormation::Client.new()
9
+ end
10
+
11
+ def find_nested_stacks
12
+ stacks = []
13
+ resp = @client.describe_stack_resources({
14
+ stack_name: @stack
15
+ })
16
+ resp.stack_resources.each do |r|
17
+ if r.resource_type == 'AWS::CloudFormation::Stack'
18
+ stacks << r.physical_resource_id
19
+ end
20
+ end
21
+ return stacks
22
+ end
23
+
24
+ def detect_drift(stack)
25
+ resp = @client.detect_stack_drift({
26
+ stack_name: stack
27
+ })
28
+ wait_for_dirft_detection(resp.stack_drift_detection_id)
29
+ end
30
+
31
+ def wait_for_dirft_detection(id,count=0)
32
+ resp = @client.describe_stack_drift_detection_status({
33
+ stack_drift_detection_id: id
34
+ })
35
+ if resp.detection_status == 'DETECTION_IN_PROGRESS' && count < 10
36
+ sleep(2)
37
+ count += 1
38
+ wait_for_dirft_detection(id,count)
39
+ end
40
+ end
41
+
42
+ def get_drift(stack)
43
+ rows = []
44
+ resp = @client.describe_stack_resource_drifts({
45
+ stack_name: stack,
46
+ stack_resource_drift_status_filters: ["MODIFIED", "DELETED"]
47
+ })
48
+
49
+ if resp.stack_resource_drifts.any?
50
+ resp.stack_resource_drifts.each do |drift|
51
+ next if drift.resource_type != 'AWS::CloudWatch::Alarm'
52
+
53
+ if drift.stack_resource_drift_status == 'MODIFIED'
54
+ drift.property_differences.each do |diff|
55
+ rows << [
56
+ drift.physical_resource_id,
57
+ diff.property_path,
58
+ diff.expected_value,
59
+ diff.actual_value,
60
+ diff.difference_type
61
+ ]
62
+ end
63
+ elsif drift.stack_resource_drift_status == 'DELETED'
64
+ rows << [
65
+ drift.physical_resource_id.red,
66
+ "",
67
+ "",
68
+ "",
69
+ drift.stack_resource_drift_status.red
70
+ ]
71
+ end
72
+ end
73
+ end
74
+
75
+ return rows
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,4 @@
1
+ module CfnGuardian
2
+ class ValidationError < StandardError
3
+ end
4
+ end
@@ -13,7 +13,6 @@ module Logging
13
13
 
14
14
  def logger
15
15
  @logger ||= Logger.new($stdout)
16
- @logger.level = Logger::DEBUG
17
16
  @logger.formatter = proc do |severity, datetime, progname, msg|
18
17
  "\e[#{colors[severity.to_sym]}m#{severity}: #{msg}\e[0m\n"
19
18
  end
@@ -1,11 +1,14 @@
1
1
  require 'cfnguardian/string'
2
+ require 'digest/md5'
2
3
 
3
4
  module CfnGuardian
4
5
  module Models
5
- class Alarm
6
+ class BaseAlarm
6
7
 
7
- attr_reader :type
8
- attr_accessor :class,
8
+ attr_reader :type,
9
+ :resource_hash
10
+
11
+ attr_accessor :group,
9
12
  :name,
10
13
  :metric_name,
11
14
  :namespace,
@@ -17,21 +20,24 @@ module CfnGuardian
17
20
  :statistic,
18
21
  :actions_enabled,
19
22
  :enabled,
20
- :resource,
23
+ :resource_id,
24
+ :resource_name,
21
25
  :alarm_action,
22
26
  :treat_missing_data,
23
27
  :datapoints_to_alarm,
24
28
  :extended_statistic,
25
29
  :evaluate_low_sample_count_percentile,
26
- :unit
30
+ :unit,
31
+ :maintenance_groups,
32
+ :additional_notifiers
27
33
 
28
34
  def initialize(resource)
29
35
  @type = 'Alarm'
30
- @class = nil
36
+ @group = nil
31
37
  @name = ''
32
38
  @metric_name = nil
33
39
  @namespace = nil
34
- @dimensions = {}
40
+ @dimensions = nil
35
41
  @threshold = 0
36
42
  @period = 60
37
43
  @evaluation_periods = 1
@@ -43,37 +49,35 @@ module CfnGuardian
43
49
  @evaluate_low_sample_count_percentile = nil
44
50
  @unit = nil
45
51
  @enabled = true
46
- @resource_name = Digest::MD5.hexdigest resource['Id']
47
- @resource = resource['Id']
52
+ @resource_hash = Digest::MD5.hexdigest resource['Id']
53
+ @resource_id = resource['Id']
54
+ @resource_name = resource.fetch('Name', nil)
48
55
  @alarm_action = 'Critical'
49
56
  @treat_missing_data = nil
57
+ @maintenance_groups = []
58
+ @additional_notifiers = []
50
59
  end
51
60
 
52
61
  def metric_name=(metric_name)
53
62
  raise ArgumentError.new("metric_name '#{metric_name}' must be of type String, provided type '#{metric_name.class}'") unless metric_name.is_a?(String)
54
63
  @metric_name=metric_name
55
- end
56
-
57
- def to_h
58
- Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] } ]
59
- end
60
-
64
+ end
61
65
  end
62
66
 
63
67
 
64
- class ApiGatewayAlarm < Alarm
68
+ class ApiGatewayAlarm < BaseAlarm
65
69
  def initialize(resource)
66
70
  super(resource)
67
- @class = 'ApiGateway'
71
+ @group = 'ApiGateway'
68
72
  @namespace = 'AWS/ApiGateway'
69
73
  @dimensions = { ApiName: resource['Id'] }
70
74
  end
71
75
  end
72
76
 
73
- class ApplicationTargetGroupAlarm < Alarm
77
+ class ApplicationTargetGroupAlarm < BaseAlarm
74
78
  def initialize(resource)
75
79
  super(resource)
76
- @class = 'ApplicationTargetGroup'
80
+ @group = 'ApplicationTargetGroup'
77
81
  @namespace = 'AWS/ApplicationELB'
78
82
  @dimensions = {
79
83
  TargetGroup: resource['Id'],
@@ -82,19 +86,53 @@ module CfnGuardian
82
86
  end
83
87
  end
84
88
 
85
- class AmazonMQBrokerAlarm < Alarm
89
+ class AmazonMQBrokerAlarm < BaseAlarm
90
+ def initialize(resource)
91
+ super(resource)
92
+ @group = 'AmazonMQBroker'
93
+ @namespace = 'AWS/AmazonMQ'
94
+ @dimensions = { Broker: resource['Id'] }
95
+ end
96
+ end
97
+
98
+ class AmazonMQRabbitMQBrokerAlarm < BaseAlarm
86
99
  def initialize(resource)
87
100
  super(resource)
88
- @class = 'AmazonMQBroker'
101
+ @group = 'AmazonMQRabbitMQBroker'
89
102
  @namespace = 'AWS/AmazonMQ'
90
103
  @dimensions = { Broker: resource['Id'] }
91
104
  end
92
105
  end
106
+
107
+ class AmazonMQRabbitMQNodeAlarm < BaseAlarm
108
+ def initialize(resource)
109
+ super(resource)
110
+ @group = 'AmazonMQRabbitMQNode'
111
+ @namespace = 'AWS/AmazonMQ'
112
+ @dimensions = {
113
+ Broker: resource['Id'],
114
+ Node: resource['Node']
115
+ }
116
+ end
117
+ end
118
+
119
+ class AmazonMQRabbitMQQueueAlarm < BaseAlarm
120
+ def initialize(resource)
121
+ super(resource)
122
+ @group = 'AmazonMQRabbitMQQueue'
123
+ @namespace = 'AWS/AmazonMQ'
124
+ @dimensions = {
125
+ Broker: resource['Id'],
126
+ Queue: resource['Queue'],
127
+ VirtualHost: resource['Vhost']
128
+ }
129
+ end
130
+ end
93
131
 
94
- class CloudFrontDistributionAlarm < Alarm
132
+ class CloudFrontDistributionAlarm < BaseAlarm
95
133
  def initialize(resource)
96
134
  super(resource)
97
- @class = 'CloudFrontDistribution'
135
+ @group = 'CloudFrontDistribution'
98
136
  @namespace = 'AWS/CloudFront'
99
137
  @dimensions = {
100
138
  DistributionId: resource['Id'],
@@ -105,47 +143,47 @@ module CfnGuardian
105
143
  end
106
144
  end
107
145
 
108
- class AutoScalingGroupAlarm < Alarm
146
+ class AutoScalingGroupAlarm < BaseAlarm
109
147
  def initialize(resource)
110
148
  super(resource)
111
- @class = 'AutoScalingGroup'
149
+ @group = 'AutoScalingGroup'
112
150
  @namespace = 'AWS/EC2'
113
151
  @dimensions = { AutoScalingGroupName: resource['Id'] }
114
152
  end
115
153
  end
116
154
 
117
- class DomainExpiryAlarm < Alarm
155
+ class DomainExpiryAlarm < BaseAlarm
118
156
  def initialize(resource)
119
157
  super(resource)
120
- @class = 'DomainExpiry'
158
+ @group = 'DomainExpiry'
121
159
  @namespace = 'DNS'
122
160
  @dimensions = { Domain: resource['Id'] }
123
161
  @comparison_operator = 'LessThanThreshold'
124
162
  end
125
163
  end
126
164
 
127
- class DynamoDBTableAlarm < Alarm
165
+ class DynamoDBTableAlarm < BaseAlarm
128
166
  def initialize(resource)
129
167
  super(resource)
130
- @class = 'DynamoDBTable'
168
+ @group = 'DynamoDBTable'
131
169
  @namespace = 'AWS/DynamoDB'
132
170
  @dimensions = { TableName: resource['Id'] }
133
171
  end
134
172
  end
135
173
 
136
- class Ec2InstanceAlarm < Alarm
174
+ class Ec2InstanceAlarm < BaseAlarm
137
175
  def initialize(resource)
138
176
  super(resource)
139
- @class = 'Ec2Instance'
177
+ @group = 'Ec2Instance'
140
178
  @namespace = 'AWS/EC2'
141
179
  @dimensions = { InstanceId: resource['Id'] }
142
180
  end
143
181
  end
144
182
 
145
- class ECSClusterAlarm < Alarm
183
+ class ECSClusterAlarm < BaseAlarm
146
184
  def initialize(resource)
147
185
  super(resource)
148
- @class = 'ECSCluster'
186
+ @group = 'ECSCluster'
149
187
  @namespace = 'AWS/ECS'
150
188
  @dimensions = { ClusterName: resource['Id'] }
151
189
  @threshold = 75
@@ -154,10 +192,10 @@ module CfnGuardian
154
192
  end
155
193
  end
156
194
 
157
- class ECSServiceAlarm < Alarm
195
+ class ECSServiceAlarm < BaseAlarm
158
196
  def initialize(resource)
159
197
  super(resource)
160
- @class = 'ECSService'
198
+ @group = 'ECSService'
161
199
  @namespace = 'AWS/ECS'
162
200
  @dimensions = {
163
201
  ServiceName: resource['Id'],
@@ -166,37 +204,37 @@ module CfnGuardian
166
204
  end
167
205
  end
168
206
 
169
- class ElastiCacheReplicationGroupAlarm < Alarm
207
+ class ElastiCacheReplicationGroupAlarm < BaseAlarm
170
208
  def initialize(resource)
171
209
  super(resource)
172
- @class = 'ElastiCacheReplicationGroup'
210
+ @group = 'ElastiCacheReplicationGroup'
173
211
  @namespace = 'AWS/ElastiCache'
174
212
  @dimensions = { CacheClusterId: resource['Id'] }
175
213
  end
176
214
  end
177
215
 
178
- class ElasticLoadBalancerAlarm < Alarm
216
+ class ElasticLoadBalancerAlarm < BaseAlarm
179
217
  def initialize(resource)
180
218
  super(resource)
181
- @class = 'ElasticLoadBalancer'
219
+ @group = 'ElasticLoadBalancer'
182
220
  @namespace = 'AWS/ELB'
183
221
  @dimensions = { LoadBalancerName: resource['Id'] }
184
222
  end
185
223
  end
186
224
 
187
- class ElasticFileSystemAlarm < Alarm
225
+ class ElasticFileSystemAlarm < BaseAlarm
188
226
  def initialize(resource)
189
227
  super(resource)
190
- @class = 'ElasticFileSystem'
228
+ @group = 'ElasticFileSystem'
191
229
  @namespace = 'AWS/EFS'
192
230
  @dimensions = { FileSystemId: resource['Id'] }
193
231
  end
194
232
  end
195
233
 
196
- class HttpAlarm < Alarm
234
+ class HttpAlarm < BaseAlarm
197
235
  def initialize(resource)
198
236
  super(resource)
199
- @class = 'Http'
237
+ @group = 'Http'
200
238
  @namespace = 'HttpCheck'
201
239
  @dimensions = { Endpoint: resource['Id'] }
202
240
  @comparison_operator = 'LessThanThreshold'
@@ -204,11 +242,51 @@ module CfnGuardian
204
242
  @evaluation_periods = 2
205
243
  end
206
244
  end
245
+
246
+ class InternalHttpAlarm < HttpAlarm
247
+ def initialize(resource)
248
+ super(resource)
249
+ end
250
+ end
251
+
252
+ class PortAlarm < BaseAlarm
253
+ def initialize(resource)
254
+ super(resource)
255
+ @group = 'Port'
256
+ @namespace = 'TcpPortCheck'
257
+ @dimensions = { Endpoint: "#{resource['Id']}:#{resource['Port']}" }
258
+ @comparison_operator = 'LessThanThreshold'
259
+ @threshold = 1
260
+ @evaluation_periods = 2
261
+ end
262
+ end
263
+
264
+ class InternalPortAlarm < PortAlarm
265
+ def initialize(resource)
266
+ super(resource)
267
+ end
268
+ end
207
269
 
208
- class NrpeAlarm < Alarm
270
+ class SslAlarm < BaseAlarm
271
+ def initialize(resource)
272
+ super(resource)
273
+ @group = 'Ssl'
274
+ @namespace = 'SSL'
275
+ @dimensions = { URL: resource['Id'] }
276
+ @comparison_operator = 'LessThanThreshold'
277
+ end
278
+ end
279
+
280
+ class InternalSslAlarm < SslAlarm
281
+ def initialize(resource)
282
+ super(resource)
283
+ end
284
+ end
285
+
286
+ class NrpeAlarm < BaseAlarm
209
287
  def initialize(resource,environment)
210
288
  super(resource)
211
- @class = 'Nrpe'
289
+ @group = 'Nrpe'
212
290
  @namespace = 'NRPE'
213
291
  @dimensions = { Host: "#{environment}-#{resource['Id']}" }
214
292
  @treat_missing_data = 'breaching'
@@ -216,10 +294,10 @@ module CfnGuardian
216
294
  end
217
295
  end
218
296
 
219
- class LambdaAlarm < Alarm
297
+ class LambdaAlarm < BaseAlarm
220
298
  def initialize(resource)
221
299
  super(resource)
222
- @class = 'Lambda'
300
+ @group = 'Lambda'
223
301
  @namespace = 'AWS/Lambda'
224
302
  @dimensions = { FunctionName: resource['Id'] }
225
303
  @statistic = 'Average'
@@ -227,10 +305,10 @@ module CfnGuardian
227
305
  end
228
306
  end
229
307
 
230
- class NetworkTargetGroupAlarm < Alarm
308
+ class NetworkTargetGroupAlarm < BaseAlarm
231
309
  def initialize(resource)
232
310
  super(resource)
233
- @class = 'NetworkTargetGroup'
311
+ @group = 'NetworkTargetGroup'
234
312
  @namespace = 'AWS/NetworkELB'
235
313
  @dimensions = {
236
314
  TargetGroup: resource['Id'],
@@ -239,37 +317,37 @@ module CfnGuardian
239
317
  end
240
318
  end
241
319
 
242
- class RedshiftClusterAlarm < Alarm
320
+ class RedshiftClusterAlarm < BaseAlarm
243
321
  def initialize(resource)
244
322
  super(resource)
245
- @class = 'RedshiftCluster'
323
+ @group = 'RedshiftCluster'
246
324
  @namespace = 'AWS/Redshift'
247
325
  @dimensions = { ClusterIdentifier: resource['Id'] }
248
326
  end
249
327
  end
250
328
 
251
- class RDSClusterInstanceAlarm < Alarm
329
+ class RDSClusterInstanceAlarm < BaseAlarm
252
330
  def initialize(resource)
253
331
  super(resource)
254
- @class = 'RDSClusterInstance'
332
+ @group = 'RDSClusterInstance'
255
333
  @namespace = 'AWS/RDS'
256
334
  @dimensions = { DBInstanceIdentifier: resource['Id'] }
257
335
  end
258
336
  end
259
337
 
260
- class RDSInstanceAlarm < Alarm
338
+ class RDSInstanceAlarm < BaseAlarm
261
339
  def initialize(resource)
262
340
  super(resource)
263
- @class = 'RDSInstance'
341
+ @group = 'RDSInstance'
264
342
  @namespace = 'AWS/RDS'
265
343
  @dimensions = { DBInstanceIdentifier: resource['Id'] }
266
344
  end
267
345
  end
268
346
 
269
- class SqlAlarm < Alarm
347
+ class SqlAlarm < BaseAlarm
270
348
  def initialize(resource)
271
349
  super(resource)
272
- @class = 'Sql'
350
+ @group = 'Sql'
273
351
  @namespace = 'SQL'
274
352
  @dimensions = { Host: resource['Id'] }
275
353
  @treat_missing_data = 'breaching'
@@ -277,10 +355,10 @@ module CfnGuardian
277
355
  end
278
356
  end
279
357
 
280
- class SQSQueueAlarm < Alarm
358
+ class SQSQueueAlarm < BaseAlarm
281
359
  def initialize(resource)
282
360
  super(resource)
283
- @class = 'SQSQueue'
361
+ @group = 'SQSQueue'
284
362
  @namespace = 'AWS/SQS'
285
363
  @dimensions = { QueueName: resource['Id'] }
286
364
  @statistic = 'Average'
@@ -288,5 +366,61 @@ module CfnGuardian
288
366
  end
289
367
  end
290
368
 
369
+ class LogGroupAlarm < BaseAlarm
370
+ def initialize(resource)
371
+ super(resource)
372
+ @group = 'LogGroup'
373
+ @namespace = "MetricFilters"
374
+ @statistic = 'Sum'
375
+ @threshold = 1
376
+ @period = 300
377
+ @alarm_action = 'Informational'
378
+ end
379
+ end
380
+
381
+ class SFTPAlarm < BaseAlarm
382
+ def initialize(resource)
383
+ super(resource)
384
+ @group = 'SFTP'
385
+ @namespace = 'SftpCheck'
386
+ @period = 300
387
+ @comparison_operator = 'LessThanThreshold'
388
+ @threshold = 1
389
+ @dimensions = { Host: resource['Id'], User: resource['User'] }
390
+ end
391
+ end
392
+
393
+ class InternalSFTPAlarm < SFTPAlarm
394
+ def initialize(resource)
395
+ super(resource)
396
+ end
397
+ end
398
+
399
+ class TLSAlarm < BaseAlarm
400
+ def initialize(resource)
401
+ super(resource)
402
+ @group = 'TLS'
403
+ @namespace = 'TLSVersionCheck'
404
+ @period = 300
405
+ @port = resource.fetch('Port', 443)
406
+ @dimensions = { Endpoint: "#{resource['Id']}:#{@port}" }
407
+ @comparison_operator = 'LessThanThreshold'
408
+ @threshold = 1
409
+ @evaluation_periods = 1
410
+ end
411
+ end
412
+
413
+ class AzureFileAlarm < BaseAlarm
414
+ def initialize(resource)
415
+ super(resource)
416
+ @group = 'AzureFile'
417
+ @namespace = 'FileAgeCheck'
418
+ @period = 300
419
+ @comparison_operator = 'GreaterThanThreshold'
420
+ @threshold = 0
421
+ @dimensions = { StorageAccount: resource['Id'], StorageContainer: resource['Container'] }
422
+ end
423
+ end
424
+
291
425
  end
292
426
  end