From 647a1c564d4cd453a90d2dfc8f656c9fcee2b294 Mon Sep 17 00:00:00 2001 From: Corneliu Croitoru Date: Thu, 24 Oct 2024 15:56:10 +0200 Subject: [PATCH 1/3] feat: expose CloudFront distribution as property (#18) - Add public readonly distribution property to Hosting construct - Expose CloudFront distribution from HostingInfrastructure - Update documentation with new property --- lib/hosting.ts | 122 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 40 deletions(-) diff --git a/lib/hosting.ts b/lib/hosting.ts index e6afe3c..3fdaa29 100644 --- a/lib/hosting.ts +++ b/lib/hosting.ts @@ -1,36 +1,34 @@ /* - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ - -import { + import { Aws, aws_cloudfront as cloudfront, + aws_s3 as s3, + aws_cloudfront_origins as origins, } from "aws-cdk-lib"; import * as path from "path"; import * as fs from "fs"; - import { Construct } from "constructs"; import { HostingInfrastructure } from "./hosting_infrastructure"; import { PipelineInfrastructure } from "./pipeline_infrastructure"; import { HostingConfiguration } from "../bin/cli/shared/types"; import { truncateString } from "./utility"; + interface IParamProps { hostingConfiguration: HostingConfiguration; buildFilePath: string; cffSourceFilePath: string; - connectionArn?: string ; + connectionArn?: string; certificateArn?: string; } @@ -43,50 +41,94 @@ interface IParamProps { * @param scope - The Construct scope in which this construct is defined. * @param id - The identifier for this construct within the scope. * @param params - Parameters for configuring hosting resources. - * - `configuration` (required): The IConfiguration object representing the hosting configuration. - * - `buildFilePath` (required): The path to the build file for the hosting resources. - * - `connectionArn` (optional): The ARN of the connection resource (if applicable). - * - `certificateArn` (optional): The ARN of the certificate resource (if applicable). */ export class Hosting extends Construct { + /** + * The CloudFront Distribution created by this construct. + */ + public readonly distribution: cloudfront.Distribution; + + /** + * The S3 Bucket used for hosting the content. + */ + public readonly hostingBucket: s3.Bucket; + + /** + * The CloudFront Function used for URI manipulation. + */ + public readonly changeUriFunction: cloudfront.Function; + + /** + * The Key-Value Store used by CloudFront. + */ + public readonly uriStore: cloudfront.KeyValueStore; + + /** + * The distribution's domain name. + */ + public readonly distributionDomainName: string; + + /** + * The distribution's URL (https://{domainName}). + */ + public readonly distributionUrl: string; + + /** + * Reference to the hosting infrastructure construct. + */ + public readonly hostingInfrastructure: HostingInfrastructure; + + /** + * Reference to the pipeline infrastructure construct. + */ + public readonly pipelineInfrastructure: PipelineInfrastructure; + constructor(scope: Construct, id: string, params: IParamProps) { super(scope, id); - const uriStore = new cloudfront.KeyValueStore(this, 'UriStore', { + // Create URI Store + this.uriStore = new cloudfront.KeyValueStore(this, 'UriStore', { keyValueStoreName: truncateString(Aws.STACK_NAME + "-" + Aws.REGION, 65) }); + // Setup CloudFront Function let cloudFrontFunctionCode = fs.readFileSync(params.cffSourceFilePath, 'utf-8'); + cloudFrontFunctionCode = cloudFrontFunctionCode.replace(/__KVS_ID__/g, this.uriStore.keyValueStoreId); - cloudFrontFunctionCode = cloudFrontFunctionCode.replace(/__KVS_ID__/g, uriStore.keyValueStoreId); - - const changeUri = new cloudfront.Function(this, "ChangeUri", { + this.changeUriFunction = new cloudfront.Function(this, "ChangeUri", { code: cloudfront.FunctionCode.fromInline(cloudFrontFunctionCode), - runtime: cloudfront.FunctionRuntime.JS_2_0, + runtime: cloudfront.FunctionRuntime.JS_2_0, comment: "Change uri", - }); - - (changeUri.node.defaultChild as cloudfront.CfnFunction).addPropertyOverride("FunctionConfig.KeyValueStoreAssociations", - [{ - "KeyValueStoreARN": uriStore.keyValueStoreArn - }]); - + (this.changeUriFunction.node.defaultChild as cloudfront.CfnFunction).addPropertyOverride( + "FunctionConfig.KeyValueStoreAssociations", + [{ + "KeyValueStoreARN": this.uriStore.keyValueStoreArn + }] + ); - const hostingInfrastructure = new HostingInfrastructure(this, "HostingInfrastructure", { - changeUri: changeUri, + // Create Hosting Infrastructure + this.hostingInfrastructure = new HostingInfrastructure(this, "HostingInfrastructure", { + changeUri: this.changeUriFunction, certificateArn: params.certificateArn, hostingConfiguration: params.hostingConfiguration, }); - new PipelineInfrastructure(this, "PipelineInfrastructure", { + // Set properties from hosting infrastructure + this.distribution = this.hostingInfrastructure.distribution; + this.hostingBucket = this.hostingInfrastructure.hostingBucket; + this.distributionDomainName = this.distribution.distributionDomainName; + this.distributionUrl = `https://${this.distributionDomainName}`; + + // Create Pipeline Infrastructure + this.pipelineInfrastructure = new PipelineInfrastructure(this, "PipelineInfrastructure", { hostingConfiguration: params.hostingConfiguration, connectionArn: params.connectionArn, - kvsArn: uriStore.keyValueStoreArn, - hostingBucket: hostingInfrastructure.hostingBucket, - changeUri: changeUri, + kvsArn: this.uriStore.keyValueStoreArn, + hostingBucket: this.hostingBucket, + changeUri: this.changeUriFunction, buildFilePath: params.buildFilePath, }); } -} +} \ No newline at end of file From 7794c193adc85b2b8b861cf0dc67e4e9f9efc77b Mon Sep 17 00:00:00 2001 From: Corneliu Croitoru Date: Thu, 24 Oct 2024 15:57:21 +0200 Subject: [PATCH 2/3] feat: expose CloudFront distribution as property (#18) - Add public readonly distribution property to Hosting construct - Expose CloudFront distribution from HostingInfrastructure - Update documentation with new property --- lib/hosting.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/hosting.ts b/lib/hosting.ts index 3fdaa29..91a9f60 100644 --- a/lib/hosting.ts +++ b/lib/hosting.ts @@ -13,8 +13,7 @@ import { Aws, aws_cloudfront as cloudfront, - aws_s3 as s3, - aws_cloudfront_origins as origins, + aws_s3 as s3 } from "aws-cdk-lib"; import * as path from "path"; import * as fs from "fs"; From a828dd6d4c88310f542577f0141b3d05cd201b77 Mon Sep 17 00:00:00 2001 From: Corneliu Croitoru Date: Thu, 24 Oct 2024 16:02:43 +0200 Subject: [PATCH 3/3] feat: expose CloudFront distribution as property (#18) - Add public readonly distribution property to Hosting construct - Expose CloudFront distribution from HostingInfrastructure - Update documentation with new property --- lib/hosting.ts | 2 +- lib/hosting_infrastructure.ts | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/hosting.ts b/lib/hosting.ts index 91a9f60..f97adb0 100644 --- a/lib/hosting.ts +++ b/lib/hosting.ts @@ -50,7 +50,7 @@ export class Hosting extends Construct { /** * The S3 Bucket used for hosting the content. */ - public readonly hostingBucket: s3.Bucket; + public readonly hostingBucket: s3.IBucket; /** * The CloudFront Function used for URI manipulation. diff --git a/lib/hosting_infrastructure.ts b/lib/hosting_infrastructure.ts index a792cf0..d30853f 100644 --- a/lib/hosting_infrastructure.ts +++ b/lib/hosting_infrastructure.ts @@ -47,7 +47,9 @@ interface IConfigProps { export class HostingInfrastructure extends Construct { + public readonly hostingBucket: IBucket; + public readonly distribution: cloudfront.Distribution; @@ -77,7 +79,7 @@ export class HostingInfrastructure extends Construct { }, ]); */ - const hostingBucket = new s3.Bucket(this, "HostingBucket", { + this.hostingBucket = new s3.Bucket(this, "HostingBucket", { versioned: false, ...(s3Logs ? { serverAccessLogsBucket: s3Logs } : {}), enforceSSL: true, @@ -90,9 +92,8 @@ export class HostingInfrastructure extends Construct { }), }); - this.hostingBucket = hostingBucket; - const s3origin = new origins.S3Origin(hostingBucket); + const s3origin = new origins.S3Origin(this.hostingBucket); const myResponseHeadersPolicy = new cloudfront.ResponseHeadersPolicy( this, @@ -246,7 +247,7 @@ export class HostingInfrastructure extends Construct { - const distribution = new cloudfront.Distribution(this, "Distribution", { + this.distribution = new cloudfront.Distribution(this, "Distribution", { comment: "Static hosting - " + Aws.STACK_NAME, defaultRootObject: "index.html", httpVersion: cloudfront.HttpVersion.HTTP2_AND_3, @@ -279,7 +280,7 @@ export class HostingInfrastructure extends Construct { : {}), }); - NagSuppressions.addResourceSuppressions(distribution, [ + NagSuppressions.addResourceSuppressions(this.distribution, [ { id: "AwsSolutions-CFR4", reason: @@ -290,7 +291,7 @@ export class HostingInfrastructure extends Construct { //OAC is not implemented in CDK so tweaking is required:eplace OAI par OAC //https://github.com/aws/aws-cdk/issues/21771 - const cfnDistribution = distribution.node.defaultChild as CfnDistribution; + const cfnDistribution = this.distribution.node.defaultChild as CfnDistribution; cfnDistribution.addOverride( "Properties.DistributionConfig.Origins.0.S3OriginConfig.OriginAccessIdentity", "" @@ -300,7 +301,7 @@ export class HostingInfrastructure extends Construct { oac.getAtt("Id") ); - const comS3PolicyOverride = hostingBucket.node.findChild("Policy").node + const comS3PolicyOverride = this.hostingBucket.node.findChild("Policy").node .defaultChild as CfnBucketPolicy; const statement = comS3PolicyOverride.policyDocument.statements[1]; if (statement["_principal"] && statement["_principal"].CanonicalUser) { @@ -318,14 +319,14 @@ export class HostingInfrastructure extends Construct { service: "cloudfront", region: "", resource: "distribution", - resourceName: distribution.distributionId, + resourceName: this.distribution.distributionId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME, }), }, } ); - const s3OriginNode = distribution.node + const s3OriginNode = this.distribution.node .findAll() //.filter((child) => child.node.id === "S3Origin"); .filter((child) => child.node.id === "S3Origin"); @@ -333,7 +334,7 @@ export class HostingInfrastructure extends Construct { //End of tweaking for OAC is not implemented in CDK so tweaking is required new CfnOutput(this, "DomainName", { - value: "https://" + distribution.domainName, + value: "https://" + this.distribution.domainName, }); const stackName = calculateMainStackName(params.hostingConfiguration); @@ -341,7 +342,7 @@ export class HostingInfrastructure extends Construct { new ssm.StringParameter(this, 'SSMConnectionRegion', { parameterName: '/' + stackName + '/' + SSM_DOMAIN_STR, - stringValue: distribution.domainName, + stringValue: this.distribution.domainName, });