{
  "Description": "(SO0143C) DevOps Monitoring Dashboard on AWS - Create Canary Alarm Template. Version: v1.8.13",
  "AWSTemplateFormatVersion": "2010-09-09",
  "Metadata": {
    "AWS::CloudFormation::Interface": {
      "ParameterGroups": [
        {
          "Label": {
            "default": "Canary Configuration"
          },
          "Parameters": [
            "CanaryName",
            "CreateCanary",
            "URL",
            "Interval",
            "ResponseThreshold",
            "CreateBucket",
            "BucketName"
          ]
        },
        {
          "Label": {
            "default": "Application Monitoring"
          },
          "Parameters": [
            "AppName",
            "RepoName"
          ]
        },
        {
          "Label": {
            "default": "Alarm Configuration"
          },
          "Parameters": [
            "PercentThreshold",
            "EvalPeriods",
            "AlarmPeriods"
          ]
        }
      ],
      "ParameterLabels": {
        "URL": {
          "default": "URL"
        },
        "CanaryName": {
          "default": "Canary Name"
        },
        "CreateCanary": {
          "default": "Create New Canary?"
        },
        "CanaryInterval": {
          "default": "Canary Interval"
        },
        "CreateBucket": {
          "default": "Create Artifact Bucket?"
        },
        "BucketName": {
          "default": "Artifact Bucket Name"
        },
        "PercentThreshold": {
          "default": "% Success Threshold (<)"
        },
        "ResponseThreshold": {
          "default": "Response Threshold (ms)"
        },
        "EvalPeriods": {
          "default": "Evaluation Periods"
        },
        "AlarmPeriods": {
          "default": "Alarm Periods"
        },
        "AppName": {
          "default": "Application Name"
        },
        "RepoName": {
          "default": "Repository Name"
        }
      }
    }
  },
  "Parameters": {
    "RepoName": {
      "Type": "String",
      "Description": "Name of CodeCommit repository for the application."
    },
    "AppName": {
      "Type": "String",
      "Description": "Name of the application that canary monitors."
    },
    "URL": {
      "Type": "String",
      "Description": "Application or endpoint URL you want to monitor with the canary (for example, https://www.example.com). The canary will check the site every 5 minutes."
    },
    "CanaryName": {
      "Type": "String",
      "Default": "mycanary",
      "AllowedPattern": "^[0-9a-z_-]+$",
      "Description": "Name of your Canary (new or existing) so users can easily understand what it is in the console."
    },
    "ResponseThreshold": {
      "Type": "String",
      "Default": "15000",
      "AllowedPattern": "^[0-9]+$",
      "Description": "Number of milliseconds to wait for a url response before considering the canary failed."
    },
    "Interval": {
      "Type": "Number",
      "Default": 5,
      "Description": "Interval, in minutes."
    },
    "PercentThreshold": {
      "Type": "Number",
      "Default": 100,
      "Description": "Threshold for Success (percentage). Any value less than this value will result in an alarm being triggered."
    },
    "EvalPeriods": {
      "Type": "Number",
      "Default": 1,
      "Description": "Number of periods to compare to the threshold."
    },
    "AlarmPeriods": {
      "Type": "Number",
      "Default": 1,
      "Description": "Number of collection periods over which the threshold is exceeded before alarming. This value must be less than or equal to Evaluation Periods"
    },
    "CreateBucket": {
      "Type": "String",
      "Default": "No",
      "AllowedValues": [
        "No",
        "Yes"
      ],
      "Description": "Canaries store artifacts in an S3 bucket. Should this canary create a new bucket? Enter the bucket name (new or existing) below."
    },
    "CreateCanary": {
      "Type": "String",
      "Default": "Yes",
      "AllowedValues": [
        "No",
        "Yes"
      ],
      "Description": "Should a new canary be created? If yes, a canary with the name specified above will be created. If no, just skip the rest of Canary Configuration and move on to Application Monitoring."
    },
    "BucketName": {
      "Type": "String",
      "Description": "Name of the bucket (new or existing) for logging Canary artifacts. Each Canary will log to this bucket to a different prefix."
    }
  },
  "Conditions": {
    "CanaryBucketCondition": {
      "Fn::Equals": [
        {
          "Ref": "CreateBucket"
        },
        "Yes"
      ]
    },
    "CreateCanaryCondition": {
      "Fn::Equals": [
        {
          "Ref": "CreateCanary"
        },
        "Yes"
      ]
    },
    "CreateBucketCondition": {
      "Fn::And": [
        {
          "Condition": "CanaryBucketCondition"
        },
        {
          "Condition": "CreateCanaryCondition"
        }
      ]
    },
    "IntervalEqualsOne": {
      "Fn::Equals": [
        {
          "Ref": "Interval"
        },
        1
      ]
    },
    "CDKMetadataAvailable": {
      "Fn::Or": [
        {
          "Fn::Or": [
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "af-south-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-east-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-northeast-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-northeast-2"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-northeast-3"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-south-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-south-2"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-southeast-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-southeast-2"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-southeast-3"
              ]
            }
          ]
        },
        {
          "Fn::Or": [
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ap-southeast-4"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ca-central-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "ca-west-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "cn-north-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "cn-northwest-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-central-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-central-2"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-north-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-south-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-south-2"
              ]
            }
          ]
        },
        {
          "Fn::Or": [
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-west-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-west-2"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "eu-west-3"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "il-central-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "me-central-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "me-south-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "sa-east-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "us-east-1"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "us-east-2"
              ]
            },
            {
              "Fn::Equals": [
                {
                  "Ref": "AWS::Region"
                },
                "us-west-1"
              ]
            }
          ]
        },
        {
          "Fn::Equals": [
            {
              "Ref": "AWS::Region"
            },
            "us-west-2"
          ]
        }
      ]
    }
  },
  "Resources": {
    "ArtifactBucket7410C9EF": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketEncryption": {
          "ServerSideEncryptionConfiguration": [
            {
              "ServerSideEncryptionByDefault": {
                "SSEAlgorithm": "AES256"
              }
            }
          ]
        },
        "BucketName": {
          "Ref": "BucketName"
        },
        "PublicAccessBlockConfiguration": {
          "BlockPublicAcls": true,
          "BlockPublicPolicy": true,
          "IgnorePublicAcls": true,
          "RestrictPublicBuckets": true
        },
        "Tags": [
          {
            "Key": "Name",
            "Value": "DevOps Monitoring Dashboard on AWS Canary Artifacts"
          }
        ]
      },
      "UpdateReplacePolicy": "Retain",
      "DeletionPolicy": "Retain",
      "Metadata": {
        "aws:cdk:path": "canary-alarm/ArtifactBucket/Resource",
        "cfn_nag": {
          "rules_to_suppress": [
            {
              "id": "W35",
              "reason": "This bucket is used by canary to store artifacts and no access logging bucket is needed."
            }
          ]
        },
        "cdk_nag": {
          "rules_to_suppress": [
            {
              "reason": "This bucket is used by canary to store artifacts and no access logging bucket is needed.",
              "id": "AwsSolutions-S1"
            }
          ]
        }
      },
      "Condition": "CreateBucketCondition"
    },
    "ArtifactBucketPolicy4B4B7752": {
      "Type": "AWS::S3::BucketPolicy",
      "Properties": {
        "Bucket": {
          "Ref": "ArtifactBucket7410C9EF"
        },
        "PolicyDocument": {
          "Statement": [
            {
              "Action": "s3:*",
              "Condition": {
                "Bool": {
                  "aws:SecureTransport": "false"
                }
              },
              "Effect": "Deny",
              "Principal": {
                "AWS": "*"
              },
              "Resource": [
                {
                  "Fn::GetAtt": [
                    "ArtifactBucket7410C9EF",
                    "Arn"
                  ]
                },
                {
                  "Fn::Join": [
                    "",
                    [
                      {
                        "Fn::GetAtt": [
                          "ArtifactBucket7410C9EF",
                          "Arn"
                        ]
                      },
                      "/*"
                    ]
                  ]
                }
              ],
              "Sid": "HttpsOnly"
            }
          ],
          "Version": "2012-10-17"
        }
      },
      "Metadata": {
        "aws:cdk:path": "canary-alarm/ArtifactBucket/Policy/Resource"
      },
      "Condition": "CreateBucketCondition"
    },
    "HttpCanaryServiceRoleF71089F4": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                "Service": "lambda.amazonaws.com"
              }
            }
          ],
          "Version": "2012-10-17"
        },
        "Policies": [
          {
            "PolicyDocument": {
              "Statement": [
                {
                  "Action": "s3:ListAllMyBuckets",
                  "Effect": "Allow",
                  "Resource": "*"
                },
                {
                  "Action": "s3:GetBucketLocation",
                  "Effect": "Allow",
                  "Resource": {
                    "Fn::Join": [
                      "",
                      [
                        "arn:",
                        {
                          "Ref": "AWS::Partition"
                        },
                        ":s3:::",
                        {
                          "Ref": "BucketName"
                        }
                      ]
                    ]
                  }
                },
                {
                  "Action": "s3:PutObject",
                  "Effect": "Allow",
                  "Resource": {
                    "Fn::Join": [
                      "",
                      [
                        "arn:",
                        {
                          "Ref": "AWS::Partition"
                        },
                        ":s3:::",
                        {
                          "Ref": "BucketName"
                        },
                        "/",
                        {
                          "Ref": "AWS::StackName"
                        },
                        "/*"
                      ]
                    ]
                  }
                },
                {
                  "Action": "cloudwatch:PutMetricData",
                  "Condition": {
                    "StringEquals": {
                      "cloudwatch:namespace": "CloudWatchSynthetics"
                    }
                  },
                  "Effect": "Allow",
                  "Resource": "*"
                },
                {
                  "Action": [
                    "logs:CreateLogStream",
                    "logs:CreateLogGroup",
                    "logs:PutLogEvents"
                  ],
                  "Effect": "Allow",
                  "Resource": {
                    "Fn::Join": [
                      "",
                      [
                        "arn:",
                        {
                          "Ref": "AWS::Partition"
                        },
                        ":logs:",
                        {
                          "Ref": "AWS::Region"
                        },
                        ":",
                        {
                          "Ref": "AWS::AccountId"
                        },
                        ":log-group:/aws/lambda/cwsyn-*"
                      ]
                    ]
                  }
                }
              ],
              "Version": "2012-10-17"
            },
            "PolicyName": "canaryPolicy"
          }
        ]
      },
      "Metadata": {
        "aws:cdk:path": "canary-alarm/HttpCanary/ServiceRole/Resource",
        "cfn_nag": {
          "rules_to_suppress": [
            {
              "id": "W11",
              "reason": "Resource * is required by the canary service role."
            }
          ]
        },
        "cdk_nag": {
          "rules_to_suppress": [
            {
              "reason": "Resource * is required by the canary service role.",
              "id": "AwsSolutions-IAM5"
            }
          ]
        }
      }
    },
    "HttpCanaryE06DE534": {
      "Type": "AWS::Synthetics::Canary",
      "Properties": {
        "ArtifactS3Location": {
          "Fn::Join": [
            "",
            [
              "s3://",
              {
                "Ref": "BucketName"
              },
              "/",
              {
                "Ref": "AWS::StackName"
              }
            ]
          ]
        },
        "Code": {
          "Handler": "index.handler",
          "Script": {
            "Fn::Join": [
              "",
              [
                "var synthetics = require('Synthetics');\nconst log = require('SyntheticsLogger');\n\nconst pageLoadBlueprint = async function () {\n\n  const URL = \"",
                {
                  "Ref": "URL"
                },
                "\";\n\n  let page = await synthetics.getPage();\n  const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});\n  //Wait for page to render.\n  //Increase or decrease wait time based on endpoint being monitored.\n  await page.waitForTimeout(",
                {
                  "Ref": "ResponseThreshold"
                },
                ");\n  // This will take a screenshot that will be included in test output artifacts\n  await synthetics.takeScreenshot('loaded', 'loaded');\n  let pageTitle = await page.title();\n  log.info('Page title: ' + pageTitle);\n  if (response.status() !== 200) {\n      throw \"Failed to load page!\";\n  }\n};\n\nexports.handler = async () => {\n    return await pageLoadBlueprint();\n};"
              ]
            ]
          }
        },
        "ExecutionRoleArn": {
          "Fn::GetAtt": [
            "HttpCanaryServiceRoleF71089F4",
            "Arn"
          ]
        },
        "Name": {
          "Ref": "CanaryName"
        },
        "RuntimeVersion": "syn-nodejs-puppeteer-6.2",
        "Schedule": {
          "DurationInSeconds": "0",
          "Expression": {
            "Fn::If": [
              "IntervalEqualsOne",
              "rate(1 minute)",
              {
                "Fn::Join": [
                  "",
                  [
                    "rate(",
                    {
                      "Ref": "Interval"
                    },
                    " minutes)"
                  ]
                ]
              }
            ]
          }
        },
        "StartCanaryAfterCreation": true
      },
      "Metadata": {
        "aws:cdk:path": "canary-alarm/HttpCanary/Resource"
      },
      "Condition": "CreateCanaryCondition"
    },
    "AlarmAppAlarm83138885": {
      "Type": "AWS::CloudWatch::Alarm",
      "Properties": {
        "AlarmDescription": "Alarm when canary success is less than 100% on the most recent check.",
        "AlarmName": {
          "Fn::Join": [
            "",
            [
              "SO0143-[",
              {
                "Ref": "AppName"
              },
              "]-[",
              {
                "Ref": "RepoName"
              },
              "]-MTTR"
            ]
          ]
        },
        "ComparisonOperator": "LessThanThreshold",
        "DatapointsToAlarm": {
          "Ref": "AlarmPeriods"
        },
        "Dimensions": [
          {
            "Name": "CanaryName",
            "Value": {
              "Ref": "CanaryName"
            }
          }
        ],
        "EvaluationPeriods": {
          "Ref": "EvalPeriods"
        },
        "MetricName": "SuccessPercent",
        "Namespace": "CloudWatchSynthetics",
        "Period": 300,
        "Statistic": "Average",
        "Threshold": {
          "Ref": "PercentThreshold"
        }
      },
      "Metadata": {
        "aws:cdk:path": "canary-alarm/Alarm/AppAlarm/Resource",
        "cfn_nag": {
          "rules_to_suppress": [
            {
              "id": "W28",
              "reason": "Static names chosen intentionally to provide fixed name structure required in the solution"
            }
          ]
        }
      }
    },
    "CDKMetadata": {
      "Type": "AWS::CDK::Metadata",
      "Properties": {
        "Analytics": "v2:deflate64:H4sIAAAAAAAA/0WOwW7CMBBEv4W7syVwKNeSDygKH4C2zlYscWzkXRMhy/+OnFD1NDNv5jA7aD9b2G5wlsYOY+P4B/JZ0Y6m+/UnjDiRUqyhC35g5eANznLJsod8THYkreXbrXIKju3zH7/zGo4oVIw8vV5J2QrkDj3GZb66YhgnyH1wVGHVYqwLaZhR7RXyl8M41WoxpZieJKRol/l30ntaPv3RYnwYCG7y8dhtoT1Au7kJcxOTV54I+lVflScVYAsBAAA="
      },
      "Metadata": {
        "aws:cdk:path": "canary-alarm/CDKMetadata/Default"
      },
      "Condition": "CDKMetadataAvailable"
    }
  },
  "Outputs": {
    "SolutionVersion": {
      "Description": "Version for DevOps Monitoring Dashboard on AWS Solution",
      "Value": "v1.8.13"
    },
    "CanaryName": {
      "Description": "Name of the Canary",
      "Value": {
        "Ref": "CanaryName"
      }
    },
    "AlarmName": {
      "Description": "Name of the Canary Alarm",
      "Value": {
        "Fn::Join": [
          "",
          [
            "SO0143-[",
            {
              "Ref": "AppName"
            },
            "]-[",
            {
              "Ref": "RepoName"
            },
            "]-MTTR"
          ]
        ]
      }
    }
  }
}