#  (c) 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. This AWS Content is provided subject to the terms of the AWS
#   Customer Agreement available at https://aws.amazon.com/agreement or other writtenagreement between Customer
#   and Amazon Web Services, Inc.

--- 

AWSTemplateFormatVersion: 2010-09-09
Description: Cloudformation Template to provision Percentage based and composite CW Alarms and lambda function for detailed monitoring of unhealthy targets of ALB/NLB. 

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: CW Alarm, Lambda and SNS related Paramaters
      Parameters:
      - pLoadBalancerFullName
      - pELBName
      - pTargetGroupARN
      - pTargetGroupName
      - pNamespace
      - pIAMLambdaRoleARN
      - pTargetGroupType
      - pS3BucketName
      - pS3FolderName
      - pLambdaSNSNotificationTopicARN
      - pS3BucketNameForMessageUploads
      - pLowThresholdValue
      - pMediumThresholdValue
      - pHighThresholdValue
      - pCWApplicationLogsEnabled
      - pCWLogGroupForAppLogs

    ParameterLabels:
      pLoadBalancerFullName:
        default: Full Loadbalancer Name
      pELBName:
        default: Name Tag used for the Load balancer 
      pTargetGroupARN:
        default: ARN of the target group to be monitored for unhealthy targets
      pTargetGroupName:
        default: Target Group Name
      pNamespace:
        default: Namespace of the CloudWatch Alarm depending on the LoadBalancerType
      pIAMLambdaRoleARN:
        default: ARN of the IAM role For the Lambda Function
      pTargetGroupType:
        default: Targetgroup Type
      pS3BucketName:
        default: Name of the S3 Bucket Name
      pS3FolderName:
        default: S3 Folder Name along with the Zip FileName
      pLambdaSNSNotificationTopicARN:
        default: ARN of the SNS Topic Subscribed to EMAIL for custom notification messages
      pS3BucketNameForMessageUploads:
        default: S3 Bucket Name where the Files with complete messages will be uploaded
      pLowThresholdValue:
        default: LOW Severity Alarm Threshold Value
      pMediumThresholdValue:
        default: MEDIUM Severity Alarm Threshold Value
      pHighThresholdValue:
        default: HIGH Severity Alarm Threshold Value
      pCWApplicationLogsEnabled:
        default: Are Application Logs for EC2 servers stored in CloudWatch Log Group
      pCWLogGroupForAppLogs:
        default: Cloudwatch Log Group Name where EC2 servers logs are stored

Parameters:
  pCWApplicationLogsEnabled:
    Type: String
    Default: 'FALSE'
    Description: >-
      Select True/False according to whether the EC2 Servers registered with the ALB, are sending Application logs to Cloudwatch log group.
    AllowedValues:
      - 'TRUE'
      - 'FALSE'
  pCWLogGroupForAppLogs:
    Type: String
    Default: 'NONE'
    Description: >-
      Enter the Name of the CloudWatch Log Group if you are storing applications logs of ALB Targets and have selected TRUE. 
      If you have selected FALSE, leave the value as 'NONE'
  pLoadBalancerFullName:
    Default: ''
    Type: String
    Description: >-
      Enter your load balancer name. e.g.net/lbname/d665cae1604417d or
      app/lbname/d665cae1604417d.
      For CLASSIC LOAD BALANCERS only the name of the CLB.
  pLowThresholdValue:
    Default: '0'
    Type: String
    Description: >-
      Enter the Threshold Value required for LOW Severity Alarm
  pMediumThresholdValue:
    Default: '30'
    Type: String
    Description: >-
      Enter the Threshold Value required for Medium Severity Alarm
  pHighThresholdValue:
    Default: '50'
    Type: String
    Description: >-
      Enter the Threshold Value required for High Severity Alarm
  pELBName:
    Default: ''
    Type: String
    Description: >-
      Enter only the Name Tag used for the ALB or NLB
  pTargetGroupARN:
    Default: 'NONE'
    Type: String
    Description: >-
      Enter your target group ARN in case of ALB or NLB
      e.g.arn:aws:elasticloadbalancing:us-east-1:1111111111:targetgroup/my-tg/83b692961bdc9a81
      In case of CLB leave this value as NONE
  pTargetGroupName:
    Default: 'NONE'
    Type: String
    Description: >-
      Enter the target group Name in case of ALB or NLB
      In case of CLB leave this value as NONE
  pNamespace:
    Type: String
    Description: Please specify the namespace of your load balancer type
    Default: AWS/ApplicationELB
    AllowedValues:
      - AWS/NetworkELB
      - AWS/ApplicationELB
      - AWS/ELB
    
  pIAMLambdaRoleARN:
    Type: String
    Default: arn:aws:iam::111111111111:role/IAMRole_Lambda_ELBUnhealthyTargetMonitoring
    Description: >-
      Enter IAM role to be assumed by Lambda Function. This is created using the Step1 Cloudformation Stack.
      e.g. arn:aws:iam::111111111111:role/IAMRole_Lambda_ELBUnhealthyTargetMonitoring
  pTargetGroupType:
    Type: String
    Description: Please specify the Type of the TargetGroup
    Default: Instance
    AllowedValues:
      - Instance
      - IP
  pS3BucketName:
    Default: ''
    Type: String
    Description: >-
      Enter the name of the S3 bucket name where the lambda code is stored
  pS3BucketNameForMessageUploads:
    Default: ''
    Type: String
    Description: >-
      Enter the name of the S3 bucket where the txt files with complete messages will be uploaded
  pS3FolderName:
    Default: 'lambdacode/identifying_unhealthy_targets.zip'
    Type: String
    Description: >-
      Enter the name of the folder within S3 bucket where the lambda code is stored
  pLambdaSNSNotificationTopicARN:
    Default: ''
    Type: String
    Description: >-
      Enter the ARN Of the SNS topic to which Lambda will send the customized notification messages with all Unhealthy target details

Conditions:
  CreateCLBAlarms: !Equals 
    - !Ref pNamespace
    - AWS/ELB
  CreateALBAlarms: !Equals 
    - !Ref pNamespace
    - AWS/ApplicationELB
  CreateNLBAlarms: !Equals 
    - !Ref pNamespace
    - AWS/NetworkELB
  CreateNLBOrELBAlarms: !Or
    - !Condition CreateALBAlarms
    - !Condition CreateNLBAlarms


Resources:
#CLB Percentage ALARMS
  rCLBPercentageUnHealthyTargetsAlarmLow:
    Type: 'AWS::CloudWatch::Alarm'
    Condition: CreateCLBAlarms
    Properties:
      EvaluationPeriods: '1'
      AlarmDescription: Alarms when there is any unhealthy target
      ComparisonOperator: GreaterThanThreshold
      AlarmName: !Sub PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}
      Threshold: !Ref pLowThresholdValue
      Metrics: 
        - Expression: (m1/(m1+m2))*100
          Id: m3
          Label: customer_elb_unhealthy_target_percentage[low]
          ReturnData: True
        - Id: m1
          MetricStat:
            Metric:
              MetricName: UnHealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancerName
                  Value: !Ref pELBName
            Period: 60
            Stat: Maximum
          ReturnData: False
        - Id: m2
          MetricStat:
            Metric:
              MetricName: HealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancerName
                  Value: !Ref pELBName
            Period: 60
            Stat: Maximum
          ReturnData: False

  rCLBPercentageUnHealthyTargetsAlarmLMed:
    Type: 'AWS::CloudWatch::Alarm'
    Condition: CreateCLBAlarms
    Properties:
      EvaluationPeriods: '1'
      AlarmDescription: Alarms when there is any unhealthy target
      ComparisonOperator: GreaterThanThreshold
      AlarmName: !Sub PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}
      Threshold: !Ref pMediumThresholdValue
      Metrics: 
        - Expression: (m1/(m1+m2))*100
          Id: m3
          Label: customer_elb_unhealthy_target_percentage[med]
          ReturnData: True
        - Id: m1
          MetricStat:
            Metric:
              MetricName: UnHealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancerName
                  Value: !Ref pELBName
            Period: 60
            Stat: Maximum
          ReturnData: False
        - Id: m2
          MetricStat:
            Metric:
              MetricName: HealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancerName
                  Value: !Ref pELBName
            Period: 60
            Stat: Maximum
          ReturnData: False

  rCLBPercentageUnHealthyTargetsAlarmLHigh:
    Type: 'AWS::CloudWatch::Alarm'
    Condition: CreateCLBAlarms
    Properties:
      EvaluationPeriods: '1'
      AlarmDescription: Alarms when there is any unhealthy target
      ComparisonOperator: GreaterThanThreshold
      AlarmName: !Sub PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName}
      Threshold: !Ref pHighThresholdValue
      Metrics: 
        - Expression: (m1/(m1+m2))*100
          Id: m3
          Label: customer_elb_unhealthy_target_percentage[high]
          ReturnData: True
        - Id: m1
          MetricStat:
            Metric:
              MetricName: UnHealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancerName
                  Value: !Ref pELBName
            Period: 60
            Stat: Maximum
          ReturnData: False
        - Id: m2
          MetricStat:
            Metric:
              MetricName: HealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancerName
                  Value: !Ref pELBName
            Period: 60
            Stat: Maximum
          ReturnData: False

  rCLBCompositeAlarmLow:
    Type: AWS::CloudWatch::CompositeAlarm
    Condition: CreateCLBAlarms
    DependsOn: 
      - rCLBPercentageUnHealthyTargetsAlarmLHigh
      - rCLBPercentageUnHealthyTargetsAlarmLMed
      - rCLBPercentageUnHealthyTargetsAlarmLow
    Properties: 
      ActionsEnabled: True
      AlarmActions: 
        - !Ref rAlarmLambdaTriggerTopic
      AlarmDescription: CompositeAlarm For Low
      AlarmName: !Sub CW-Unhealthy-Server-Alarm~[LOW]~${pELBName}~${pTargetGroupName}
      AlarmRule: !Sub "ALARM(PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}) AND OK(PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}) AND OK(PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName})"  

  rCLBCompositeAlarmMed:
    Type: AWS::CloudWatch::CompositeAlarm
    Condition: CreateCLBAlarms
    DependsOn: 
      - rCLBPercentageUnHealthyTargetsAlarmLHigh
      - rCLBPercentageUnHealthyTargetsAlarmLMed
      - rCLBPercentageUnHealthyTargetsAlarmLow
    Properties: 
      ActionsEnabled: True
      AlarmActions: 
        - !Ref rAlarmLambdaTriggerTopic
      AlarmDescription: CompositeAlarm For Low
      AlarmName: !Sub CW-Unhealthy-Server-Alarm~[MEDIUM]~${pELBName}~${pTargetGroupName}
      AlarmRule: !Sub "ALARM(PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}) AND ALARM(PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}) AND OK(PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName})"  

  rCLBCompositeAlarmHigh:
    Type: AWS::CloudWatch::CompositeAlarm
    Condition: CreateCLBAlarms
    DependsOn: 
      - rCLBPercentageUnHealthyTargetsAlarmLHigh
      - rCLBPercentageUnHealthyTargetsAlarmLMed
      - rCLBPercentageUnHealthyTargetsAlarmLow
    Properties: 
      ActionsEnabled: True
      AlarmActions: 
        - !Ref rAlarmLambdaTriggerTopic
      AlarmDescription: CompositeAlarm For Low
      AlarmName: !Sub CW-Unhealthy-Server-Alarm~[HIGH]~${pELBName}~${pTargetGroupName}
      AlarmRule: !Sub "ALARM(PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}) AND ALARM(PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}) AND ALARM(PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName})"  


#ALB or NLB Percentage Based and Composite alarms
  rALBOrNLBPercentageUnHealthyTargetsAlarmLow:
    Type: 'AWS::CloudWatch::Alarm'
    Condition: CreateNLBOrELBAlarms
    Properties:
      EvaluationPeriods: '1'
      AlarmDescription: Alarms when there is any unhealthy target
      ComparisonOperator: GreaterThanThreshold
      AlarmName: !Sub PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}
      Threshold: !Ref pLowThresholdValue
      Metrics: 
        - Expression: (m1/(m1+m2))*100
          Id: m3
          Label: customer_elb_unhealthy_target_percentage[low]
          ReturnData: True
        - Id: m1
          MetricStat:
            Metric:
              MetricName: UnHealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancer
                  Value: !Ref pLoadBalancerFullName
                - Name: TargetGroup
                  Value: !Select 
                    - '5'
                    - !Split 
                      - ':'
                      - !Ref pTargetGroupARN
            Period: 60
            Stat: Maximum
          ReturnData: False
        - Id: m2
          MetricStat:
            Metric:
              MetricName: HealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancer
                  Value: !Ref pLoadBalancerFullName
                - Name: TargetGroup
                  Value: !Select 
                    - '5'
                    - !Split 
                      - ':'
                      - !Ref pTargetGroupARN
            Period: 60
            Stat: Maximum
          ReturnData: False

  rALBOrNLBPercentageUnHealthyTargetsAlarmLMed:
    Type: 'AWS::CloudWatch::Alarm'
    Condition: CreateNLBOrELBAlarms
    Properties:
      EvaluationPeriods: '1'
      AlarmDescription: Alarms when there is any unhealthy target
      ComparisonOperator: GreaterThanThreshold
      AlarmName: !Sub PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}
      Threshold: !Ref pMediumThresholdValue
      Metrics: 
        - Expression: (m1/(m1+m2))*100
          Id: m3
          Label: customer_elb_unhealthy_target_percentage[med]
          ReturnData: True
        - Id: m1
          MetricStat:
            Metric:
              MetricName: UnHealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancer
                  Value: !Ref pLoadBalancerFullName
                - Name: TargetGroup
                  Value: !Select 
                    - '5'
                    - !Split 
                      - ':'
                      - !Ref pTargetGroupARN
            Period: 60
            Stat: Maximum
          ReturnData: False
        - Id: m2
          MetricStat:
            Metric:
              MetricName: HealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancer
                  Value: !Ref pLoadBalancerFullName
                - Name: TargetGroup
                  Value: !Select 
                    - '5'
                    - !Split 
                      - ':'
                      - !Ref pTargetGroupARN
            Period: 60
            Stat: Maximum
          ReturnData: False

  rALBOrNLBPercentageUnHealthyTargetsAlarmLHigh:
    Type: 'AWS::CloudWatch::Alarm'
    Condition: CreateNLBOrELBAlarms
    Properties:
      EvaluationPeriods: '1'
      AlarmDescription: Alarms when there is any unhealthy target
      ComparisonOperator: GreaterThanThreshold
      AlarmName: !Sub PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName}
      Threshold: !Ref pHighThresholdValue
      Metrics: 
        - Expression: (m1/(m1+m2))*100
          Id: m3
          Label: customer_elb_unhealthy_target_percentage[high]
          ReturnData: True
        - Id: m1
          MetricStat:
            Metric:
              MetricName: UnHealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancer
                  Value: !Ref pLoadBalancerFullName
                - Name: TargetGroup
                  Value: !Select 
                    - '5'
                    - !Split 
                      - ':'
                      - !Ref pTargetGroupARN
            Period: 60
            Stat: Maximum
          ReturnData: False
        - Id: m2
          MetricStat:
            Metric:
              MetricName: HealthyHostCount
              Namespace: !Ref pNamespace
              Dimensions:
                - Name: LoadBalancer
                  Value: !Ref pLoadBalancerFullName
                - Name: TargetGroup
                  Value: !Select 
                    - '5'
                    - !Split 
                      - ':'
                      - !Ref pTargetGroupARN
            Period: 60
            Stat: Maximum
          ReturnData: False

  rALBOrNLBCompositeAlarmLow:
    Type: AWS::CloudWatch::CompositeAlarm
    Condition: CreateNLBOrELBAlarms
    DependsOn: 
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLHigh
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLMed
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLow
    Properties: 
      ActionsEnabled: True
      AlarmActions: 
        - !Ref rAlarmLambdaTriggerTopic
      AlarmDescription: CompositeAlarm For Low
      AlarmName: !Sub CW-Unhealthy-Server-Alarm~[LOW]~${pELBName}~${pTargetGroupName}
      AlarmRule: !Sub "ALARM(PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}) AND OK(PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}) AND OK(PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName})"  

  rALBOrNLBCompositeAlarmMed:
    Type: AWS::CloudWatch::CompositeAlarm
    Condition: CreateNLBOrELBAlarms
    DependsOn: 
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLHigh
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLMed
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLow
    Properties: 
      ActionsEnabled: True
      AlarmActions: 
        - !Ref rAlarmLambdaTriggerTopic
      AlarmDescription: CompositeAlarm For Low
      AlarmName: !Sub CW-Unhealthy-Server-Alarm~[MEDIUM]~${pELBName}~${pTargetGroupName}
      AlarmRule: !Sub "ALARM(PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}) AND ALARM(PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}) AND OK(PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName})"  

  rALBOrNLBCompositeAlarmHigh:
    Type: AWS::CloudWatch::CompositeAlarm
    Condition: CreateNLBOrELBAlarms
    DependsOn: 
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLHigh
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLMed
      - rALBOrNLBPercentageUnHealthyTargetsAlarmLow
    Properties: 
      ActionsEnabled: True
      AlarmActions: 
        - !Ref rAlarmLambdaTriggerTopic
      AlarmDescription: CompositeAlarm For Low
      AlarmName: !Sub CW-Unhealthy-Server-Alarm~[HIGH]~${pELBName}~${pTargetGroupName}
      AlarmRule: !Sub "ALARM(PercentageOfUnhealthyELBTargetsAlarm~[LOW]~${pELBName}~${pTargetGroupName}) AND ALARM(PercentageOfUnhealthyELBTargetsAlarm~[MEDIUM]~${pELBName}~${pTargetGroupName}) AND ALARM(PercentageOfUnhealthyELBTargetsAlarm~[HIGH]~${pELBName}~${pTargetGroupName})"  

  rTargetEvaluationLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        S3Bucket: !Ref pS3BucketName
        S3Key: !Ref pS3FolderName
      Description: Identifying unhealthy targets
      FunctionName: !Sub ELB_UnhealthyTargetMonitoring_${pELBName}
      Environment:
        Variables:
          SNS_TOPIC: !Ref pLambdaSNSNotificationTopicARN
          TARGETGROUP_ARN: !Ref pTargetGroupARN
          NAMESPACE: !Ref pNamespace
          TARGETGROUP_TYPE: !Ref pTargetGroupType
          LOAD_BALANCER_NAME: !Ref pLoadBalancerFullName
          LOAD_BALANCER_NAME_TAG: !Ref pELBName
          S3Bucket_For_Message_Uploads: !Ref pS3BucketNameForMessageUploads
          ApplicationLogsEnabled: !Ref pCWApplicationLogsEnabled
          CWLogGroupName: !Ref pCWLogGroupForAppLogs
      Handler: identifying_unhealthy_targets.lambda_handler
      Role: !Ref pIAMLambdaRoleARN
      Timeout: 300
      Runtime: python3.8

  rLambdaInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !GetAtt 
        - rTargetEvaluationLambdaFunction
        - Arn
      SourceArn: !Ref rAlarmLambdaTriggerTopic
      Principal: sns.amazonaws.com

  rAlarmLambdaTriggerTopic:
    Type: 'AWS::SNS::Topic'
    Properties:
      TopicName: !Sub Customer_SNS_UnhealthyTargetCheckLambda_${pELBName}
      Subscription:
        - Endpoint: !GetAtt 
            - rTargetEvaluationLambdaFunction
            - Arn
          Protocol: lambda

  