Skip to content

CloudWatch + ALB + Autoscaling with Launch Templates

Step-01: Introduction

  • Create the following Alarms using CloudWatch with the end to end usecase we have built so far
  • AWS Application Load Balancer Alarms
  • AWS Autoscaling Group Alarms
  • AWS CIS Alarms (Center for Internet Security)
  • AWS CloudWatch Synthetics
  • Implement a Heart Beat Monitor

Image

Image

Image

Step-02: Copy all files from Section-15

  • Copy all the files from 15-Autoscaling-with-Launch-Templates\terraform-manifests

Step-03: c12-route53-dnsregistration.tf

  • Change the DNS name as per your demo content
  name    = "cloudwatch1.devopsincloud.com"

Step-04: c14-01-cloudwatch-variables.tf

  • Create a place holder file to define CloudWatch Variables

Step-05: c14-02-cloudwatch-asg-alarms.tf

# Define CloudWatch Alarms for Autoscaling Groups

# Autoscaling - Scaling Policy for High CPU
resource "aws_autoscaling_policy" "high_cpu" {
  name                   = "high-cpu"
  scaling_adjustment     = 4
  adjustment_type        = "ChangeInCapacity"
  cooldown               = 300
  autoscaling_group_name = aws_autoscaling_group.my_asg.name 
}

# Cloud Watch Alarm to trigger the above scaling policy when CPU Utilization is above 80%
# Also send the notificaiton email to users present in SNS Topic Subscription
resource "aws_cloudwatch_metric_alarm" "app1_asg_cwa_cpu" {
  alarm_name          = "App1-ASG-CWA-CPUUtilization"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "2"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = "120"
  statistic           = "Average"
  threshold           = "80"

  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.my_asg.name 
  }

  alarm_description = "This metric monitors ec2 cpu utilization and triggers the ASG Scaling policy to scale-out if CPU is above 80%"

  ok_actions          = [aws_sns_topic.myasg_sns_topic.arn]  
  alarm_actions     = [
    aws_autoscaling_policy.high_cpu.arn, 
    aws_sns_topic.myasg_sns_topic.arn
    ]
}

Step-06: c14-03-cloudwatch-alb-alarms.tf

# Define CloudWatch Alarms for ALB
# Alert if HTTP 4xx errors are more than threshold value
resource "aws_cloudwatch_metric_alarm" "alb_4xx_errors" {
  alarm_name          = "App1-ALB-HTTP-4xx-errors"
  comparison_operator = "GreaterThanThreshold"
  datapoints_to_alarm = "2" # "2"
  evaluation_periods  = "3" # "3"
  metric_name         = "HTTPCode_Target_4XX_Count"
  namespace           = "AWS/ApplicationELB"
  period              = "120"
  statistic           = "Sum"
  threshold           = "5"  # Update real-world value like 100, 200 etc
  treat_missing_data  = "missing"  
  dimensions = {
    LoadBalancer = module.alb.lb_arn_suffix
  }
  alarm_description = "This metric monitors ALB HTTP 4xx errors and if they are above 100 in specified interval, it is going to send a notification email"
  ok_actions          = [aws_sns_topic.myasg_sns_topic.arn]  
  alarm_actions     = [aws_sns_topic.myasg_sns_topic.arn]
}

# Per AppELB Metrics
## - HTTPCode_ELB_5XX_Count
## - HTTPCode_ELB_502_Count
## - TargetResponseTime
# Per AppELB, per TG Metrics
## - UnHealthyHostCount
## - HealthyHostCount
## - HTTPCode_Target_4XX_Count
## - TargetResponseTime

Step-07: c14-04-cloudwatch-cis-alarms.tf

# Create Log Group for CIS
resource "aws_cloudwatch_log_group" "cis_log_group" {
  name = "cis-log-group-${random_pet.this.id}"
}

# Define CIS Alarms
module "all_cis_alarms" {
  source  = "terraform-aws-modules/cloudwatch/aws//modules/cis-alarms"
  version = "2.0.0"

  disabled_controls = ["DisableOrDeleteCMK", "VPCChanges"]
  log_group_name = aws_cloudwatch_log_group.cis_log_group.name 
  alarm_actions  = [aws_sns_topic.myasg_sns_topic.arn]
  tags = local.common_tags
}

Step-08: AWS CloudWatch Synthetics - Run manually and Understand

  • Understand AWS CloudWatch Synthetics
  • Create CloudWatch Synthetics using AWS management console and explore more about it

Step-09: AWS CloudWatch Synthetics using Terraform

  • Review the following files
  • File-1: sswebsite2\nodejs\node_modules\sswebsite2.js
  • File-2: sswebsite2v1.zip

Step-09-01: Create Folder Structure

  • nodejs\node_modules\

Step-09-02: Create sswebsite2.js file

  • Use Heart Beat Monitor sample from AWS Management Console - AWS CloudWatch Sythetic Service
  • Update your Application DNS Name
# Before 
    const urls = ['https://stacksimplify.com'];

# After 
    const urls = ['https://yourapp.com'];

Step-09-03: Create ZIP file

cd sswebsite2
zip -r sswebsite2v1.zip nodejs

Step-09-04: c14-05-cloudwatch-synthetics.tf - Create IAM Policy and Role

# AWS IAM Policy
resource "aws_iam_policy" "cw_canary_iam_policy" {
  name        = "cw-canary-iam-policy"
  path        = "/"
  description = "CloudWatch Canary Synthetic IAM Policy"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "cloudwatch:PutMetricData",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "cloudwatch:namespace": "CloudWatchSynthetics"
                }
            }
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "logs:CreateLogStream",
                "s3:ListAllMyBuckets",
                "logs:CreateLogGroup",
                "logs:PutLogEvents",
                "s3:GetBucketLocation",
                "xray:PutTraceSegments"
            ],
            "Resource": "*"
        }
    ]
})
}

# AWS IAM Role
resource "aws_iam_role" "cw_canary_iam_role" {
  name                = "cw-canary-iam-role"
  description = "CloudWatch Synthetics lambda execution role for running canaries"
  path = "/service-role/"
  #assume_role_policy  = data.aws_iam_policy_document.instance_assume_role_policy.json # (not shown)
  assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" 
  managed_policy_arns = [aws_iam_policy.cw_canary_iam_policy.arn]
}

Step-09-05: c14-05-cloudwatch-synthetics.tf - Create S3 Bucket

# Create S3 Bucket
resource "aws_s3_bucket" "cw_canary_bucket" {
  bucket = "cw-canary-bucket-${random_pet.this.id}"
  acl    = "private"
  force_destroy = true

  tags = {
    Name        = "My bucket"
    Environment = "Dev"
  }
}

Step-09-06: c14-05-cloudwatch-synthetics.tf - Create AWS CloudWatch Canary Resource


# AWS CloudWatch Canary
resource "aws_synthetics_canary" "sswebsite2" {
  name                 = "sswebsite2"
  artifact_s3_location = "s3://${aws_s3_bucket.cw_canary_bucket.id}/sswebsite2"
  execution_role_arn   = aws_iam_role.cw_canary_iam_role.arn 
  handler              = "sswebsite2.handler"
  zip_file             = "sswebsite2/sswebsite2v1.zip"
  runtime_version      = "syn-nodejs-puppeteer-3.1"
  start_canary = true

  run_config {
    active_tracing = true
    memory_in_mb = 960
    timeout_in_seconds = 60
  }
  schedule {
    expression = "rate(1 minute)"
  }
}

Step-09-07: c14-05-cloudwatch-synthetics.tf - Create AWS CloudWatch Metric Alarm for Canary Resource

# AWS CloudWatch Metric Alarm for Synthetics Heart Beat Monitor when availability is less than 10 percent
resource "aws_cloudwatch_metric_alarm" "synthetics_alarm_app1" {
  alarm_name          = "Synthetics-Alarm-App1"
  comparison_operator = "LessThanThreshold"
  datapoints_to_alarm = "1" # "2"
  evaluation_periods  = "1" # "3"
  metric_name         = "SuccessPercent"
  namespace           = "CloudWatchSynthetics"
  period              = "300"
  statistic           = "Average"
  threshold           = "90"  
  treat_missing_data  = "breaching" # You can also add "missing"
  dimensions = {
    CanaryName = aws_synthetics_canary.sswebsite2.id 
  }
  alarm_description = "Synthetics alarm metric: SuccessPercent  LessThanThreshold 90"
  ok_actions          = [aws_sns_topic.myasg_sns_topic.arn]  
  alarm_actions     = [aws_sns_topic.myasg_sns_topic.arn]
}

Step-10: Execute Terraform Commands

# Terraform Initialize
terraform init

# Terraform Validate
terraform validate

# Terraform Plan
terraform plan

# Terraform Apply
terraform apply -auto-approve

Step-11: Verify Resources

  1. Confirm SNS Subscription in your email
  2. Verify EC2 Instances
  3. Verify Launch Templates (High Level)
  4. Verify Autoscaling Group (High Level)
  5. Verify Load Balancer
  6. Verify Load Balancer Target Group - Health Checks
  7. Cloud Watch
  8. ALB Alarm
  9. ASG Alarm
  10. CIS Alarms
  11. Synthetics
  12. Access and Test
# Access and Test
http://cloudwatch.devopsincloud.com
http://cloudwatch.devopsincloud.com/app1/index.html
http://cloudwatch.devopsincloud.com/app1/metadata.html

Step-11: Clean-Up

# Delete Resources
terraform destroy -auto-approve

# Delete Files
rm -rf .terraform*
rm -rf terraform.tfstate*

Additional Knowledge

terraform import aws_cloudwatch_metric_alarm.test alarm-12345
terraform import aws_cloudwatch_metric_alarm.temp1 alb-4xx-temp-1

References