diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/aws-cdk-route53-health-check.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/aws-cdk-route53-health-check.assets.json new file mode 100644 index 0000000000000..d1678d1099dd4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/aws-cdk-route53-health-check.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "4fbcb1396d413264c2962a109ddc7d4a604c44aa26cd8ad7c1fe6d6a0e003245": { + "source": { + "path": "aws-cdk-route53-health-check.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "4fbcb1396d413264c2962a109ddc7d4a604c44aa26cd8ad7c1fe6d6a0e003245.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/aws-cdk-route53-health-check.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/aws-cdk-route53-health-check.template.json new file mode 100644 index 0000000000000..b15be3ba218c2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/aws-cdk-route53-health-check.template.json @@ -0,0 +1,186 @@ +{ + "Resources": { + "HostedZoneDB99F866": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "cdk.test." + } + }, + "HealthCheckHttp45CD8FAC": { + "Type": "AWS::Route53::HealthCheck", + "Properties": { + "HealthCheckConfig": { + "FailureThreshold": 3, + "FullyQualifiedDomainName": "lb.cdk.test", + "RequestInterval": 30, + "ResourcePath": "/health", + "Type": "HTTP" + } + } + }, + "ARecordHttp1E242324": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HealthCheckId": { + "Ref": "HealthCheckHttp45CD8FAC" + }, + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "cdk.test.", + "ResourceRecords": [ + "1.2.3.4" + ], + "SetIdentifier": "WEIGHT_100_ID_awscdkroute53healthcheckARecordHttp450B1986", + "TTL": "1800", + "Type": "A", + "Weight": 100 + } + }, + "ARecordHttp2C99D5094": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "cdk.test.", + "ResourceRecords": [ + "5.6.7.8" + ], + "SetIdentifier": "WEIGHT_0_ID_awscdkroute53healthcheckARecordHttp22B2DEA01", + "TTL": "1800", + "Type": "A", + "Weight": 0 + } + }, + "HealthCheckHttpsE7EFCB62": { + "Type": "AWS::Route53::HealthCheck", + "Properties": { + "HealthCheckConfig": { + "FailureThreshold": 3, + "FullyQualifiedDomainName": "lb-ssl.cdk.test", + "Port": 443, + "RequestInterval": 30, + "ResourcePath": "/health", + "Type": "HTTPS" + } + } + }, + "ARecordHttps92EB971E": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HealthCheckId": { + "Ref": "HealthCheckHttpsE7EFCB62" + }, + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "_foo.cdk.test.", + "ResourceRecords": [ + "1.2.3.4" + ], + "SetIdentifier": "WEIGHT_100_ID_awscdkroute53healthcheckARecordHttps207C4ED0", + "TTL": "1800", + "Type": "A", + "Weight": 100 + } + }, + "ARecordHttps2771FA16F": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "_foo.cdk.test.", + "ResourceRecords": [ + "5.6.7.8" + ], + "SetIdentifier": "WEIGHT_0_ID_awscdkroute53healthcheckARecordHttps25F6F420F", + "TTL": "1800", + "Type": "A", + "Weight": 0 + } + }, + "HealthCheckTcpC39A45DC": { + "Type": "AWS::Route53::HealthCheck", + "Properties": { + "HealthCheckConfig": { + "FailureThreshold": 3, + "FullyQualifiedDomainName": "lb-tcp.cdk.test", + "Port": 443, + "RequestInterval": 30, + "Type": "TCP" + } + } + }, + "ARecordTcp8F151AA4": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HealthCheckId": { + "Ref": "HealthCheckTcpC39A45DC" + }, + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "_bar.cdk.test.", + "ResourceRecords": [ + "1.2.3.4" + ], + "SetIdentifier": "WEIGHT_100_ID_awscdkroute53healthcheckARecordTcpEB5E7F54", + "TTL": "1800", + "Type": "A", + "Weight": 100 + } + }, + "ARecordTcp21F5B796C": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "_bar.cdk.test.", + "ResourceRecords": [ + "5.6.7.8" + ], + "SetIdentifier": "WEIGHT_0_ID_awscdkroute53healthcheckARecordTcp2987B773D", + "TTL": "1800", + "Type": "A", + "Weight": 0 + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integ.json new file mode 100644 index 0000000000000..b9facaa6fe16c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integ.json @@ -0,0 +1,14 @@ +{ + "enableLookups": true, + "version": "36.0.0", + "testCases": { + "integ-test/DefaultTest": { + "stacks": [ + "aws-cdk-route53-health-check" + ], + "diffAssets": true, + "assertionStack": "integ-test/DefaultTest/DeployAssert", + "assertionStackName": "integtestDefaultTestDeployAssert24D5C536" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integtestDefaultTestDeployAssert24D5C536.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integtestDefaultTestDeployAssert24D5C536.assets.json new file mode 100644 index 0000000000000..3555eb95abb24 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integtestDefaultTestDeployAssert24D5C536.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integtestDefaultTestDeployAssert24D5C536.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integtestDefaultTestDeployAssert24D5C536.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integtestDefaultTestDeployAssert24D5C536.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/integtestDefaultTestDeployAssert24D5C536.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/manifest.json new file mode 100644 index 0000000000000..403ff5bb52860 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/manifest.json @@ -0,0 +1,185 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-cdk-route53-health-check.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-route53-health-check.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-route53-health-check": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-route53-health-check.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/4fbcb1396d413264c2962a109ddc7d4a604c44aa26cd8ad7c1fe6d6a0e003245.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-route53-health-check.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-route53-health-check.assets" + ], + "metadata": { + "/aws-cdk-route53-health-check/HostedZone/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedZoneDB99F866" + } + ], + "/aws-cdk-route53-health-check/HealthCheckHttp/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HealthCheckHttp45CD8FAC" + } + ], + "/aws-cdk-route53-health-check/ARecordHttp/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ARecordHttp1E242324" + } + ], + "/aws-cdk-route53-health-check/ARecordHttp2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ARecordHttp2C99D5094" + } + ], + "/aws-cdk-route53-health-check/HealthCheckHttps/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HealthCheckHttpsE7EFCB62" + } + ], + "/aws-cdk-route53-health-check/ARecordHttps/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ARecordHttps92EB971E" + } + ], + "/aws-cdk-route53-health-check/ARecordHttps2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ARecordHttps2771FA16F" + } + ], + "/aws-cdk-route53-health-check/HealthCheckTcp/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HealthCheckTcpC39A45DC" + } + ], + "/aws-cdk-route53-health-check/ARecordTcp/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ARecordTcp8F151AA4" + } + ], + "/aws-cdk-route53-health-check/ARecordTcp2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ARecordTcp21F5B796C" + } + ], + "/aws-cdk-route53-health-check/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-route53-health-check/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ], + "HealthCheckA1C381C7": [ + { + "type": "aws:cdk:logicalId", + "data": "HealthCheckA1C381C7", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ARecordE7B57761": [ + { + "type": "aws:cdk:logicalId", + "data": "ARecordE7B57761", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ] + }, + "displayName": "aws-cdk-route53-health-check" + }, + "integtestDefaultTestDeployAssert24D5C536.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integtestDefaultTestDeployAssert24D5C536.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integtestDefaultTestDeployAssert24D5C536": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integtestDefaultTestDeployAssert24D5C536.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integtestDefaultTestDeployAssert24D5C536.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integtestDefaultTestDeployAssert24D5C536.assets" + ], + "metadata": { + "/integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-test/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/tree.json new file mode 100644 index 0000000000000..added9098279f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.js.snapshot/tree.json @@ -0,0 +1,429 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-route53-health-check": { + "id": "aws-cdk-route53-health-check", + "path": "aws-cdk-route53-health-check", + "children": { + "HostedZone": { + "id": "HostedZone", + "path": "aws-cdk-route53-health-check/HostedZone", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/HostedZone/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HostedZone", + "aws:cdk:cloudformation:props": { + "name": "cdk.test." + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHostedZone", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.HostedZone", + "version": "0.0.0" + } + }, + "HealthCheckHttp": { + "id": "HealthCheckHttp", + "path": "aws-cdk-route53-health-check/HealthCheckHttp", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/HealthCheckHttp/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HealthCheck", + "aws:cdk:cloudformation:props": { + "healthCheckConfig": { + "type": "HTTP", + "failureThreshold": 3, + "fullyQualifiedDomainName": "lb.cdk.test", + "requestInterval": 30, + "resourcePath": "/health" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHealthCheck", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.HealthCheck", + "version": "0.0.0" + } + }, + "ARecordHttp": { + "id": "ARecordHttp", + "path": "aws-cdk-route53-health-check/ARecordHttp", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/ARecordHttp/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "healthCheckId": { + "Ref": "HealthCheckHttp45CD8FAC" + }, + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "cdk.test.", + "resourceRecords": [ + "1.2.3.4" + ], + "setIdentifier": "WEIGHT_100_ID_awscdkroute53healthcheckARecordHttp450B1986", + "ttl": "1800", + "type": "A", + "weight": 100 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "ARecordHttp2": { + "id": "ARecordHttp2", + "path": "aws-cdk-route53-health-check/ARecordHttp2", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/ARecordHttp2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "cdk.test.", + "resourceRecords": [ + "5.6.7.8" + ], + "setIdentifier": "WEIGHT_0_ID_awscdkroute53healthcheckARecordHttp22B2DEA01", + "ttl": "1800", + "type": "A", + "weight": 0 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "HealthCheckHttps": { + "id": "HealthCheckHttps", + "path": "aws-cdk-route53-health-check/HealthCheckHttps", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/HealthCheckHttps/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HealthCheck", + "aws:cdk:cloudformation:props": { + "healthCheckConfig": { + "type": "HTTPS", + "failureThreshold": 3, + "fullyQualifiedDomainName": "lb-ssl.cdk.test", + "port": 443, + "requestInterval": 30, + "resourcePath": "/health" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHealthCheck", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.HealthCheck", + "version": "0.0.0" + } + }, + "ARecordHttps": { + "id": "ARecordHttps", + "path": "aws-cdk-route53-health-check/ARecordHttps", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/ARecordHttps/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "healthCheckId": { + "Ref": "HealthCheckHttpsE7EFCB62" + }, + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "_foo.cdk.test.", + "resourceRecords": [ + "1.2.3.4" + ], + "setIdentifier": "WEIGHT_100_ID_awscdkroute53healthcheckARecordHttps207C4ED0", + "ttl": "1800", + "type": "A", + "weight": 100 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "ARecordHttps2": { + "id": "ARecordHttps2", + "path": "aws-cdk-route53-health-check/ARecordHttps2", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/ARecordHttps2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "_foo.cdk.test.", + "resourceRecords": [ + "5.6.7.8" + ], + "setIdentifier": "WEIGHT_0_ID_awscdkroute53healthcheckARecordHttps25F6F420F", + "ttl": "1800", + "type": "A", + "weight": 0 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "HealthCheckTcp": { + "id": "HealthCheckTcp", + "path": "aws-cdk-route53-health-check/HealthCheckTcp", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/HealthCheckTcp/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HealthCheck", + "aws:cdk:cloudformation:props": { + "healthCheckConfig": { + "type": "TCP", + "failureThreshold": 3, + "fullyQualifiedDomainName": "lb-tcp.cdk.test", + "port": 443, + "requestInterval": 30 + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHealthCheck", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.HealthCheck", + "version": "0.0.0" + } + }, + "ARecordTcp": { + "id": "ARecordTcp", + "path": "aws-cdk-route53-health-check/ARecordTcp", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/ARecordTcp/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "healthCheckId": { + "Ref": "HealthCheckTcpC39A45DC" + }, + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "_bar.cdk.test.", + "resourceRecords": [ + "1.2.3.4" + ], + "setIdentifier": "WEIGHT_100_ID_awscdkroute53healthcheckARecordTcpEB5E7F54", + "ttl": "1800", + "type": "A", + "weight": 100 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "ARecordTcp2": { + "id": "ARecordTcp2", + "path": "aws-cdk-route53-health-check/ARecordTcp2", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-route53-health-check/ARecordTcp2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "_bar.cdk.test.", + "resourceRecords": [ + "5.6.7.8" + ], + "setIdentifier": "WEIGHT_0_ID_awscdkroute53healthcheckARecordTcp2987B773D", + "ttl": "1800", + "type": "A", + "weight": 0 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-route53-health-check/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-route53-health-check/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "integ-test": { + "id": "integ-test", + "path": "integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.ts new file mode 100644 index 0000000000000..5566454d52e30 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.health-check.ts @@ -0,0 +1,75 @@ +import * as cdk from 'aws-cdk-lib'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-route53-health-check'); + +const zone = new route53.HostedZone(stack, 'HostedZone', { + zoneName: 'cdk.test', +}); + +const healthCheckHttp = new route53.HealthCheck(stack, 'HealthCheckHttp', { + type: route53.HealthCheckType.HTTP, + fqdn: 'lb.cdk.test', + resourcePath: '/health', +}); + +new route53.ARecord(stack, 'ARecordHttp', { + zone, + target: route53.RecordTarget.fromValues('1.2.3.4'), + healthCheck: healthCheckHttp, + weight: 100, +}); +new route53.ARecord(stack, 'ARecordHttp2', { + zone, + target: route53.RecordTarget.fromValues('5.6.7.8'), + weight: 0, +}); + +const healthCheckHttps = new route53.HealthCheck(stack, 'HealthCheckHttps', { + type: route53.HealthCheckType.HTTPS, + fqdn: 'lb-ssl.cdk.test', + resourcePath: '/health', + port: 443, +}); + +new route53.ARecord(stack, 'ARecordHttps', { + zone, + recordName: '_foo', + target: route53.RecordTarget.fromValues('1.2.3.4'), + healthCheck: healthCheckHttps, + weight: 100, +}); +new route53.ARecord(stack, 'ARecordHttps2', { + zone, + recordName: '_foo', + target: route53.RecordTarget.fromValues('5.6.7.8'), + weight: 0, +}); + +const healthCheckTcp = new route53.HealthCheck(stack, 'HealthCheckTcp', { + type: route53.HealthCheckType.TCP, + fqdn: 'lb-tcp.cdk.test', + port: 443, +}); + +new route53.ARecord(stack, 'ARecordTcp', { + zone, + recordName: '_bar', + target: route53.RecordTarget.fromValues('1.2.3.4'), + healthCheck: healthCheckTcp, + weight: 100, +}); +new route53.ARecord(stack, 'ARecordTcp2', { + zone, + recordName: '_bar', + target: route53.RecordTarget.fromValues('5.6.7.8'), + weight: 0, +}); + +new IntegTest(app, 'integ-test', { + testCases: [stack], + diffAssets: true, + enableLookups: true, +}); diff --git a/packages/aws-cdk-lib/aws-route53/README.md b/packages/aws-cdk-lib/aws-route53/README.md index 1c26a12541242..2d5f135c5a735 100644 --- a/packages/aws-cdk-lib/aws-route53/README.md +++ b/packages/aws-cdk-lib/aws-route53/README.md @@ -220,6 +220,37 @@ Constructs are available for A, AAAA, CAA, CNAME, MX, NS, SRV and TXT records. Use the `CaaAmazonRecord` construct to easily restrict certificate authorities allowed to issue certificates for a domain to Amazon only. +### Health Checks + +You can associate health checks with records: + +```ts +declare const myZone: route53.HostedZone; + +const healthCheck = new route53.HealthCheck(this, 'HealthCheck', { + type: route53.HealthCheckType.HTTP, + fqdn: 'example.com', + port: 80, + resourcePath: '/health', + failureThreshold: 3, + requestInterval: Duration.seconds(30), +}); + +new route53.ARecord(this, 'ARecord', { + zone: myZone, + target: route53.RecordTarget.fromIpAddresses('1.2.3.4'), + healthCheck, + weight: 100, +}); +new route53.ARecord(this, 'ARecord2', { + zone: myZone, + target: route53.RecordTarget.fromIpAddresses('5.6.7.8'), + weight: 0, +}); +``` + +Possible values for `type` are `HTTP`, `HTTPS`, `TCP`, `CLOUDWATCH_METRIC`, `CALCULATED` and `RECOVERY_CONTROL`. + ### Replacing existing record sets (dangerous!) Use the `deleteExisting` prop to delete an existing record set before deploying the new one. diff --git a/packages/aws-cdk-lib/aws-route53/lib/health-check.ts b/packages/aws-cdk-lib/aws-route53/lib/health-check.ts new file mode 100644 index 0000000000000..ad6b5f3141c80 --- /dev/null +++ b/packages/aws-cdk-lib/aws-route53/lib/health-check.ts @@ -0,0 +1,517 @@ +import { Construct } from 'constructs'; +import { CfnHealthCheck } from './route53.generated'; +import { Duration, IResource, Resource } from '../../core'; + +/** + * Imported or created health check + */ +export interface IHealthCheck extends IResource { + /** + * The ID of the health check. + * + * @attribute + */ + readonly healthCheckId: string; +} + +/** + * Properties for a new health check. + */ +export interface HealthCheckProps { + /** + * The type of health check to be associated with the record. + */ + readonly type: HealthCheckType; + + /** + * CloudWatch alarm that you want Amazon Route 53 health checkers to use to determine whether the specified health check is healthy. + * + * @default - no alarm identifier + */ + readonly alarmIdentifier?: AlarmIdentifier; + + /** + * A complex type that contains one ChildHealthCheck element for each health check that you want to associate with a CALCULATED health check. + * + * @default - no child health checks + */ + readonly childHealthChecks?: string[]; + + /** + * Specify whether you want Amazon Route 53 to send the value of FullyQualifiedDomainName to the endpoint in the client_hello message during TLS negotiation. This allows the endpoint to respond to HTTPS health check requests with the applicable SSL/TLS certificate. + * + * @default - not configured + */ + readonly enableSNI?: boolean; + + /** + * The number of consecutive health checks that an endpoint must pass or fail for Amazon Route 53 to change the current status of the endpoint from unhealthy to healthy or vice versa. + * + * @default 3 + */ + readonly failureThreshold?: number; + + /** + * Fully qualified domain name of the endpoint to be checked. + * + * Amazon Route 53 behavior depends on whether you specify a value for IPAddress. + * + * If you specify a value for IPAddress: + * + * Amazon Route 53 sends health check requests to the specified IPv4 or IPv6 address and passes the value of FullyQualifiedDomainName in the Host header for all health checks except TCP health checks. This is typically the fully qualified DNS name of the endpoint on which you want Route 53 to perform health checks. + * Note: If you specify a value for Port property other than 80 or 443, Route 53 will constract the value for Host header as FullyQualifiedDomainName:Port. + * + * If you don't specify a value for IPAddress: + * + * Route 53 sends a DNS request to the domain that you specify for FullyQualifiedDomainName at the interval that you specify for RequestInterval. Using an IPv4 address that DNS returns, Route 53 then checks the health of the endpoint. + * + * Additionally, if the type of the health check is HTTP, HTTPS, HTTP_STR_MATCH, or HTTPS_STR_MATCH, Route 53 passes the value of FullyQualifiedDomainName in the Host header, as it does when you specify value for IPAddress. If the type is TCP, Route 53 doesn't pass a Host header. + * + * @default - not configured + */ + readonly fqdn?: string; + + /** + * The number of child health checks that are associated with a CALCULATED health that Amazon Route 53 must consider healthy for the CALCULATED health check to be considered healthy. + * + * @default - not configured + */ + readonly healthThreshold?: number; + + /** + * The status of the health check when CloudWatch has insufficient data about the state of associated alarm. + * + * @default - not configured + */ + readonly insufficientDataHealthStatus?: InsufficientDataHealthStatusEnum; + + /** + * Specify whether you want Amazon Route 53 to invert the status of a health check, so a health check that would normally be considered unhealthy is considered healthy, and vice versa. + * + * @default - not configured + */ + readonly inverted?: boolean; + + /** + * The IPv4 or IPv6 IP address for the endpoint that you want Amazon Route 53 to perform health checks on. If you don't specify a value for IPAddress, Route 53 sends a DNS request to resolve the domain name that you specify in FullyQualifiedDomainName at the interval that you specify in RequestInterval. Using an IPv4 address that DNS returns, Route 53 then checks the health of the endpoint. + * + * @default - not configured + */ + readonly ipAddress?: string; + + /** + * Specify whether you want Amazon Route 53 to measure the latency between health checkers in multiple AWS regions and your endpoint, and to display CloudWatch latency graphs on the Health Checks page in the Route 53 console. + * + * @default - not configured + */ + readonly measureLatency?: boolean; + + /** + * The port on the endpoint that you want Amazon Route 53 to perform health checks on. + * + * @default - not configured + */ + readonly port?: number; + + /** + * A complex type that contains one Region element for each region from which you want Amazon Route 53 health checkers to check the specified endpoint. + * + * @default - not configured + */ + readonly regions?: string[]; + + /** + * The number of seconds between the time that Amazon Route 53 gets a response from your endpoint and the time that it sends the next health check request. Each Route 53 health checker makes requests at this interval. + * + * @default 30 + */ + readonly requestInterval?: Duration; + + /** + * The path that you want Amazon Route 53 to request when performing health checks. The path can be any value for which your endpoint will return an HTTP status code of 2xx or 3xx when the endpoint is healthy, for example the file /docs/route53-health-check.html. Route 53 automatically adds the DNS name for the service and a leading forward slash (/) character. + * + * @default - not configured + */ + readonly resourcePath?: string; + + /** + * The Amazon Resource Name (ARN) of the Route 53 Application Recovery Controller routing control that you want Amazon Route 53 health checkers to use to determine whether the specified health check is healthy. + * + * @default - not configured + */ + readonly routingControl?: string; + + /** + * The string that you want Amazon Route 53 to search for in the response body from the specified resource. If the string appears in the response body, Route 53 considers the resource healthy. + * + * Route 53 considers case when searching for SearchString in the response body. + * + * @default - not configured + */ + readonly searchString?: string; +} + +/** + * Amazon Route 53 health checks monitor the health and performance of your web applications, web servers, and other resources. Each health check that you create can monitor one of the following: + * - The health of a resource, such as a web server, + * - The status of other health checks, and + * - The CloudWatch alarm that you specify, + * - The status of an Amazon Route 53 routing control. + */ +export class HealthCheck extends Resource implements IHealthCheck { + /** + * Import an existing health check into this CDK app. + * + * @param scope The parent creating construct (usually `this`). + * @param id The construct's name. + * @param healthCheckId ID of the health check. + * @returns a reference to the existing health check. + */ + public static fromHealthCheckId(scope: Construct, id: string, healthCheckId: string): IHealthCheck { + class Import extends Resource implements IHealthCheck { + public readonly healthCheckId = healthCheckId; + } + + return new Import(scope, id); + } + + public readonly healthCheckId: string; + + /** + * Creates a new health check. + * + * @param scope The parent creating construct (usually `this`). + * @param id The construct's name. + * @param props the properties of the new health check. + * @returns a reference to the newly created health check. + */ + constructor(scope: Construct, id: string, props: HealthCheckProps) { + super(scope, id); + + validateProperties(props); + + const resource = new CfnHealthCheck(this, 'Resource', { + healthCheckConfig: { + type: props.type, + alarmIdentifier: props.alarmIdentifier, + childHealthChecks: props.childHealthChecks, + enableSni: props.enableSNI, + failureThreshold: props.failureThreshold ?? 3, + fullyQualifiedDomainName: props.fqdn, + healthThreshold: props.healthThreshold, + insufficientDataHealthStatus: props.insufficientDataHealthStatus, + inverted: props.inverted, + ipAddress: props.ipAddress, + measureLatency: props.measureLatency, + port: props.port, + regions: props.regions, + requestInterval: (props.requestInterval && props.requestInterval.toSeconds()) ?? 30, + resourcePath: props.resourcePath, + routingControlArn: props.routingControl, + searchString: props.searchString, + }, + }); + + this.healthCheckId = resource.ref; + } +} + +function validateProperties(props: HealthCheckProps) { + switch (props.type) { + case HealthCheckType.HTTP: { + ruleAlarmIdentifierIsNotAllowed(props); + ruleEnableSNIIsNotAllowed(props); + ruleSearchStringIsNotAllowed(props); + ruleChildHealthChecksIsNotAllowed(props); + ruleRoutingControlIsNotAllowed(props); + ruleHealthThresholdIsNotAllowed(props); + validateFqdn(props); + validateIpAddress(props); + break; + } + case HealthCheckType.HTTPS: { + ruleAlarmIdentifierIsNotAllowed(props); + ruleSearchStringIsNotAllowed(props); + ruleChildHealthChecksIsNotAllowed(props); + ruleRoutingControlIsNotAllowed(props); + ruleHealthThresholdIsNotAllowed(props); + validateFqdn(props); + validateIpAddress(props); + break; + } + case HealthCheckType.HTTP_STR_MATCH: { + ruleAlarmIdentifierIsNotAllowed(props); + validateSearchStringForStringMatch(props); + ruleEnableSNIIsNotAllowed(props); + ruleChildHealthChecksIsNotAllowed(props); + ruleRoutingControlIsNotAllowed(props); + ruleHealthThresholdIsNotAllowed(props); + validateFqdn(props); + validateIpAddress(props); + break; + } + case HealthCheckType.HTTPS_STR_MATCH: { + ruleAlarmIdentifierIsNotAllowed(props); + validateSearchStringForStringMatch(props); + ruleChildHealthChecksIsNotAllowed(props); + ruleRoutingControlIsNotAllowed(props); + ruleHealthThresholdIsNotAllowed(props); + validateFqdn(props); + validateIpAddress(props); + break; + } + case HealthCheckType.TCP: { + ruleAlarmIdentifierIsNotAllowed(props); + ruleEnableSNIIsNotAllowed(props); + ruleSearchStringIsNotAllowed(props); + ruleChildHealthChecksIsNotAllowed(props); + ruleRoutingControlIsNotAllowed(props); + ruleHealthThresholdIsNotAllowed(props); + validateFqdn(props); + validateIpAddress(props); + break; + } + case HealthCheckType.RECOVERY_CONTROL: { + ruleAlarmIdentifierIsNotAllowed(props); + ruleRoutingControlIsRequired(props); + ruleEnableSNIIsNotAllowed(props); + ruleSearchStringIsNotAllowed(props); + ruleChildHealthChecksIsNotAllowed(props); + rulePortIsNotAllowed(props); + ruleHealthThresholdIsNotAllowed(props); + break; + } + case HealthCheckType.CALCULATED: { + validateChildHealthChecks(props); + ruleAlarmIdentifierIsNotAllowed(props); + rulePortIsNotAllowed(props); + ruleEnableSNIIsNotAllowed(props); + ruleSearchStringIsNotAllowed(props); + ruleRoutingControlIsNotAllowed(props); + ruleHealthThresholdIsRequired(props); + break; + } + case HealthCheckType.CLOUDWATCH_METRIC: { + ruleAlarmIdentifierIsRequired(props); + rulePortIsNotAllowed(props); + ruleEnableSNIIsNotAllowed(props); + ruleSearchStringIsNotAllowed(props); + ruleChildHealthChecksIsNotAllowed(props); + ruleRoutingControlIsNotAllowed(props); + ruleHealthThresholdIsNotAllowed(props); + break; + } + default: + throw new Error(`Unsupported health check type: ${props.type}`); + } + + validateRequestInterval(props); + validateFailureThreshold(props); +} + +function validateFqdn(props: HealthCheckProps) { + if (props.fqdn && props.fqdn.length > 255) { + throw new Error('FQDN must be between 0 and 255 characters long'); + } +} + +function validateSearchStringForStringMatch(props: HealthCheckProps) { + if (props.searchString === null || props.searchString === undefined) { + throw new Error(`SearchString is required for health check type: ${props.type}`); + } + + if (props.searchString.length === 0 || props.searchString.length > 255) { + throw new Error('SearchString must be between 1 and 255 characters long'); + } +} + +function validateChildHealthChecks(props: HealthCheckProps) { + if (!props.childHealthChecks || props.childHealthChecks.length === 0) { + throw new Error(`ChildHealthChecks is required for health check type: ${props.type}`); + } +} + +function ruleHealthThresholdIsRequired(props: HealthCheckProps) { + if (props.healthThreshold === undefined) { + throw new Error(`HealthThreshold is required for health check type: ${props.type}`); + } +} + +function ruleHealthThresholdIsNotAllowed(props: HealthCheckProps) { + if (props.healthThreshold !== undefined) { + throw new Error(`HealthThreshold is not supported for health check type: ${props.type}`); + } +} + +function ruleRoutingControlIsRequired(props: HealthCheckProps) { + if (props.routingControl === undefined) { + throw new Error(`RoutingControl is required for health check type: ${props.type}`); + } +} + +function rulePortIsNotAllowed(props: HealthCheckProps) { + if (props.port) { + throw new Error(`Port is not supported for health check type: ${props.type}`); + } +} + +function ruleEnableSNIIsNotAllowed(props: HealthCheckProps) { + if (props.enableSNI) { + throw new Error(`EnableSNI is only supported for health check type: ${HealthCheckType.HTTPS}`); + } +} + +function ruleChildHealthChecksIsNotAllowed(props: HealthCheckProps) { + if (props.childHealthChecks && props.childHealthChecks.length > 0) { + throw new Error(`ChildHealthChecks is only supported for health check type: ${HealthCheckType.CALCULATED}`); + } +} + +function ruleSearchStringIsNotAllowed(props: HealthCheckProps) { + if (props.searchString) { + throw new Error(`SearchString is only supported for health check types: ${HealthCheckType.HTTP_STR_MATCH}, ${HealthCheckType.HTTPS_STR_MATCH}`); + } +} + +function validateFailureThreshold(props: HealthCheckProps) { + if (props.failureThreshold !== undefined && (props.failureThreshold < 1 || props.failureThreshold > 10)) { + throw new Error('FailureThreshold must be between 1 and 10'); + } +} + +function validateRequestInterval(props: HealthCheckProps) { + if (props.requestInterval !== undefined) { + const requestIntervalInSeconds = props.requestInterval.toSeconds(); + if (requestIntervalInSeconds < 10 || requestIntervalInSeconds > 30) { + throw new Error('RequestInterval must be between 10 and 30 seconds'); + } + } +} + +function validateIpAddress(props: HealthCheckProps) { + if (props.ipAddress === undefined) { + return; + } + + if ( + !new RegExp( + '^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$', + ).test(props.ipAddress) + ) { + throw new Error('IpAddress must be a valid IPv4 or IPv6 address'); + } +} + +function ruleAlarmIdentifierIsRequired(props: HealthCheckProps) { + if (!props.alarmIdentifier) { + throw new Error(`AlarmIdentifier is required for health check type: ${HealthCheckType.CLOUDWATCH_METRIC}`); + } +} + +function ruleAlarmIdentifierIsNotAllowed(props: HealthCheckProps) { + if (props.alarmIdentifier) { + throw new Error(`AlarmIdentifier is not supported for health check type: ${props.type}`); + } +} + +function ruleRoutingControlIsNotAllowed(props: HealthCheckProps) { + if (props.routingControl) { + throw new Error(`RoutingControl is not supported for health check type: ${props.type}`); + } +} + +/** + * The status of the health check when CloudWatch has insufficient data about the state of associated alarm. + */ +export enum InsufficientDataHealthStatusEnum { + /** + * Route 53 health check status will be healthy. + */ + HEALTHY = 'HEALTHY', + + /** + * Route 53 health check status will be unhealthy. + */ + UNHEALTHY = 'UNHEALTHY', + + /** + * Route 53 health check status will be the status of the health check before Route 53 had insufficient data. + */ + LAST_KNOWN_STATUS = 'LAST_KNOWN_STATUS', +} + +/** + * The type of health check to be associated with the record. + */ +export enum HealthCheckType { + /** + * HTTP health check + * + * Route 53 tries to establish a TCP connection. If successful, Route 53 submits an HTTP request and waits for an HTTP status code of 200 or greater and less than 400. + */ + HTTP = 'HTTP', + + /** + * HTTPS health check + * + * Route 53 tries to establish a TCP connection. If successful, Route 53 submits an HTTPS request and waits for an HTTP status code of 200 or greater and less than 400. + */ + HTTPS = 'HTTPS', + + /** + * HTTP health check with string matching + * + * Route 53 tries to establish a TCP connection. If successful, Route 53 submits an HTTP request and searches the first 5,120 bytes of the response body for the string that you specify in SearchString. + */ + HTTP_STR_MATCH = 'HTTP_STR_MATCH', + + /** + * HTTPS health check with string matching + * + * Route 53 tries to establish a TCP connection. If successful, Route 53 submits an HTTPS request and searches the first 5,120 bytes of the response body for the string that you specify in SearchString. + */ + HTTPS_STR_MATCH = 'HTTPS_STR_MATCH', + + /** + * TCP health check + * + * Route 53 tries to establish a TCP connection. + */ + TCP = 'TCP', + + /** + * CloudWatch metric health check + * + * The health check is associated with a CloudWatch alarm. If the state of the alarm is OK, the health check is considered healthy. If the state is ALARM, the health check is considered unhealthy. If CloudWatch doesn't have sufficient data to determine whether the state is OK or ALARM, the health check status depends on the setting for InsufficientDataHealthStatus: Healthy, Unhealthy, or LastKnownStatus. + */ + CLOUDWATCH_METRIC = 'CLOUDWATCH_METRIC', + + /** + * Calculated health check + * + * For health checks that monitor the status of other health checks, Route 53 adds up the number of health checks that Route 53 health checkers consider to be healthy and compares that number with the value of HealthThreshold. + */ + CALCULATED = 'CALCULATED', + + /** + * Recovery control health check + * + * The health check is assocated with a Route53 Application Recovery Controller routing control. If the routing control state is ON, the health check is considered healthy. If the state is OFF, the health check is considered unhealthy. + */ + RECOVERY_CONTROL = 'RECOVERY_CONTROL', +} + +/** + * A CloudWatch alarm that you want Amazon Route 53 health checker to use to determine whether this health check is healthy. + */ +export interface AlarmIdentifier { + /** + * The region of the CloudWatch alarm that you want Amazon Route 53 health checkers to use to determine whether this health check is healthy. + */ + readonly region: string; + + /** + * The name of the CloudWatch alarm that you want Amazon Route 53 health checkers to use to determine whether this health check is healthy. + */ + readonly name: string; +} diff --git a/packages/aws-cdk-lib/aws-route53/lib/index.ts b/packages/aws-cdk-lib/aws-route53/lib/index.ts index 4083ca39bfab6..7997e835e6eba 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/index.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/index.ts @@ -6,6 +6,7 @@ export * from './key-signing-key'; export * from './record-set'; export * from './vpc-endpoint-service-domain-name'; export * from './geo-location'; +export * from './health-check'; // AWS::Route53 CloudFormation Resources: export * from './route53.generated'; diff --git a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts index 6706443b1f260..b262c64ce024b 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts @@ -1,6 +1,7 @@ import { Construct } from 'constructs'; import { AliasRecordTargetConfig, IAliasRecordTarget } from './alias-record-target'; import { GeoLocation } from './geo-location'; +import { IHealthCheck } from './health-check'; import { IHostedZone } from './hosted-zone-ref'; import { CfnRecordSet } from './route53.generated'; import { determineFullyQualifiedDomainName } from './util'; @@ -231,6 +232,15 @@ export interface RecordSetOptions { * @default - Auto generated string */ readonly setIdentifier?: string; + + /** + * The health check to associate with the record set. + * + * Route53 will return this record set in response to DNS queries only if the health check is passing. + * + * @default - No health check configured + */ + readonly healthCheck?: IHealthCheck; } /** @@ -345,6 +355,7 @@ export class RecordSet extends Resource implements IRecordSet { setIdentifier: props.setIdentifier ?? this.configureSetIdentifier(), weight: props.weight, region: props.region, + healthCheckId: props.healthCheck?.healthCheckId, }); this.domainName = recordSet.ref; diff --git a/packages/aws-cdk-lib/aws-route53/test/health-check.test.ts b/packages/aws-cdk-lib/aws-route53/test/health-check.test.ts new file mode 100644 index 0000000000000..e854b82d414df --- /dev/null +++ b/packages/aws-cdk-lib/aws-route53/test/health-check.test.ts @@ -0,0 +1,374 @@ +import { Template } from '../../assertions'; +import { CfnRoutingControl } from '../../aws-route53recoverycontrol'; +import * as cdk from '../../core'; +import { HealthCheck, HealthCheckType } from '../lib'; + +describe('health check', () => { + test('resolves routing control arn', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + const routingControl = new CfnRoutingControl(stack, 'RoutingControl', { + name: 'routing-control-name', + }); + + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.RECOVERY_CONTROL, + routingControl: routingControl.attrRoutingControlArn, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::HealthCheck', { + HealthCheckConfig: { + Type: 'RECOVERY_CONTROL', + RoutingControlArn: stack.resolve(routingControl.attrRoutingControlArn), + }, + }); + }); + + test('import health check from id', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + const healthCheck = HealthCheck.fromHealthCheckId(stack, 'HealthCheck', 'health-check-id'); + + expect(healthCheck.healthCheckId).toEqual('health-check-id'); + }); + + describe('properties validation', () => { + test.each([undefined, []])('calculated health check requires child health checks', (childHealthChecks) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck1', { + type: HealthCheckType.CALCULATED, + childHealthChecks, + }); + }).toThrow(/ChildHealthChecks is required for health check type: CALCULATED/); + }); + + test.each` + type | props + ${HealthCheckType.HTTP} | ${{}} + ${HealthCheckType.HTTPS} | ${{}} + ${HealthCheckType.HTTP_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.HTTPS_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.TCP} | ${{}} + ${HealthCheckType.CLOUDWATCH_METRIC} | ${{ alarmIdentifier: { name: 'alarm-name', region: 'us-east-1' } }} + ${HealthCheckType.RECOVERY_CONTROL} | ${{ routingControl: 'arn:aws:route53resolver:us-east-1:123456789012:routing-control/routing-control-id' }} + `('child health checks are not allowed for $type health check', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + childHealthChecks: ['child1', 'child2'], + }); + }).toThrow(/ChildHealthChecks is only supported for health check type:/); + }); + + test('health threshold is allowed for calculated health checks', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.CALCULATED, + childHealthChecks: ['child1', 'child2'], + healthThreshold: 1, + }); + }).not.toThrow(); + }); + + test.each` + type | props + ${HealthCheckType.HTTP} | ${{}} + ${HealthCheckType.HTTPS} | ${{}} + ${HealthCheckType.HTTP_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.HTTPS_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.TCP} | ${{}} + ${HealthCheckType.CLOUDWATCH_METRIC} | ${{ alarmIdentifier: { name: 'alarm-name', region: 'us-east-1' } }} + ${HealthCheckType.RECOVERY_CONTROL} | ${{ routingControl: 'arn:aws:route53resolver:us-east-1:123456789012:routing-control/routing-control-id' }} + `('health threshold is not allowed for $type health check', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + healthThreshold: 1, + }); + }).toThrow(/HealthThreshold is not supported for health check type:/); + }); + + test.each([HealthCheckType.HTTP_STR_MATCH, HealthCheckType.HTTPS_STR_MATCH])( + 'http_str_match and https_str_match require search string', + (type) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HttpStrMatchHealthCheck', { + type, + }); + }).toThrow(/SearchString is required for health check type:/); + }, + ); + + test.each(['', 'a'.repeat(256)])( + 'http_str_match and https_str_match require search string length between 1 and 255 characters long', + (searchString) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HttpStrMatchHealthCheck', { + type: HealthCheckType.HTTP_STR_MATCH, + searchString, + }); + }).toThrow(/SearchString must be between 1 and 255 characters long/); + }, + ); + + test.each` + type | props + ${HealthCheckType.HTTP} | ${{}} + ${HealthCheckType.HTTPS} | ${{}} + ${HealthCheckType.TCP} | ${{}} + ${HealthCheckType.CALCULATED} | ${{ childHealthChecks: ['child1', 'child2'] }} + ${HealthCheckType.CLOUDWATCH_METRIC} | ${{ alarmIdentifier: { name: 'alarm-name', region: 'us-east-1' } }} + ${HealthCheckType.RECOVERY_CONTROL} | ${{ routingControl: 'arn:aws:route53resolver:us-east-1:123456789012:routing-control/routing-control-id' }} + `('search string is not supported for $type health check', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + searchString: 'search', + }); + }).toThrow(/SearchString is only supported for health check types: HTTP_STR_MATCH, HTTPS_STR_MATCH/); + }); + + test('recovery control requires routing control', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'RecoveryControlHealthCheck', { + type: HealthCheckType.RECOVERY_CONTROL, + }); + }).toThrow(/RoutingControl is required for health check type: RECOVERY_CONTROL/); + }); + + test.each` + type | props + ${HealthCheckType.HTTP} | ${{}} + ${HealthCheckType.HTTPS} | ${{}} + ${HealthCheckType.HTTP_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.HTTPS_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.TCP} | ${{}} + ${HealthCheckType.CALCULATED} | ${{ childHealthChecks: ['child1', 'child2'] }} + ${HealthCheckType.CLOUDWATCH_METRIC} | ${{ alarmIdentifier: { name: 'alarm-name', region: 'us-east-1' } }} + `('recovery control is not supported for $type health check', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + routingControl: 'arn:aws:route53resolver:us-east-1:123456789012:routing-control/routing-control-id', + }); + }).toThrow(/RoutingControl is not supported for health check type:/); + }); + + test('fqdn max length is 255 characters', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.HTTP, + fqdn: 'a'.repeat(256), + }); + }).toThrow(/FQDN must be between 0 and 255 characters long/); + }); + + test('failure threshold defaults to 3', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.HTTP, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::HealthCheck', { + HealthCheckConfig: { + FailureThreshold: 3, + }, + }); + }); + + test.each([0, 11])('failure threshold must be between 1 and 10', (threshold) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.HTTP, + failureThreshold: threshold, + }); + }).toThrow(/FailureThreshold must be between 1 and 10/); + }); + + test('request interval defaults to 30 seconds', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.HTTP, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Route53::HealthCheck', { + HealthCheckConfig: { + RequestInterval: 30, + }, + }); + }); + + test.each([cdk.Duration.seconds(0), cdk.Duration.seconds(31)])('request interval must be between 10 and 30 seconds', (interval) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.HTTP, + requestInterval: interval, + }); + }).toThrow(/RequestInterval must be between 10 and 30 seconds/); + }); + + test.each` + type | props + ${HealthCheckType.HTTP} | ${{}} + ${HealthCheckType.HTTPS} | ${{}} + ${HealthCheckType.HTTP_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.HTTPS_STR_MATCH} | ${{ searchString: 'search' }} + ${HealthCheckType.TCP} | ${{}} + `('port is allowed for $type health check', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + port: 80, + }); + }).not.toThrow(); + }); + + test.each` + type | props + ${HealthCheckType.CALCULATED} | ${{ childHealthChecks: ['child1', 'child2'] }} + ${HealthCheckType.CLOUDWATCH_METRIC} | ${{ alarmIdentifier: { name: 'alarm-name', region: 'us-east-1' } }} + ${HealthCheckType.RECOVERY_CONTROL} | ${{ routingControl: 'arn:aws:route53resolver:us-east-1:123456789012:routing-control/routing-control-id' }} + `('port is not allowed for $type health check', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + port: 80, + }); + }).toThrow(/Port is not supported for health check type:/); + }); + + test('alaram identifier is required for CLOUDWATCH_METRIC health checks', () => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type: HealthCheckType.CLOUDWATCH_METRIC, + }); + }).toThrow(/AlarmIdentifier is required for health check type: CLOUDWATCH_METRIC/); + }); + + test.each` + type | props + ${HealthCheckType.HTTP} | ${{}} + ${HealthCheckType.HTTPS} | ${{}} + ${HealthCheckType.HTTP_STR_MATCH} | ${{}} + ${HealthCheckType.HTTPS_STR_MATCH} | ${{}} + ${HealthCheckType.TCP} | ${{}} + ${HealthCheckType.CALCULATED} | ${{ childHealthChecks: ['child1', 'child2'] }} + ${HealthCheckType.RECOVERY_CONTROL} | ${{}} + `('alarm identifier is not supported for $type', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + alarmIdentifier: { + name: 'alarm-name', + region: 'us-east-1', + }, + }); + }).toThrow(/AlarmIdentifier is not supported for health check type:/); + }); + + test.each` + type | props + ${HealthCheckType.HTTP} | ${{ ipAddress: '' }} + ${HealthCheckType.HTTP_STR_MATCH} | ${{ ipAddress: '', searchString: 'search' }} + ${HealthCheckType.TCP} | ${{ ipAddress: '' }} + ${HealthCheckType.HTTP} | ${{ ipAddress: 'invalid' }} + ${HealthCheckType.HTTP} | ${{ ipAddress: '1.2.3' }} + ${HealthCheckType.HTTPS} | ${{ ipAddress: '1.2' }} + ${HealthCheckType.HTTP_STR_MATCH} | ${{ ipAddress: '1', searchString: 'search' }} + ${HealthCheckType.HTTP} | ${{ ipAddress: '2001:' }} + ${HealthCheckType.TCP} | ${{ ipAddress: '2001:::::::7334' }} + `('validates IP address for TCP health checks, ip address: $props.ipAddress', ({ type, props }) => { + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + expect(() => { + new HealthCheck(stack, 'HealthCheck', { + type, + ...props, + }); + }).toThrow(/IpAddress must be a valid IPv4 or IPv6 address/); + }); + }); +}); diff --git a/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts b/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts index 20abd698ee68b..feffbd9f3bd22 100644 --- a/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts +++ b/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts @@ -188,6 +188,36 @@ describe('record set', () => { }); }); + test('A record with health check', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { + zoneName: 'myzone', + }); + + const healthCheck = new route53.HealthCheck(stack, 'HealthCheck', { + type: route53.HealthCheckType.HTTP, + fqdn: 'example.com', + resourcePath: '/health', + }); + + // WHEN + new route53.ARecord(stack, 'Alias', { + zone, + recordName: '_foo', + target: route53.RecordTarget.fromValues('1.2.3.4'), + healthCheck, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: '_foo.myzone.', + Type: 'A', + HealthCheckId: stack.resolve(healthCheck.healthCheckId), + }); + }); + test('A record with imported alias', () => { // GIVEN const stack = new Stack(); diff --git a/packages/aws-cdk-lib/awslint.json b/packages/aws-cdk-lib/awslint.json index 8db48224a89b1..f059b08cd51df 100644 --- a/packages/aws-cdk-lib/awslint.json +++ b/packages/aws-cdk-lib/awslint.json @@ -145,6 +145,7 @@ "props-physical-name:aws-cdk-lib.aws_route53.CaaRecordProps", "props-physical-name:aws-cdk-lib.aws_route53.CnameRecordProps", "props-physical-name:aws-cdk-lib.aws_route53.DsRecordProps", + "props-physical-name:aws-cdk-lib.aws_route53.HealthCheckProps", "props-physical-name:aws-cdk-lib.aws_route53.HostedZoneProps", "props-physical-name:aws-cdk-lib.aws_route53.MxRecordProps", "props-physical-name:aws-cdk-lib.aws_route53.NsRecordProps",