Repository: aws-cloudformation/cfn-lint

Issue:
Add rule for "AWS::Route53::HealthCheck" Configuration
According to the documentation and the Specfiles the `AlarmIdentifier` of the
Route 53 HealthCheck HealthCheckConfig is optional. But I get the following error:

![image](https://user-images.githubusercontent.com/35613489/51989004-60e06300-24a6-11e9-99d7-89b5d05a907d.png)

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-healthcheck-healthcheckconfig.html#cfn-route53-healthcheck-healthcheckconfig-alarmidentifier
https://docs.aws.amazon.com/Route53/latest/APIReference/API_HealthCheckConfig.html

Used resource:
```
  Route53HealthCheck:
    Type: "AWS::Route53::HealthCheck"
    Properties:
      HealthCheckConfig:
        InsufficientDataHealthStatus: "Healthy"
        Type: "CLOUDWATCH_METRIC"
```

@cmmeyer Can you share some light on this?


Discussion:
@fatbasstard -- Since you've set your HealthCheck to by of Type to `CLOUDWATCH_METRIC`, Route53 expects an `AlarmIdentifier` to track for health.  It's not required for other HealthCheck types. For instance,  I was able to successfully create the following `HTTP` HealthCheck w/o an AlarmIdentifier (yes, yes, it's JSON...)

```
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Resources": {
        "myHealthCheck": {
            "Type": "AWS::Route53::HealthCheck",
            "Properties": {
                "HealthCheckConfig": {
                    "Port": 80,
                    "Type": "HTTP",
                    "ResourcePath": "/example/index.html",
                    "FullyQualifiedDomainName": "example.com",
                    "RequestInterval": 30,
                    "FailureThreshold": 3
                }
            }
        }
    }
}
```

This seems to be a documentation failing across the board -- it's hard to figure out even from the API docs.  It would make a good rule, but I don't think it's a spec bug...
Renamed the issue to "Create a rule for this"

Resolving patch:
diff --git a/src/cfnlint/rules/resources/route53/HealthCheck.py b/src/cfnlint/rules/resources/route53/HealthCheck.py
new file mode 100644
--- /dev/null
+++ b/src/cfnlint/rules/resources/route53/HealthCheck.py
@@ -0,0 +1,64 @@
+"""
+  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of this
+  software and associated documentation files (the "Software"), to deal in the Software
+  without restriction, including without limitation the rights to use, copy, modify,
+  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""
+from cfnlint import CloudFormationLintRule
+from cfnlint import RuleMatch
+
+
+class HealthCheck(CloudFormationLintRule):
+    """Check Route53 Recordset Configuration"""
+    id = 'E3023'
+    shortdesc = 'Validate that AlarmIdentifier is specified when using CloudWatch Metrics'
+    description = 'When using a CloudWatch Metric for Route53 Health Checks you must also specify the AlarmIdentifier'
+    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-healthcheck-healthcheckconfig.html#cfn-route53-healthcheck-healthcheckconfig-alarmidentifier'
+    tags = ['resources', 'route53', 'alarm_identifier']
+
+    def __init__(self):
+        """Init"""
+        super(HealthCheck, self).__init__()
+        self.resource_sub_property_types = ['AWS::Route53::HealthCheck.HealthCheckConfig']
+
+    def check(self, properties, path, cfn):
+        """Check itself"""
+        matches = []
+        property_sets = cfn.get_object_without_conditions(properties)
+        for property_set in property_sets:
+            health_type = property_set.get('Object').get('Type')
+            if health_type == 'CLOUDWATCH_METRIC':
+                if 'AlarmIdentifier' not in property_set.get('Object'):
+                    if property_set['Scenario'] is None:
+                        message = '"AlarmIdentifier" must be specified when the check type is "CLOUDWATCH_METRIC" at {0}'
+                        matches.append(RuleMatch(
+                            path,
+                            message.format('/'.join(map(str, path)))
+                        ))
+                    else:
+                        scenario_text = ' and '.join(['when condition "%s" is %s' % (k, v) for (k, v) in property_set['Scenario'].items()])
+                        message = '"AlarmIdentifier" must be specified when the check type is "CLOUDWATCH_METRIC" {0} at {1}'
+                        matches.append(RuleMatch(
+                            path,
+                            message.format(scenario_text, '/'.join(map(str, path)))
+                        ))
+
+        return matches
+
+    def match_resource_sub_properties(self, properties, _, path, cfn):
+        """Match for sub properties"""
+        matches = []
+
+        matches.extend(self.check(properties, path, cfn))
+
+        return matches


Test patch:
diff --git a/test/fixtures/templates/bad/resources/route53/health_check.yaml b/test/fixtures/templates/bad/resources/route53/health_check.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/bad/resources/route53/health_check.yaml
@@ -0,0 +1,23 @@
+---
+AWSTemplateFormatVersion: "2010-09-09"
+Conditions:
+  cIsPrimaryRegion: !Equals ['us-east-1', !Ref 'AWS::Region']
+Resources:
+  Route53HealthCheck:
+    Type: "AWS::Route53::HealthCheck"
+    Properties:
+      HealthCheckConfig:
+        InsufficientDataHealthStatus: "Healthy"
+        Type: "CLOUDWATCH_METRIC"
+  Route53HealthCheck1:
+    Type: "AWS::Route53::HealthCheck"
+    Properties:
+      HealthCheckConfig:
+        InsufficientDataHealthStatus: "Healthy"
+        Type: !If [cIsPrimaryRegion, "CLOUDWATCH_METRIC", "HTTP"]
+        AlarmIdentifier:
+          Fn::If:
+          - cIsPrimaryRegion
+          - !Ref 'AWS::NoValue'
+          - Name: String
+            Region: String
diff --git a/test/fixtures/templates/good/resources/route53/health_check.yaml b/test/fixtures/templates/good/resources/route53/health_check.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/good/resources/route53/health_check.yaml
@@ -0,0 +1,26 @@
+---
+AWSTemplateFormatVersion: "2010-09-09"
+Conditions:
+  cIsPrimaryRegion: !Equals ['us-east-1', !Ref 'AWS::Region']
+Resources:
+  Route53HealthCheck:
+    Type: "AWS::Route53::HealthCheck"
+    Properties:
+      HealthCheckConfig:
+        InsufficientDataHealthStatus: "Healthy"
+        Type: "CLOUDWATCH_METRIC"
+        AlarmIdentifier:
+          Name: String
+          Region: String
+  Route53HealthCheck1:
+    Type: "AWS::Route53::HealthCheck"
+    Properties:
+      HealthCheckConfig:
+        InsufficientDataHealthStatus: "Healthy"
+        Type: !If [cIsPrimaryRegion, "CLOUDWATCH_METRIC", "HTTP"]
+        AlarmIdentifier:
+          Fn::If:
+          - cIsPrimaryRegion
+          - Name: String
+            Region: String
+          - !Ref 'AWS::NoValue'
diff --git a/test/rules/resources/route53/test_health_check.py b/test/rules/resources/route53/test_health_check.py
new file mode 100644
--- /dev/null
+++ b/test/rules/resources/route53/test_health_check.py
@@ -0,0 +1,37 @@
+"""
+  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of this
+  software and associated documentation files (the "Software"), to deal in the Software
+  without restriction, including without limitation the rights to use, copy, modify,
+  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""
+from cfnlint.rules.resources.route53.HealthCheck import HealthCheck  # pylint: disable=E0401
+from ... import BaseRuleTestCase
+
+
+class TestRoute53RecordSets(BaseRuleTestCase):
+    """Test CloudFront Aliases Configuration"""
+    def setUp(self):
+        """Setup"""
+        super(TestRoute53RecordSets, self).setUp()
+        self.collection.register(HealthCheck())
+        self.success_templates = [
+            'test/fixtures/templates/good/resources/route53/health_check.yaml'
+        ]
+
+    def test_file_positive(self):
+        """Test Positive"""
+        self.helper_file_positive()
+
+    def test_file_negative(self):
+        """Test failure"""
+        self.helper_file_negative('test/fixtures/templates/bad/resources/route53/health_check.yaml', 2)