Skip to content

Commit

Permalink
Merge pull request #1 from hazelops/initial-branch
Browse files Browse the repository at this point in the history
added the source code
  • Loading branch information
igorkotof authored Oct 1, 2020
2 parents 4f46858 + 676ac4a commit 1a60eda
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 0 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,32 @@
# terraform-aws-route53-healthcheck
Terraform AWS Route53 Healthcheck Module

The module creates:
* SNS Topic
* SNS Subscription (e.g. PagerDuty)
* Route53 Healthcheck
* Cloudwatch Metric Alarm
* (optional) Secondary Route53 record for failover routing policy to public S3 website with "Maintenance page"
* (optional) S3 public website with "Maintenance page" (bucket name = var.fqdn)

### Example:
We will monitor s3-static-website.s3-website-us-east-1.amazonaws.com with PagerDuty notification with
failover routing policy to S3 public website bucket with "Maintenance page"
```
module "route53-health-check" {
source = "hazelops/route53-healthcheck/aws""
version = "~> 1.0"
env = "production"
name = "my-monitoring"
port = "80"
type = "HTTP"
fqdn = "s3-static-website.s3-website-us-east-1.amazonaws.com"
subscription_endpoint = "https://events.pagerduty.com/integration/<Integration_Key>/enqueue"
r53_failover_enabled = true
domain_name = "example.com"
}
```
Note: This module just creates a secondary Route53 record for failover routing policy. Creating a primary record is out of scope of this module.
6 changes: 6 additions & 0 deletions data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
data "aws_region" "current" {}

data "aws_route53_zone" "this" {
name = "${var.domain_name}."
private_zone = false
}
10 changes: 10 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<title>Website Maintenance</title>

<article>

<center><h1>We&rsquo;ll be back soon!</h1></center>
<div style="text-align:center">
<p>The service is under Maintenance now.</p>
</div>
</article>
89 changes: 89 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
resource "aws_sns_topic" "this" {
name = "${var.env}-${var.name}-r53-healthcheck"
display_name = "${var.env}-${var.name}"
}

resource "aws_sns_topic_subscription" "this" {
endpoint = var.subscription_endpoint
endpoint_auto_confirms = var.endpoint_auto_confirms
protocol = var.subscription_endpoint_protocol
topic_arn = aws_sns_topic.this.arn
}

resource "aws_route53_health_check" "this" {
fqdn = var.fqdn
port = var.port
type = var.type
resource_path = var.resource_path
failure_threshold = var.failure_threshold
request_interval = var.request_interval

tags = {
Terraform = "true"
Name = "${var.env}-${var.name}"
}
}

resource "aws_cloudwatch_metric_alarm" "this" {
alarm_name = "${var.name}-r53-healthcheck-failed"
namespace = var.cw_alarm_namespace
metric_name = var.cw_alarm_metric_name
comparison_operator = var.cw_alarm_comparison_operator
evaluation_periods = var.cw_alarm_evaluation_periods
period = var.cw_alarm_period
statistic = var.cw_alarm_statistic
threshold = var.cw_alarm_threshold
unit = var.cw_alarm_unit

dimensions = {
HealthCheckId = aws_route53_health_check.this.id
}

alarm_description = "This metric monitors ${var.name} service endpoint ( ${var.type}://${var.fqdn}:${var.port}${var.resource_path} ) whether it is UP or Down"
ok_actions = [aws_sns_topic.this.arn]
alarm_actions = [aws_sns_topic.this.arn]
insufficient_data_actions = [aws_sns_topic.this.arn]
treat_missing_data = "breaching"
}

/*
This approach can be used for email SNS subscription:
resource "aws_cloudformation_stack" "tf_sns_topic" {
name = "EmailSNSTopicStack"
template_body = data.template_file.aws_cf_sns_stack.rendered
tags = {
name = "EmailSNSTopicStack"
}
# [INFO] Can be updated and implemented for removing email subscription during tf destroy - it's continue
# existing after default tf destroy because it was created by the CF stack
#provisioner "local-exec" {
# when = destroy
# command = "aws sns unsubscribe --subscription-arn arn:aws:sns:us-east-1:${aws_account_id}:R53-Health:31f450f7-4cf3-4de2-9202-ddc21d022262"
#}
}
data.template_file.aws_cf_sns_stack.rendered = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"SNSTopicEmail": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "${sns_topic_name}",
"DisplayName": "${sns_display_name}",
"Subscription": [
${sns_subscription_list}
]
}
}
},
"Outputs" : {
"ARN" : {
"Description" : "Email SNS Topic ARN",
"Value" : { "Ref" : "SNSTopicEmail" }
}
}
}
*/
58 changes: 58 additions & 0 deletions s3_website.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# AWS S3 bucket for website static hosting
resource "aws_s3_bucket" "this" {
count = var.r53_failover_enabled ? 1 : 0
bucket = var.fqdn
acl = "public-read"

tags = {
Terraform = "true"
Name = "${var.env}-${var.name}"
}

policy = <<EOF
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "PublicReadForS3BucketObjects",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::${var.fqdn}/*"
}
]
}
EOF

website {
index_document = "index.html"
}
}

resource "aws_s3_bucket_object" "this" {
count = var.r53_failover_enabled ? 1 : 0
key = "index.html"
bucket = aws_s3_bucket.this[0].id
source = "${path.module}/index.html"
content_type = "text/html"
}

resource "aws_route53_record" "this" {
count = var.r53_failover_enabled ? 1 : 0
zone_id = data.aws_route53_zone.this.zone_id
name = var.fqdn
type = "A"

alias {
name = aws_s3_bucket.this[0].website_domain
zone_id = aws_s3_bucket.this[0].hosted_zone_id
evaluate_target_health = false
}

failover_routing_policy {
type = "SECONDARY"
}
set_identifier = "${var.fqdn}-SECONDARY"
}
96 changes: 96 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
variable "env" {}
variable "domain_name" {}

variable "name" {
description = "The name of the monitoring and name of the subscription service endpoint"
}

variable "subscription_endpoint" {
description = "Endpoint endpoint for SNS topic subscription, PagerDuty, Slack etc."
}

variable "endpoint_auto_confirms" {
type = bool
default = true
description = "Endpoint endpoint for SNS topic subscription, PagerDuty (https://events.pagerduty.com/integration/<Integration Key>/enqueue)"
}

variable "fqdn" {
description = "The FQDN of the endpoint to be monitored"
}

variable "subscription_endpoint_protocol" {
default = "https"
description = "Endpoint protocol for SNS topic subscription"
}

variable "port" {
default = "443"
description = "The port of the endpoint to be monitored"
}

variable "type" {
default = "HTTPS"
description = "The protocol to use when performing health checks. Valid values are HTTP, HTTPS, HTTP_STR_MATCH, HTTPS_STR_MATCH, TCP, CALCULATED and CLOUDWATCH_METRIC"
}

variable "resource_path" {
default = "/"
description = "The path that you want Amazon Route 53 to request when performing health checks."
}

variable "failure_threshold" {
default = "3"
description = "The number of consecutive health checks that an endpoint must pass or fail."
}

variable "request_interval" {
default = "30"
description = "The number of seconds between the time that Amazon Route 53 gets a response from your endpoint and the time that it sends the next health-check request."
}

variable "cw_alarm_namespace" {
default = "AWS/Route53"
description = "Namespace of Cloudwatch metric alarm"
}

variable "cw_alarm_comparison_operator" {
default = "LessThanThreshold"
description = "Comparison Operator of Cloudwatch metric alarm"
}

variable "cw_alarm_metric_name" {
default = "HealthCheckStatus"
description = "Metric name of Cloudwatch metric alarm"
}

variable "cw_alarm_evaluation_periods" {
default = "1"
description = "Evaluation periods of Cloudwatch metric alarm"
}

variable "cw_alarm_period" {
default = "60"
description = "Period of Cloudwatch metric alarm"
}

variable "cw_alarm_statistic" {
default = "Minimum"
description = "Statistic of Cloudwatch metric alarm"
}

variable "cw_alarm_threshold" {
default = "1"
description = "Threshold of Cloudwatch metric alarm"
}

variable "cw_alarm_unit" {
default = "None"
description = "Unit of Cloudwatch metric alarm"
}

variable "r53_failover_enabled" {
type = bool
default = false
description = "Enabling creating secondary Failover R53 Record"
}
8 changes: 8 additions & 0 deletions versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
required_version = ">= 0.12"
}

0 comments on commit 1a60eda

Please sign in to comment.