
{
    "Transform": "AWS::Serverless-2016-10-31",
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Template of Identify Unhealthy Target for Application and Network Load Balancer",
    "Parameters": {
        "Namespace": {
            "Type": "String",
          "Description": "Please specify the namespace of your load balancer type",
            "AllowedValues": ["AWS/ApplicationELB","AWS/NetworkELB"]
        },
        "LoadBalancerName": {
            "Type": "String",
            "Default": "",
            "Description": "Enter your load balancer name. e.g.net/lbname/d665cae1604417d or app/lbname/d665cae1604417d"
        },
        "TargetGroupARN": {
            "Type": "String",
            "Default": "",
            "Description": "Enter your target group ARN. e.g.arn:aws:elasticloadbalancing:us-east-1:1111111111:targetgroup/my-tg/83b692961bdc9a81"
        },
        "CloudWatchAlarmName": {
            "Type": "String",
            "Default": "",
            "Description": "Please enter the CloudWatch Alarm name that you want to create for monitoring unhealthy targets"
        },
        "Email": {
            "Type": "String",
            "Description": "The Email address to be notified when the CloudWatch alarm is triggered"
        },
       "VPCSubnetIDs": {
         "Description": "A comma-delimited list of strings - the subnet IDs that your Lambda function should be assigned to. The subnets need to be part of your ELB's VPC and have a default route to NGW or NAT instance so that the Lambda function can access Internet. For example, subnet-a7926688,subnet-878974cf",
         "Type": "CommaDelimitedList"
      },
       "VPCSecurityGroupIDs": {
         "Description": "An comma-delimited list of strings - the security groups that your Lambda function will use. The security groups need to allow Lambda to communicate with your ELB. For example, sg-b23fb7d7,sg-af5fd2ca",
         "Type": "CommaDelimitedList"
      },
        "OnDemandHealthCheck": {
              "Type": "String",
              "AllowedValues": ["True", "False"],
              "Description": "Please specify if you want to turn on OnDemand HealthCheck"
          },
        "TargetGroupType": {
              "Type": "String",
              "AllowedValues": ["Instance", "IP"],
              "Description": "Please specify the Type of the TargetGroup"
          },
        "Region": {
            			"Type": "String",
            			"AllowedValues": ["us-east-1", "us-east-2", "us-west-1", "us-west-2", "eu-west-1", "eu-west-2", "eu-west-3", "eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ap-northeast-1", "ap-northeast-2", "sa-east-1", "ap-south-1", "ca-central-1"],
            			"Description": "Please specify the region where you want to create your Lambda function"
        }
      },
        "Mappings": {
            		"RegionMap": {
            			"ap-northeast-1": {
            				"Bucket": "exampleloadbalancer-ap-northeast-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"ap-northeast-2": {
            				"Bucket": "exampleloadbalancer-ap-northeast-2",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"ap-south-1": {
            				"Bucket": "exampleloadbalancer-ap-south-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"ap-southeast-1": {
            				"Bucket": "exampleloadbalancer-ap-southeast-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"ap-southeast-2": {
            				"Bucket": "exampleloadbalancer-ap-southeast-2",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"ca-central-1": {
            				"Bucket": "exampleloadbalancer-ca-central-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"eu-central-1": {
            				"Bucket": "exampleloadbalancer-eu-central-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"eu-west-1": {
            				"Bucket": "exampleloadbalancer-eu-west-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"eu-west-2": {
            				"Bucket": "exampleloadbalancer-eu-west-2",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"sa-east-1": {
            				"Bucket": "exampleloadbalancer-sa-east-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"us-east-1": {
            				"Bucket": "exampleloadbalancer-us-east-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"us-east-2": {
            				"Bucket": "exampleloadbalancer-us-east-2",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"us-west-1": {
            				"Bucket": "exampleloadbalancer-us-west-1",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"us-west-2": {
            				"Bucket": "exampleloadbalancer-us-west-2",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			},
            			"eu-west-3": {
            				"Bucket": "exampleloadbalancer-eu-west-3",
            				"Key": "blog-posts/identifying-unhealthy-targets-of-elastic-load-balancer/identifying_unhealthy_targets.zip"
            			}

            		}
            	},
    "Resources": {
        "AlarmNotificationTopic": {
            "Type": "AWS::SNS::Topic",
            "Properties": {
                "Subscription": [{
                    "Endpoint": {
                        "Ref": "Email"
                    },
                    "Protocol": "email"
                }]
            }
        },
        "AlarmTriggerTopic": {
            "Type": "AWS::SNS::Topic",
            "Properties": {
                "Subscription": [{
                    "Endpoint": {
                        "Fn::GetAtt": ["LambdaFunction", "Arn"]
                    },
                    "Protocol": "lambda"
                }]
            },
            "DependsOn": ["LambdaFunction"]
        },
        "UnHealthyHostCountAlarm": {
            "Type": "AWS::CloudWatch::Alarm",
            "Properties": {
                "AlarmName": {
                    "Ref": "CloudWatchAlarmName"
                },
                "AlarmDescription": "Alarms when there is any unhealthy target",
                "AlarmActions": [{
                    "Ref": "AlarmTriggerTopic"
                }],
                "MetricName": "UnHealthyHostCount",
                "Namespace": {
                    "Ref": "Namespace"
                },
                "ComparisonOperator": "GreaterThanOrEqualToThreshold",
                "Dimensions": [{
                    "Name": "LoadBalancer",
                    "Value": {
                        "Ref": "LoadBalancerName"
                    }
                },
                {
                    "Name": "TargetGroup",
                    "Value": {
                       "Fn::Select" : [ "5", { "Fn::Split": [":", {"Ref":"TargetGroupARN"}]}]
                    }
                }],
                "EvaluationPeriods": "3",
                "Period": "60",
                "Statistic": "Maximum",
                "Threshold": "1"
            }
        },
        "LambdaFunction": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "CodeUri": {
            					"Bucket": {
            						"Fn::FindInMap": ["RegionMap", {
            							"Ref": "Region"
            						}, "Bucket"]
            					},
            					"Key": {
            						"Fn::FindInMap": ["RegionMap", {
            							"Ref": "Region"
            						}, "Key"]
            					}
            				},
                "Description": "Identifying unhealthy targets",
                "Handler": "identifying_unhealthy_targets.lambda_handler",
                "Role": {
                    "Fn::GetAtt": [
                        "LambdaIAMRole",
                        "Arn"
                    ]
                },
              "VpcConfig": {
               "SecurityGroupIds": { "Ref": "VPCSecurityGroupIDs" },
               "SubnetIds": { "Ref": "VPCSubnetIDs" }
            },
                "Runtime": "python2.7",
                "Timeout": 300,
                "Environment": {
                    "Variables": {
                        "SNS_TOPIC": {
                            "Ref": "AlarmNotificationTopic"
                        },
                       "TARGETGROUP_ARN": {
                            "Ref": "TargetGroupARN"
                        },
                      "NAMESPACE": {
                        "Ref": "Namespace"
                        },
                       "ONDEMAND_HEALTHCHECK": {
                            "Ref": "OnDemandHealthCheck"
                        },
                       "TARGETGROUP_TYPE": {
                            "Ref": "TargetGroupType"
                        }
                    }
                }
            }
        },
        "LambdaInvokePermission": {
            "Type": "AWS::Lambda::Permission",
            "Properties": {
                "FunctionName": {
                    "Fn::GetAtt": [
                        "LambdaFunction",
                        "Arn"
                    ]
                },
                "Principal": "sns.amazonaws.com",
                "Action": "lambda:InvokeFunction",
                "SourceArn": {
                    "Ref": "AlarmTriggerTopic"
                }
            }
        },
        "LambdaIAMRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [{
                        "Effect": "Allow",
                        "Principal": {
                            "Service": "lambda.amazonaws.com"
                        },
                        "Action": "sts:AssumeRole"
                    }]
                },
                "Path": "/",
                "Policies": [{
                    "PolicyName": "IdentifyingUnheathyTargets",
                    "PolicyDocument": {
                        "Version": "2012-10-17",
                        "Statement": [{
                                "Sid": "LambdaLogging",
                                "Effect": "Allow",
                                "Action": [
                                    "logs:CreateLogGroup",
                                    "logs:CreateLogStream",
                                    "logs:PutLogEvents"
                                ],
                                "Resource": "*"
                            },
                            {
                                "Sid": "SNS",
                                "Action": [
                                    "sns:Publish"
                                ],
                                "Effect": "Allow",
                                "Resource": "*"
                            },
                            {
                                "Sid": "ELB",
                                "Action": [
                                    "elasticloadbalancing:Describe*"
                                ],
                                "Effect": "Allow",
                                "Resource": "*"
                            },
                           {
                                "Sid": "EC2",
                                "Action": [
                                    "ec2:CreateNetworkInterface",
                                    "ec2:Describe*",
                                  "ec2:AttachNetworkInterface",
                                  "ec2:DeleteNetworkInterface"
                                ],
                                "Effect": "Allow",
                                "Resource": "*"
                            },
                            {
                                "Sid": "CloudWatch",
                                "Action": [
                                    "cloudwatch:putMetricData"
                                ],
                                "Effect": "Allow",
                                "Resource": "*"
                            }
                        ]
                    }
                }]
            }
        }
    }
}
