diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/aws-cdk-cloudfront-with-webacl.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/aws-cdk-cloudfront-with-webacl.assets.json new file mode 100644 index 0000000000000..9cbbbfe967006 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/aws-cdk-cloudfront-with-webacl.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "376696827a9cd2af7e143b2f3b06e066e69cc46212bced3a6690eebde614b186": { + "source": { + "path": "aws-cdk-cloudfront-with-webacl.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "376696827a9cd2af7e143b2f3b06e066e69cc46212bced3a6690eebde614b186.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-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/aws-cdk-cloudfront-with-webacl.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/aws-cdk-cloudfront-with-webacl.template.json new file mode 100644 index 0000000000000..bfaa179b7fa34 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/aws-cdk-cloudfront-with-webacl.template.json @@ -0,0 +1,83 @@ +{ + "Resources": { + "WebAcl": { + "Type": "AWS::WAFv2::WebACL", + "Properties": { + "DefaultAction": { + "Allow": {} + }, + "Scope": "CLOUDFRONT", + "VisibilityConfig": { + "CloudWatchMetricsEnabled": false, + "MetricName": "webAclMetric", + "SampledRequestsEnabled": false + } + } + }, + "Distribution830FAC52": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "awscdkcloudfrontwithwebaclDistributionOrigin11CAC3663", + "ViewerProtocolPolicy": "allow-all" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": "www.example.com", + "Id": "awscdkcloudfrontwithwebaclDistributionOrigin11CAC3663" + } + ], + "WebACLId": { + "Fn::GetAtt": [ + "WebAcl", + "Arn" + ] + } + } + } + } + }, + "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-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.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-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integ.json new file mode 100644 index 0000000000000..b94d99be5517f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "integ-cloudfront-with-webacl/DefaultTest": { + "stacks": [ + "aws-cdk-cloudfront-with-webacl" + ], + "assertionStack": "integ-cloudfront-with-webacl/DefaultTest/DeployAssert", + "assertionStackName": "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.assets.json new file mode 100644 index 0000000000000..51c22564b38bc --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.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-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.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-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/manifest.json new file mode 100644 index 0000000000000..5b42d422302a5 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/manifest.json @@ -0,0 +1,119 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-cdk-cloudfront-with-webacl.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-cloudfront-with-webacl.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-cloudfront-with-webacl": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-cloudfront-with-webacl.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}/376696827a9cd2af7e143b2f3b06e066e69cc46212bced3a6690eebde614b186.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-cloudfront-with-webacl.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-cloudfront-with-webacl.assets" + ], + "metadata": { + "/aws-cdk-cloudfront-with-webacl/WebAcl": [ + { + "type": "aws:cdk:logicalId", + "data": "WebAcl" + } + ], + "/aws-cdk-cloudfront-with-webacl/Distribution/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Distribution830FAC52" + } + ], + "/aws-cdk-cloudfront-with-webacl/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-cloudfront-with-webacl/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-cloudfront-with-webacl" + }, + "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.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": [ + "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.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": [ + "integcloudfrontwithwebaclDefaultTestDeployAssert166CAFCF.assets" + ], + "metadata": { + "/integ-cloudfront-with-webacl/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-cloudfront-with-webacl/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-cloudfront-with-webacl/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-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/tree.json new file mode 100644 index 0000000000000..9ce07a8dc2fb2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.js.snapshot/tree.json @@ -0,0 +1,181 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-cloudfront-with-webacl": { + "id": "aws-cdk-cloudfront-with-webacl", + "path": "aws-cdk-cloudfront-with-webacl", + "children": { + "WebAcl": { + "id": "WebAcl", + "path": "aws-cdk-cloudfront-with-webacl/WebAcl", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::WAFv2::WebACL", + "aws:cdk:cloudformation:props": { + "defaultAction": { + "allow": {} + }, + "scope": "CLOUDFRONT", + "visibilityConfig": { + "cloudWatchMetricsEnabled": false, + "metricName": "webAclMetric", + "sampledRequestsEnabled": false + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Distribution": { + "id": "Distribution", + "path": "aws-cdk-cloudfront-with-webacl/Distribution", + "children": { + "Origin1": { + "id": "Origin1", + "path": "aws-cdk-cloudfront-with-webacl/Distribution/Origin1", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-cloudfront-with-webacl/Distribution/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudFront::Distribution", + "aws:cdk:cloudformation:props": { + "distributionConfig": { + "enabled": true, + "origins": [ + { + "domainName": "www.example.com", + "id": "awscdkcloudfrontwithwebaclDistributionOrigin11CAC3663", + "customOriginConfig": { + "originProtocolPolicy": "https-only" + } + } + ], + "defaultCacheBehavior": { + "pathPattern": "*", + "targetOriginId": "awscdkcloudfrontwithwebaclDistributionOrigin11CAC3663", + "cachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "compress": true, + "viewerProtocolPolicy": "allow-all" + }, + "httpVersion": "http2", + "ipv6Enabled": true, + "webAclId": { + "Fn::GetAtt": [ + "WebAcl", + "Arn" + ] + } + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-cloudfront-with-webacl/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-cloudfront-with-webacl/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "integ-cloudfront-with-webacl": { + "id": "integ-cloudfront-with-webacl", + "path": "integ-cloudfront-with-webacl", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integ-cloudfront-with-webacl/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integ-cloudfront-with-webacl/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integ-cloudfront-with-webacl/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-cloudfront-with-webacl/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-cloudfront-with-webacl/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.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": "constructs.Construct", + "version": "10.3.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.ts new file mode 100644 index 0000000000000..b2bdff67feca9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.cloudfront-with-webacl.ts @@ -0,0 +1,35 @@ +import * as cdk from 'aws-cdk-lib'; +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { TestOrigin } from './test-origin'; +import { CfnWebACL } from 'aws-cdk-lib/aws-wafv2'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-cloudfront-with-webacl', { + env: { + region: 'us-east-1', + }, +}); + +const webAcl = new CfnWebACL(stack, 'WebAcl', { + defaultAction: { + allow: {}, + }, + scope: 'CLOUDFRONT', + visibilityConfig: { + cloudWatchMetricsEnabled: false, + metricName: 'webAclMetric', + sampledRequestsEnabled: false, + }, +}); + +const distribution = new cloudfront.Distribution(stack, 'Distribution', { + defaultBehavior: { origin: new TestOrigin('www.example.com') }, +}); + +distribution.attachWebAclId(webAcl.attrArn); + +new IntegTest(app, 'integ-cloudfront-with-webacl', { + testCases: [stack], +}); diff --git a/packages/aws-cdk-lib/aws-cloudfront/README.md b/packages/aws-cdk-lib/aws-cloudfront/README.md index 69c9dbe1464c2..07c37adb03cf9 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/README.md +++ b/packages/aws-cdk-lib/aws-cloudfront/README.md @@ -217,6 +217,37 @@ new cloudfront.Distribution(this, 'myDist', { }); ``` +### Attaching WAF Web Acls + +You can attach the AWS WAF web ACL to a CloudFront distribution. + +To specify a web ACL created using the latest version of AWS WAF, use the ACL ARN, for example +`arn:aws:wafv2:us-east-1:123456789012:global/webacl/ExampleWebACL/473e64fd-f30b-4765-81a0-62ad96dd167a`. +The web ACL must be in the `us-east-1` region. + +To specify a web ACL created using AWS WAF Classic, use the ACL ID, for example `473e64fd-f30b-4765-81a0-62ad96dd167a`. + +```ts +declare const bucketOrigin: origins.S3Origin; +declare const webAcl: wafv2.CfnWebACL; +const distribution = new cloudfront.Distribution(this, 'Distribution', { + defaultBehavior: { origin: bucketOrigin }, + webAclId: webAcl.attrArn, +}); +``` + +You can also attach a web ACL to a distribution after creation. + +```ts +declare const bucketOrigin: origins.S3Origin; +declare const webAcl: wafv2.CfnWebACL; +const distribution = new cloudfront.Distribution(this, 'Distribution', { + defaultBehavior: { origin: bucketOrigin }, +}); + +distribution.attachWebAclId(webAcl.attrArn); +``` + ### Customizing Cache Keys and TTLs with Cache Policies You can use a cache policy to improve your cache hit ratio by controlling the values (URL query strings, HTTP headers, and cookies) diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts index 70f3fdfae0265..ba8bac6636cd9 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts @@ -312,6 +312,7 @@ export class Distribution extends Resource implements IDistribution { private readonly errorResponses: ErrorResponse[]; private readonly certificate?: acm.ICertificate; private readonly publishAdditionalMetrics?: boolean; + private webAclId?: string; constructor(scope: Construct, id: string, props: DistributionProps) { super(scope, id); @@ -338,6 +339,7 @@ export class Distribution extends Resource implements IDistribution { this.certificate = props.certificate; this.errorResponses = props.errorResponses ?? []; this.publishAdditionalMetrics = props.publishAdditionalMetrics; + this.webAclId = props.webAclId; // Comments have an undocumented limit of 128 characters const trimmedComment = @@ -363,7 +365,7 @@ export class Distribution extends Resource implements IDistribution { restrictions: this.renderRestrictions(props.geoRestriction), viewerCertificate: this.certificate ? this.renderViewerCertificate(this.certificate, props.minimumProtocolVersion, props.sslSupportMethod) : undefined, - webAclId: props.webAclId, + webAclId: Lazy.string({ produce: () => this.webAclId }), }, }); @@ -601,6 +603,18 @@ export class Distribution extends Resource implements IDistribution { return this.grant(identity, 'cloudfront:CreateInvalidation'); } + /** + * Attach WAF WebACL to this CloudFront distribution + * + * @param webAclId The WAF WebACL to associate with this distribution + */ + public attachWebAclId(webAclId: string) { + if (this.webAclId) { + throw new Error('A WebACL has already been attached to this distribution'); + } + this.webAclId = webAclId; + } + private addOrigin(origin: IOrigin, isFailoverOrigin: boolean = false): string { const ORIGIN_ID_MAX_LENGTH = 128; diff --git a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts index fb06b7b1b9ef5..a6dc892975c0d 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts @@ -1334,3 +1334,34 @@ describe('Distribution metrics tests', () => { }).toThrow(new RegExp(`${metric.errorMetricName} metric is only available if 'publishAdditionalMetrics' is set 'true'`)); }); }); + +describe('attachWebAclId', () => { + test('can attach WebAcl to the distribution by the method', () => { + const origin = defaultOrigin(); + + const distribution = new Distribution(stack, 'MyDist', { + defaultBehavior: { origin }, + }); + + distribution.attachWebAclId('473e64fd-f30b-4765-81a0-62ad96dd167a'); + + Template.fromStack(stack).hasResourceProperties('AWS::CloudFront::Distribution', { + DistributionConfig: { + WebACLId: '473e64fd-f30b-4765-81a0-62ad96dd167a', + }, + }); + }); + + test('throws if a WebAcl is already attached to the distribution', () => { + const origin = defaultOrigin(); + + const distribution = new Distribution(stack, 'MyDist', { + defaultBehavior: { origin }, + webAclId: '473e64fd-f30b-4765-81a0-62ad96dd167a', + }); + + expect(() => { + distribution.attachWebAclId('473e64fd-f30b-4765-81a0-62ad96dd167b'); + }).toThrow(/A WebACL has already been attached to this distribution/); + }); +}); diff --git a/packages/aws-cdk-lib/rosetta/aws_cloudfront/default.ts-fixture b/packages/aws-cdk-lib/rosetta/aws_cloudfront/default.ts-fixture index d8c849ae21977..38de5258fdd15 100644 --- a/packages/aws-cdk-lib/rosetta/aws_cloudfront/default.ts-fixture +++ b/packages/aws-cdk-lib/rosetta/aws_cloudfront/default.ts-fixture @@ -6,6 +6,7 @@ import * as s3 from 'aws-cdk-lib/aws-s3'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2'; import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as wafv2 from 'aws-cdk-lib/aws-wafv2'; import * as path from 'path'; class Context extends Stack {