cfn-guardian 0.1.0 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +1 -0
- data/.github/workflows/build-gem.yml +25 -0
- data/.github/workflows/release-gem.yml +25 -0
- data/.github/workflows/release-image.yml +33 -0
- data/.rspec +1 -0
- data/Dockerfile +19 -0
- data/Gemfile.lock +39 -21
- data/README.md +9 -378
- data/cfn-guardian.gemspec +7 -5
- data/docs/alarm_templates.md +130 -0
- data/docs/cli.md +182 -0
- data/docs/composite_alarms.md +24 -0
- data/docs/custom_checks/azure_file_check.md +28 -0
- data/docs/custom_checks/domain_expiry.md +10 -0
- data/docs/custom_checks/http.md +59 -0
- data/docs/custom_checks/log_group_metric_filters.md +27 -0
- data/docs/custom_checks/nrpe.md +29 -0
- data/docs/custom_checks/port.md +40 -0
- data/docs/custom_checks/sftp.md +73 -0
- data/docs/custom_checks/sql.md +44 -0
- data/docs/custom_checks/tls.md +25 -0
- data/docs/custom_metrics.md +71 -0
- data/docs/event_subscriptions.md +67 -0
- data/docs/maintenance_mode.md +85 -0
- data/docs/notifiers.md +33 -0
- data/docs/overview.md +22 -0
- data/docs/resources.md +93 -0
- data/docs/variables.md +58 -0
- data/lib/cfnguardian.rb +325 -37
- data/lib/cfnguardian/cloudwatch.rb +132 -0
- data/lib/cfnguardian/codecommit.rb +54 -0
- data/lib/cfnguardian/codepipeline.rb +138 -0
- data/lib/cfnguardian/compile.rb +142 -18
- data/lib/cfnguardian/config/defaults.yaml +103 -0
- data/lib/cfnguardian/deploy.rb +2 -16
- data/lib/cfnguardian/display_formatter.rb +163 -0
- data/lib/cfnguardian/drift.rb +79 -0
- data/lib/cfnguardian/error.rb +4 -0
- data/lib/cfnguardian/log.rb +0 -1
- data/lib/cfnguardian/models/alarm.rb +193 -59
- data/lib/cfnguardian/models/check.rb +128 -33
- data/lib/cfnguardian/models/composite.rb +21 -0
- data/lib/cfnguardian/models/event.rb +201 -49
- data/lib/cfnguardian/models/event_subscription.rb +96 -0
- data/lib/cfnguardian/models/metric_filter.rb +28 -0
- data/lib/cfnguardian/resources/amazonmq_rabbitmq.rb +136 -0
- data/lib/cfnguardian/resources/application_targetgroup.rb +2 -0
- data/lib/cfnguardian/resources/azure_file.rb +20 -0
- data/lib/cfnguardian/resources/base.rb +155 -33
- data/lib/cfnguardian/resources/ec2_instance.rb +11 -0
- data/lib/cfnguardian/resources/ecs_service.rb +2 -2
- data/lib/cfnguardian/resources/http.rb +17 -1
- data/lib/cfnguardian/resources/internal_http.rb +74 -0
- data/lib/cfnguardian/resources/internal_port.rb +33 -0
- data/lib/cfnguardian/resources/internal_sftp.rb +58 -0
- data/lib/cfnguardian/resources/log_group.rb +26 -0
- data/lib/cfnguardian/resources/network_targetgroup.rb +1 -0
- data/lib/cfnguardian/resources/port.rb +25 -0
- data/lib/cfnguardian/resources/rds_cluster.rb +14 -0
- data/lib/cfnguardian/resources/rds_instance.rb +73 -0
- data/lib/cfnguardian/resources/redshift_cluster.rb +2 -2
- data/lib/cfnguardian/resources/sftp.rb +50 -0
- data/lib/cfnguardian/resources/sql.rb +3 -3
- data/lib/cfnguardian/resources/tls.rb +66 -0
- data/lib/cfnguardian/s3.rb +3 -2
- data/lib/cfnguardian/stacks/main.rb +94 -72
- data/lib/cfnguardian/stacks/resources.rb +111 -43
- data/lib/cfnguardian/string.rb +12 -0
- data/lib/cfnguardian/version.rb +1 -1
- metadata +133 -10
data/docs/notifiers.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Guardian Notifiers
|
2
|
+
|
3
|
+
## SNS Notification
|
4
|
+
|
5
|
+
There are 4 default notification levels used by Guardian Critical, Warning, Task, Informational. If you wish to recieve notifications for each of these you need to supply an sns topic arn in the alarms.yaml
|
6
|
+
|
7
|
+
```yaml
|
8
|
+
Topics:
|
9
|
+
Critical: arn:aws:sns:ap-southeast-2:123456789012:Critical
|
10
|
+
Warning: arn:aws:sns:ap-southeast-2:123456789012:Warning
|
11
|
+
Task: arn:aws:sns:ap-southeast-2:123456789012:Task
|
12
|
+
Informational: arn:aws:sns:ap-southeast-2:123456789012:Informational
|
13
|
+
```
|
14
|
+
|
15
|
+
Each alarm has a default notification level but can be overriden in the config using the `AlarmAction` property at either the alarm group or alarm level. See the [Overriding Defaults](#overriding-defaults) section on how to do that.
|
16
|
+
|
17
|
+
You can add your own notification topics to the topics section and combine them with the existing topics. `AlarmAction` property will accept both a string and array of notication topics.
|
18
|
+
|
19
|
+
```yaml
|
20
|
+
Topics:
|
21
|
+
Critical: arn:aws:sns:ap-southeast-2:123456789012:Critical
|
22
|
+
Warning: arn:aws:sns:ap-southeast-2:123456789012:Warning
|
23
|
+
Task: arn:aws:sns:ap-southeast-2:123456789012:Task
|
24
|
+
Informational: arn:aws:sns:ap-southeast-2:123456789012:Informational
|
25
|
+
Custom: arn:aws:sns:ap-southeast-2:123456789012:Custom
|
26
|
+
|
27
|
+
Template:
|
28
|
+
Ec2Instance:
|
29
|
+
GroupOverrides:
|
30
|
+
AlarmActions:
|
31
|
+
- Critical
|
32
|
+
- Custom
|
33
|
+
```
|
data/docs/overview.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Guardian Documentation
|
2
|
+
|
3
|
+
## Table of Contents
|
4
|
+
1. [CLI Commands](cli.md)
|
5
|
+
2. [Resources](resources.md)
|
6
|
+
3. [Alarm Templates](alarm_templates.md)
|
7
|
+
4. Custom Checks
|
8
|
+
1. [HTTP](custom_checks/http.md)
|
9
|
+
2. [Domain Expirey](custom_checks/domain_expirey.md)
|
10
|
+
3. [LogGroup Metric Filters](custom_checks/log_group_metric_filters.md)
|
11
|
+
4. [NRPE](custom_checks/nrpe.md)
|
12
|
+
5. [Port](custom_checks/port.md)
|
13
|
+
6. [SFTP](custom_checks/sftp.md)
|
14
|
+
7. [SQL](custom_checks/sql.md)
|
15
|
+
8. [TLS](custom_checks/tls.md)
|
16
|
+
9. [Azure File Check](custom_checks/azure_file_check.md)
|
17
|
+
5. [Event Subscriptions](event_subscriptions.md)
|
18
|
+
6. [Notifiers](notifiers.md)
|
19
|
+
7. [Maintenance Mode](maintenance_mode.md)
|
20
|
+
8. [Composite Alarms](composite_alarms.md)
|
21
|
+
9. [Alarms for Custom Metrics](custom_metrics.md)
|
22
|
+
10. [Dimension Variables](variables.md)
|
data/docs/resources.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# Resources
|
2
|
+
|
3
|
+
Resources are AWS resources grouped by the resource type such as `RDSInstance`, `Ec2Instance`, `ApplicationTargetGroup` etc. These are defined under the top level key `Resources` in the yaml config file. The resource group is then used to generate standard set of alarms.
|
4
|
+
|
5
|
+
Custom resource groups can be created however matching alarm templates must be created to create alarms.
|
6
|
+
|
7
|
+
## Resource lookup
|
8
|
+
|
9
|
+
Resources can be looked up within an account using the tool [monitorable](https://github.com/base2Services/monitorable). This tool will scan every region within an account for AWS resources that can be monitored and return a valid Guardian yaml config using the `--format cfn-guardian` flag.
|
10
|
+
|
11
|
+
```sh
|
12
|
+
./monitorable.py --format cfn-guardian
|
13
|
+
```
|
14
|
+
|
15
|
+
## YAML Configuration
|
16
|
+
|
17
|
+
The resources key is where the resources are defined.
|
18
|
+
|
19
|
+
```yaml
|
20
|
+
Resources:
|
21
|
+
# resource group
|
22
|
+
Ec2Instance:
|
23
|
+
# Array of resources defining the resource id with the Id: key
|
24
|
+
- Id: i-1a2b3c4d5e
|
25
|
+
```
|
26
|
+
|
27
|
+
There are some resources that require more that the resource id to generate the alarm, for these cases addition key:values are required.
|
28
|
+
|
29
|
+
```yaml
|
30
|
+
Resources:
|
31
|
+
ApplicationTargetGroup:
|
32
|
+
- Id: target-group-id
|
33
|
+
# Target group requires the loadbalancer id for the alarm
|
34
|
+
Loadbalancer: app/application-loadbalancer-id
|
35
|
+
```
|
36
|
+
|
37
|
+
| Resource Group | Require Keys |
|
38
|
+
| --------------------------- | ---------------- |
|
39
|
+
| ApiGateway | Id |
|
40
|
+
| AmazonMQBroker | Id |
|
41
|
+
| AutoScalingGroup | Id |
|
42
|
+
| DynamoDBTable | Id |
|
43
|
+
| ElastiCacheReplicationGroup | Id |
|
44
|
+
| ElasticFileSystem | Id |
|
45
|
+
| Ec2Instance | Id |
|
46
|
+
| EcsCluster | Id |
|
47
|
+
| EcsService | Id, Cluster |
|
48
|
+
| NetworkTargetGroup | Id, LoadBalancer |
|
49
|
+
| ApplicationTargetGroup | Id, LoadBalancer |
|
50
|
+
| ElasticLoadBalancer | Id |
|
51
|
+
| RDSInstance | Id |
|
52
|
+
| RDSClusterInstance | Id |
|
53
|
+
| RedshiftCluster | Id |
|
54
|
+
| Lambda | Id |
|
55
|
+
| CloudFrontDistribution | Id |
|
56
|
+
| SQSQueue | Id |
|
57
|
+
|
58
|
+
|
59
|
+
## Custom Resource Groups
|
60
|
+
|
61
|
+
You may want to create a custom resource group if some of the resources require differewnt alarm configurations. To create a custom resource group create new name for the group and add the resources, then create the alarm template and inherit the desired alarms.
|
62
|
+
|
63
|
+
```yaml
|
64
|
+
Resources:
|
65
|
+
# default resource group
|
66
|
+
Ec2Instance:
|
67
|
+
- Id: i-1a2b3c4d5e
|
68
|
+
- Id: i-9z8y7x6w5v
|
69
|
+
# custom resource group
|
70
|
+
CustomEc2Instance:
|
71
|
+
- Id: i-6fefg5qe4e
|
72
|
+
|
73
|
+
Templates:
|
74
|
+
# create a new alarm template with the same group name
|
75
|
+
CustomEc2Instance:
|
76
|
+
# inherit the ec2 alarms
|
77
|
+
Inherit: Ec2Instance
|
78
|
+
# alter the alarms
|
79
|
+
CPUUtilizationHigh: false
|
80
|
+
```
|
81
|
+
|
82
|
+
## Friendly Resource Names
|
83
|
+
|
84
|
+
You can set a friendly name which will replace the resource id in the alarm name.
|
85
|
+
The resource id will still be available in the alarm description.
|
86
|
+
|
87
|
+
```yaml
|
88
|
+
Resources:
|
89
|
+
ApplicationTargetGroup:
|
90
|
+
- Id: target-group-id
|
91
|
+
Loadbalancer: app/application-loadbalancer-id
|
92
|
+
Name: webapp
|
93
|
+
```
|
data/docs/variables.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
## Dimension Variables
|
2
|
+
|
3
|
+
variables can be used to reference resource group values such as the resource Id within the dimensions section of an alarm template.
|
4
|
+
|
5
|
+
For example here we are creating an alarm for a disk usage metric for a group of EC2 instances.
|
6
|
+
|
7
|
+
```yaml
|
8
|
+
Templates:
|
9
|
+
Ec2Instance:
|
10
|
+
LowDiskSpaceRootVolume:
|
11
|
+
Namespace: CWAgent
|
12
|
+
MetricName: DiskSpaceUsedPercent
|
13
|
+
Dimensions:
|
14
|
+
path: '/'
|
15
|
+
# Reference the resource Id from the resource group
|
16
|
+
host: ${Resource::Id}
|
17
|
+
device: 'xvda1'
|
18
|
+
fstype: 'ext4'
|
19
|
+
Statistic: Maximum
|
20
|
+
Threshold: 85
|
21
|
+
Period: 60
|
22
|
+
EvaluationPeriods: 1
|
23
|
+
TreatMissingData: breaching
|
24
|
+
|
25
|
+
Resources:
|
26
|
+
Ec2Instance:
|
27
|
+
- Id: i-12345678
|
28
|
+
- Id: i-abcdefgh
|
29
|
+
```
|
30
|
+
|
31
|
+
custom variables can be referenced if you have different dimensions for each resource. using the example above, you may have different file system types on each instance.
|
32
|
+
|
33
|
+
```yaml
|
34
|
+
Templates:
|
35
|
+
Ec2Instance:
|
36
|
+
LowDiskSpaceRootVolume:
|
37
|
+
Namespace: CWAgent
|
38
|
+
MetricName: DiskSpaceUsedPercent
|
39
|
+
Dimensions:
|
40
|
+
path: '/'
|
41
|
+
# Reference the resource Id from the resource group
|
42
|
+
host: ${Resource::Id}
|
43
|
+
device: 'xvda1'
|
44
|
+
# Reference the resource FileSystemType from the resource group
|
45
|
+
fstype: ${Resource::FileSystemType}
|
46
|
+
Statistic: Maximum
|
47
|
+
Threshold: 85
|
48
|
+
Period: 60
|
49
|
+
EvaluationPeriods: 1
|
50
|
+
TreatMissingData: breaching
|
51
|
+
|
52
|
+
Resources:
|
53
|
+
Ec2Instance:
|
54
|
+
- Id: i-12345678
|
55
|
+
FileSystemType: ext4
|
56
|
+
- Id: i-abcdefgh
|
57
|
+
FileSystemType: ext4
|
58
|
+
```
|
data/lib/cfnguardian.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'terminal-table'
|
3
|
+
require 'term/ansicolor'
|
3
4
|
require "cfnguardian/log"
|
4
5
|
require "cfnguardian/version"
|
5
6
|
require "cfnguardian/compile"
|
6
7
|
require "cfnguardian/validate"
|
7
8
|
require "cfnguardian/deploy"
|
9
|
+
require "cfnguardian/cloudwatch"
|
10
|
+
require "cfnguardian/display_formatter"
|
11
|
+
require "cfnguardian/drift"
|
12
|
+
require "cfnguardian/codecommit"
|
13
|
+
require "cfnguardian/codepipeline"
|
8
14
|
|
9
15
|
module CfnGuardian
|
10
16
|
class Cli < Thor
|
@@ -16,6 +22,8 @@ module CfnGuardian
|
|
16
22
|
puts CfnGuardian::VERSION
|
17
23
|
end
|
18
24
|
|
25
|
+
class_option :debug, type: :boolean, default: false, desc: "enable debug logging"
|
26
|
+
|
19
27
|
desc "compile", "Generate monitoring CloudFormation templates"
|
20
28
|
long_desc <<-LONG
|
21
29
|
Generates CloudFormation templates from the alarm configuration and output to the out/ directory.
|
@@ -23,16 +31,26 @@ module CfnGuardian
|
|
23
31
|
method_option :config, aliases: :c, type: :string, desc: "yaml config file", required: true
|
24
32
|
method_option :validate, type: :boolean, default: true, desc: "validate cfn templates"
|
25
33
|
method_option :bucket, type: :string, desc: "provide custom bucket name, will create a default bucket if not provided"
|
34
|
+
method_option :path, type: :string, default: "guardian", desc: "S3 path location for nested stacks"
|
26
35
|
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
36
|
+
method_option :template_config, type: :boolean, default: false, desc: "Genrates an AWS CodePipeline cloudformation template configuration file to override parameters"
|
37
|
+
method_option :sns_critical, type: :string, desc: "sns topic arn for the critical alarms"
|
38
|
+
method_option :sns_warning, type: :string, desc: "sns topic arn for the warning alarms"
|
39
|
+
method_option :sns_task, type: :string, desc: "sns topic arn for the task alarms"
|
40
|
+
method_option :sns_informational, type: :string, desc: "sns topic arn for the informational alarms"
|
41
|
+
method_option :sns_events, type: :string, desc: "sns topic arn for the informational alarms"
|
42
|
+
|
27
43
|
|
28
44
|
def compile
|
45
|
+
set_log_level(options[:debug])
|
46
|
+
|
29
47
|
set_region(options[:region],options[:validate])
|
30
|
-
s3 = CfnGuardian::S3.new(options[:bucket])
|
48
|
+
s3 = CfnGuardian::S3.new(options[:bucket],options[:path])
|
31
49
|
|
32
|
-
compiler = CfnGuardian::Compile.new(options
|
50
|
+
compiler = CfnGuardian::Compile.new(options[:config])
|
33
51
|
compiler.get_resources
|
34
|
-
compiler.compile_templates
|
35
|
-
logger.info "
|
52
|
+
compiler.compile_templates(s3.bucket,s3.path)
|
53
|
+
logger.info "Cloudformation templates compiled successfully in out/ directory"
|
36
54
|
if options[:validate]
|
37
55
|
s3.create_bucket_if_not_exists()
|
38
56
|
validator = CfnGuardian::Validate.new(s3.bucket)
|
@@ -40,10 +58,16 @@ module CfnGuardian
|
|
40
58
|
logger.error("One or more templates failed to validate")
|
41
59
|
exit(1)
|
42
60
|
else
|
43
|
-
logger.info "
|
61
|
+
logger.info "Cloudformation templates were validated successfully"
|
44
62
|
end
|
45
63
|
end
|
46
64
|
logger.warn "AWS cloudwatch alarms defined in the templates will cost roughly $#{'%.2f' % compiler.cost} per month"
|
65
|
+
|
66
|
+
if options[:template_config]
|
67
|
+
logger.info "Generating a AWS CodePipeline template configuration file template-config.guardian.json"
|
68
|
+
parameters = compiler.load_parameters(options)
|
69
|
+
compiler.genrate_template_config(parameters)
|
70
|
+
end
|
47
71
|
end
|
48
72
|
|
49
73
|
desc "deploy", "Generates and deploys monitoring CloudFormation templates"
|
@@ -53,21 +77,26 @@ module CfnGuardian
|
|
53
77
|
LONG
|
54
78
|
method_option :config, aliases: :c, type: :string, desc: "yaml config file", required: true
|
55
79
|
method_option :bucket, type: :string, desc: "provide custom bucket name, will create a default bucket if not provided"
|
80
|
+
method_option :path, type: :string, default: "guardian", desc: "S3 path location for nested stacks"
|
56
81
|
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
57
|
-
method_option :stack_name, aliases: :
|
58
|
-
method_option :sns_critical, type: :string, desc: "sns topic arn for the critical
|
59
|
-
method_option :sns_warning, type: :string, desc: "sns topic arn for the warning
|
60
|
-
method_option :sns_task, type: :string, desc: "sns topic arn for the task
|
61
|
-
method_option :sns_informational, type: :string, desc: "sns topic arn for the informational
|
82
|
+
method_option :stack_name, aliases: :s, type: :string, desc: "set the Cloudformation stack name. Defaults to `guardian`"
|
83
|
+
method_option :sns_critical, type: :string, desc: "sns topic arn for the critical alarms"
|
84
|
+
method_option :sns_warning, type: :string, desc: "sns topic arn for the warning alarms"
|
85
|
+
method_option :sns_task, type: :string, desc: "sns topic arn for the task alarms"
|
86
|
+
method_option :sns_informational, type: :string, desc: "sns topic arn for the informational alarms"
|
87
|
+
method_option :sns_events, type: :string, desc: "sns topic arn for the informational alarms"
|
62
88
|
|
63
89
|
def deploy
|
90
|
+
set_log_level(options[:debug])
|
91
|
+
|
64
92
|
set_region(options[:region],true)
|
65
|
-
s3 = CfnGuardian::S3.new(options[:bucket])
|
93
|
+
s3 = CfnGuardian::S3.new(options[:bucket],options[:path])
|
66
94
|
|
67
|
-
compiler = CfnGuardian::Compile.new(options
|
95
|
+
compiler = CfnGuardian::Compile.new(options[:config])
|
68
96
|
compiler.get_resources
|
69
|
-
compiler.compile_templates
|
70
|
-
|
97
|
+
compiler.compile_templates(s3.bucket,s3.path)
|
98
|
+
parameters = compiler.load_parameters(options)
|
99
|
+
logger.info "Cloudformation templates compiled successfully in out/ directory"
|
71
100
|
|
72
101
|
s3.create_bucket_if_not_exists
|
73
102
|
validator = CfnGuardian::Validate.new(s3.bucket)
|
@@ -75,56 +104,289 @@ module CfnGuardian
|
|
75
104
|
logger.error("One or more templates failed to validate")
|
76
105
|
exit(1)
|
77
106
|
else
|
78
|
-
logger.info "
|
107
|
+
logger.info "Cloudformation templates were validated successfully"
|
79
108
|
end
|
80
109
|
|
81
|
-
deployer = CfnGuardian::Deploy.new(options,s3.bucket)
|
110
|
+
deployer = CfnGuardian::Deploy.new(options,s3.bucket,parameters)
|
82
111
|
deployer.upload_templates
|
83
112
|
change_set, change_set_type = deployer.create_change_set()
|
84
113
|
deployer.wait_for_changeset(change_set.id)
|
85
114
|
deployer.execute_change_set(change_set.id)
|
86
115
|
deployer.wait_for_execute(change_set_type)
|
87
116
|
end
|
117
|
+
|
118
|
+
desc "show-drift", "Cloudformation drift detection"
|
119
|
+
long_desc <<-LONG
|
120
|
+
Displays any cloudformation drift detection in the cloudwatch alarms from the deployed stacks
|
121
|
+
LONG
|
122
|
+
method_option :stack_name, aliases: :s, type: :string, default: 'guardian', desc: "set the Cloudformation stack name"
|
123
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
124
|
+
|
125
|
+
def show_drift
|
126
|
+
set_region(options[:region],true)
|
127
|
+
|
128
|
+
rows = []
|
129
|
+
drift = CfnGuardian::Drift.new(options[:stack_name])
|
130
|
+
nested_stacks = drift.find_nested_stacks
|
131
|
+
nested_stacks.each do |stack|
|
132
|
+
drift.detect_drift(stack)
|
133
|
+
rows << drift.get_drift(stack)
|
134
|
+
end
|
135
|
+
|
136
|
+
if rows.any?
|
137
|
+
puts Terminal::Table.new(
|
138
|
+
:title => "Guardian Alarm Drift".green,
|
139
|
+
:headings => ['Alarm Name', 'Property', 'Expected', 'Actual', 'Type'],
|
140
|
+
:rows => rows.flatten(1))
|
141
|
+
exit(1)
|
142
|
+
end
|
143
|
+
end
|
88
144
|
|
89
145
|
desc "show-alarms", "Shows alarm settings"
|
90
146
|
long_desc <<-LONG
|
91
147
|
Displays the configured settings for each alarm. Can be filtered by resource group and alarm name.
|
92
148
|
Defaults to show all configured alarms.
|
93
149
|
LONG
|
94
|
-
method_option :config, aliases: :c, type: :string, desc: "yaml config file"
|
95
|
-
method_option :
|
96
|
-
method_option :
|
97
|
-
method_option :
|
150
|
+
method_option :config, aliases: :c, type: :string, desc: "yaml config file"
|
151
|
+
method_option :defaults, type: :boolean, desc: "display default alarms and properties"
|
152
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
153
|
+
method_option :filter, type: :hash, default: {}, desc: "filter the displayed alarms by [group, resource-id, alarm, stack-id, topic, maintenance-group]"
|
154
|
+
method_option :compare, type: :boolean, default: false, desc: "compare config to deployed alarms"
|
155
|
+
|
98
156
|
def show_alarms
|
99
|
-
|
157
|
+
set_log_level(options[:debug])
|
158
|
+
set_region(options[:region],options[:compare])
|
159
|
+
|
160
|
+
if options[:config]
|
161
|
+
config_file = options[:config]
|
162
|
+
elsif options[:defaults]
|
163
|
+
config_file = default_config()
|
164
|
+
else
|
165
|
+
logger.error('one of `--config YAML` or `--defaults` must be supplied')
|
166
|
+
exit -1
|
167
|
+
end
|
168
|
+
|
169
|
+
compiler = CfnGuardian::Compile.new(config_file)
|
100
170
|
compiler.get_resources
|
171
|
+
alarms = filter_compiled_alarms(compiler.alarms,options[:filter])
|
172
|
+
|
173
|
+
if alarms.empty?
|
174
|
+
logger.error "No matches found"
|
175
|
+
exit 1
|
176
|
+
end
|
101
177
|
|
102
|
-
|
103
|
-
|
178
|
+
headings = ['Property', 'Config']
|
179
|
+
formatter = CfnGuardian::DisplayFormatter.new(alarms)
|
104
180
|
|
105
|
-
if options[:
|
106
|
-
|
107
|
-
|
108
|
-
|
181
|
+
if options[:compare] && !options[:defaults]
|
182
|
+
metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_prefix(prefix: 'guardian')
|
183
|
+
metric_alarms = CfnGuardian::CloudWatch.filter_alarms(filters: options[:filter], alarms: metric_alarms)
|
184
|
+
|
185
|
+
formatted_alarms = formatter.compare_alarms(metric_alarms)
|
186
|
+
headings.push('Deployed')
|
187
|
+
else
|
188
|
+
formatted_alarms = formatter.alarms()
|
189
|
+
end
|
190
|
+
|
191
|
+
if formatted_alarms.any?
|
192
|
+
formatted_alarms.each do |fa|
|
193
|
+
puts Terminal::Table.new(
|
194
|
+
:title => fa[:title],
|
195
|
+
:headings => headings,
|
196
|
+
:rows => fa[:rows])
|
109
197
|
end
|
110
|
-
|
111
|
-
|
198
|
+
else
|
199
|
+
if options[:compare] && !options[:defaults]
|
200
|
+
logger.info "No difference found between you config and alarms in deployed AWS"
|
201
|
+
else
|
202
|
+
logger.warn "No alarms found"
|
112
203
|
end
|
113
204
|
end
|
205
|
+
end
|
206
|
+
|
207
|
+
desc "show-state", "Shows alarm state in cloudwatch"
|
208
|
+
long_desc <<-LONG
|
209
|
+
Displays the current cloudwatch alarm state. By default it will return all the guardian alarms.
|
210
|
+
LONG
|
211
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
212
|
+
method_option :state, aliases: :s, type: :string, enum: %w(OK ALARM INSUFFICIENT_DATA), desc: "filter by alarm state"
|
213
|
+
method_option :alarm_names, type: :array, desc: "list of cloudwatch alarm names"
|
214
|
+
method_option :alarm_prefix, type: :string, default: "guardian", desc: "cloudwatch alarm name prefix"
|
215
|
+
method_option :filter, type: :hash, default: {}, desc: "filter the displayed alarms by [group, resource-id, alarm, stack-id, topic, maintenance-group]"
|
216
|
+
|
217
|
+
def show_state
|
218
|
+
set_log_level(options[:debug])
|
219
|
+
set_region(options[:region],true)
|
220
|
+
action_prefix = nil
|
221
|
+
|
222
|
+
if options[:filter].has_key?('topic')
|
223
|
+
action_prefix = get_topic_arn_from_stack(options[:filter]['topic'])
|
224
|
+
elsif options[:filter].has_key?('maintenance-group')
|
225
|
+
action_prefix = "arn:aws:sns:#{Aws.config[:region]}:#{CfnGuardian::CloudWatch.aws_account_id()}:#{options[:filter]['maintenance-group']}MaintenanceGroup"
|
226
|
+
end
|
227
|
+
|
228
|
+
if options[:alarm_names]
|
229
|
+
metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_name(alarm_names: options[:alarm_names], state: options[:state], action_prefix: action_prefix)
|
230
|
+
else
|
231
|
+
metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_prefix(prefix: options[:alarm_prefix], state: options[:state], action_prefix: action_prefix)
|
232
|
+
end
|
233
|
+
|
234
|
+
metric_alarms = CfnGuardian::CloudWatch.filter_alarms(filters: options[:filter], alarms: metric_alarms)
|
235
|
+
|
236
|
+
formatter = CfnGuardian::DisplayFormatter.new()
|
237
|
+
rows = formatter.alarm_state(metric_alarms)
|
114
238
|
|
115
|
-
|
116
|
-
puts
|
117
|
-
|
118
|
-
|
119
|
-
|
239
|
+
if rows.any?
|
240
|
+
puts Terminal::Table.new(
|
241
|
+
:title => "Alarm State",
|
242
|
+
:headings => ['Alarm Name', 'State', 'Changed', 'Notifications'],
|
243
|
+
:rows => rows)
|
244
|
+
else
|
245
|
+
logger.warn "No alarms found"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
desc "show-history", "Shows alarm history for the last 7 days"
|
250
|
+
long_desc <<-LONG
|
251
|
+
Displays the alarm state or config history for the last 7 days
|
252
|
+
LONG
|
253
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
254
|
+
method_option :alarm_names, type: :array, desc: "list of cloudwatch alarm names"
|
255
|
+
method_option :type, aliases: :t, type: :string,
|
256
|
+
enum: %w(state config), default: 'state', desc: "filter by alarm state"
|
257
|
+
method_option :alarm_prefix, type: :string, default: "guardian", desc: "cloudwatch alarm name prefix"
|
258
|
+
method_option :filter, type: :hash, desc: "filter the displayed alarms by [group, resource-id, alarm, stack-id]"
|
259
|
+
|
260
|
+
def show_history
|
261
|
+
set_log_level(options[:debug])
|
262
|
+
set_region(options[:region],true)
|
263
|
+
|
264
|
+
if options[:alarm_names]
|
265
|
+
metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_name(alarm_names: options[:alarm_names], state: options[:state])
|
266
|
+
else
|
267
|
+
metric_alarms = CfnGuardian::CloudWatch.get_alarms_by_prefix(prefix: options[:alarm_prefix], state: options[:state])
|
268
|
+
end
|
269
|
+
|
270
|
+
metric_alarms = CfnGuardian::CloudWatch.filter_alarms(filters: options[:filter], alarms: metric_alarms)
|
271
|
+
|
272
|
+
case options[:type]
|
273
|
+
when 'state'
|
274
|
+
type = 'StateUpdate'
|
275
|
+
headings = ['Date', 'Summary', 'Reason']
|
276
|
+
when 'config'
|
277
|
+
type = 'ConfigurationUpdate'
|
278
|
+
headings = ['Date', 'Summary', 'Type']
|
279
|
+
end
|
280
|
+
|
281
|
+
formatter = CfnGuardian::DisplayFormatter.new()
|
282
|
+
|
283
|
+
metric_alarms.each do |alarm|
|
284
|
+
history = CfnGuardian::CloudWatch.get_alarm_history(alarm.alarm_name,type)
|
285
|
+
rows = formatter.alarm_history(history,type)
|
286
|
+
if rows.any?
|
120
287
|
puts Terminal::Table.new(
|
121
|
-
:title => alarm
|
122
|
-
:headings =>
|
123
|
-
:rows => rows
|
288
|
+
:title => alarm.alarm_name.green,
|
289
|
+
:headings => headings,
|
290
|
+
:rows => rows)
|
291
|
+
puts "\n"
|
124
292
|
end
|
125
293
|
end
|
126
294
|
end
|
127
295
|
|
296
|
+
desc "show-config-history", "Shows the last 10 commits made to the codecommit repo"
|
297
|
+
long_desc <<-LONG
|
298
|
+
Shows the last 10 commits made to the codecommit repo
|
299
|
+
LONG
|
300
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
301
|
+
method_option :repository, type: :string, default: 'guardian', desc: "codecommit repository name"
|
302
|
+
|
303
|
+
def show_config_history
|
304
|
+
set_region(options[:region],true)
|
305
|
+
|
306
|
+
history = CfnGuardian::CodeCommit.new(options[:repository]).get_commit_history()
|
307
|
+
puts Terminal::Table.new(
|
308
|
+
:headings => history.first.keys.map{|h| h.to_s.to_heading},
|
309
|
+
:rows => history.map(&:values))
|
310
|
+
end
|
311
|
+
|
312
|
+
desc "show-pipeline", "Shows the current state of the AWS code pipeline"
|
313
|
+
long_desc <<-LONG
|
314
|
+
Shows the current state of the AWS code pipeline
|
315
|
+
LONG
|
316
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
317
|
+
method_option :pipeline, aliases: :p, type: :string, default: 'guardian', desc: "codepipeline name"
|
318
|
+
|
319
|
+
def show_pipeline
|
320
|
+
set_region(options[:region],true)
|
321
|
+
pipeline = CfnGuardian::CodePipeline.new(options[:pipeline])
|
322
|
+
source = pipeline.get_source()
|
323
|
+
build = pipeline.get_build()
|
324
|
+
create = pipeline.get_create_changeset()
|
325
|
+
deploy = pipeline.get_deploy_changeset()
|
326
|
+
|
327
|
+
puts Terminal::Table.new(
|
328
|
+
:title => "Stage: #{source[:stage]}",
|
329
|
+
:rows => source[:rows])
|
330
|
+
|
331
|
+
puts "\t|"
|
332
|
+
puts "\t|"
|
333
|
+
|
334
|
+
puts Terminal::Table.new(
|
335
|
+
:title => "Stage: #{build[:stage]}",
|
336
|
+
:rows => build[:rows])
|
337
|
+
|
338
|
+
puts "\t|"
|
339
|
+
puts "\t|"
|
340
|
+
|
341
|
+
puts Terminal::Table.new(
|
342
|
+
:title => "Stage: #{create[:stage]}",
|
343
|
+
:rows => create[:rows])
|
344
|
+
|
345
|
+
puts "\t|"
|
346
|
+
puts "\t|"
|
347
|
+
|
348
|
+
puts Terminal::Table.new(
|
349
|
+
:title => "Stage: #{deploy[:stage]}",
|
350
|
+
:rows => deploy[:rows])
|
351
|
+
end
|
352
|
+
|
353
|
+
desc "disable-alarms", "Disable cloudwatch alarm notifications"
|
354
|
+
long_desc <<-LONG
|
355
|
+
Disable cloudwatch alarm notifications for a maintenance group or for specific alarms.
|
356
|
+
LONG
|
357
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
358
|
+
method_option :group, aliases: :g, type: :string, desc: "name of the maintenance group defined in the config"
|
359
|
+
method_option :alarm_prefix, type: :string, desc: "cloud watch alarm name prefix"
|
360
|
+
method_option :alarms, type: :array, desc: "List of cloudwatch alarm names"
|
361
|
+
|
362
|
+
def disable_alarms
|
363
|
+
set_region(options[:region],true)
|
364
|
+
|
365
|
+
alarm_names = CfnGuardian::CloudWatch.get_alarm_names(options[:group],options[:alarm_prefix])
|
366
|
+
CfnGuardian::CloudWatch.disable_alarms(alarm_names)
|
367
|
+
|
368
|
+
logger.info "Disabled #{alarm_names.length} alarms"
|
369
|
+
end
|
370
|
+
|
371
|
+
desc "enable-alarms", "Enable cloudwatch alarm notifications"
|
372
|
+
long_desc <<-LONG
|
373
|
+
Enable cloudwatch alarm notifications for a maintenance group or for specific alarms.
|
374
|
+
Once alarms are enable the state is set back to OK to re send notifications of any failed alarms.
|
375
|
+
LONG
|
376
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
377
|
+
method_option :group, aliases: :g, type: :string, desc: "name of the maintenance group defined in the config"
|
378
|
+
method_option :alarm_prefix, type: :string, desc: "cloud watch alarm name prefix"
|
379
|
+
method_option :alarms, type: :array, desc: "List of cloudwatch alarm names"
|
380
|
+
|
381
|
+
def enable_alarms
|
382
|
+
set_region(options[:region],true)
|
383
|
+
|
384
|
+
alarm_names = CfnGuardian::CloudWatch.get_alarm_names(options[:group],options[:alarm_prefix])
|
385
|
+
CfnGuardian::CloudWatch.enable_alarms(alarm_names)
|
386
|
+
|
387
|
+
logger.info "#{alarm_names.length} alarms enabled"
|
388
|
+
end
|
389
|
+
|
128
390
|
private
|
129
391
|
|
130
392
|
def set_region(region,required)
|
@@ -142,5 +404,31 @@ module CfnGuardian
|
|
142
404
|
end
|
143
405
|
end
|
144
406
|
|
407
|
+
def set_log_level(debug)
|
408
|
+
logger.level = debug ? Logger::DEBUG : Logger::INFO
|
409
|
+
end
|
410
|
+
|
411
|
+
def filter_compiled_alarms(alarms,filters)
|
412
|
+
filters = filters.slice('group', 'resource', 'alarm', 'topic', 'maintenance-group')
|
413
|
+
alarms.select! {|alarm| alarm.group.downcase == filters['group'].downcase} if filters.has_key?('group')
|
414
|
+
alarms.select! {|alarm| alarm.resource_id.downcase == filters['resource'].downcase} if filters.has_key?('resource')
|
415
|
+
alarms.select! {|alarm| alarm.name.downcase.include? filters['alarm'].downcase} if filters.has_key?('alarm')
|
416
|
+
alarms.select! {|alarm| alarm.alarm_action.include? filters['topic']} if filters.has_key?('topic')
|
417
|
+
alarms.select! {|alarm| alarm.maintenance_groups.include? "#{filters['maintenance-group']}MaintenanceGroup"} if filters.has_key?('maintenance-group')
|
418
|
+
return alarms
|
419
|
+
end
|
420
|
+
|
421
|
+
def default_config()
|
422
|
+
return "#{File.expand_path(File.dirname(__FILE__))}/cfnguardian/config/defaults.yaml"
|
423
|
+
end
|
424
|
+
|
425
|
+
def get_topic_arn_from_stack(topic)
|
426
|
+
client = Aws::CloudFormation::Client.new()
|
427
|
+
resp = client.describe_stacks({ stack_name: @stack_name })
|
428
|
+
stack = resp.stacks.first
|
429
|
+
parameter = stack.parameters.find {|p| p.parameter_key == topic}
|
430
|
+
return !parameter.nil? ? parameter.parameter_value : nil
|
431
|
+
end
|
432
|
+
|
145
433
|
end
|
146
434
|
end
|