Static site hosting with AWS CDK
import { Stack, Construct, StackProps, CfnOutput } from "@aws-cdk/core"; | |
import { | |
HostedZone, | |
IHostedZone, | |
ARecord, | |
RecordTarget, | |
} from "@aws-cdk/aws-route53"; | |
import { CloudFrontTarget } from "@aws-cdk/aws-route53-targets"; | |
import { BucketProps, BlockPublicAccess, Bucket } from "@aws-cdk/aws-s3"; | |
import { | |
OriginAccessIdentity, | |
SourceConfiguration, | |
CloudFrontWebDistribution, | |
IDistribution, | |
CfnDistribution, | |
ViewerCertificate, | |
} from "@aws-cdk/aws-cloudfront"; | |
import { | |
DnsValidatedCertificate, | |
ICertificate, | |
CertificateValidation, | |
} from "@aws-cdk/aws-certificatemanager"; | |
import { HttpsRedirect } from "@aws-cdk/aws-route53-patterns"; | |
interface IHostingStackProps extends StackProps { | |
bucketName: string; | |
url: string; | |
} | |
export class HostingStack extends Stack { | |
constructor(scope: Construct, id: string, props: IHostingStackProps) { | |
super(scope, id, props); | |
const bucket = this.getS3Bucket(props.bucketName); | |
const { certificate, hostedZone } = this.getRoute53HostedZone(props.url); | |
const { distribution } = this.getCloudFrontDistribution( | |
bucket, | |
certificate, | |
props.url | |
); | |
this.getRoute53Records(distribution, hostedZone, props.url); | |
new CfnOutput(this, "URL", { | |
description: "The URL of the site", | |
value: hostedZone.zoneName, | |
}); | |
} | |
private getS3Bucket(bucketName: string) { | |
const bucketConfig: BucketProps = { | |
bucketName, | |
websiteIndexDocument: "index.html", | |
websiteErrorDocument: "index.html", | |
publicReadAccess: false, | |
blockPublicAccess: BlockPublicAccess.BLOCK_ALL, | |
}; | |
const bucket = new Bucket(this, "WebsiteBucket", bucketConfig); | |
return bucket; | |
} | |
private getCloudFrontDistribution( | |
bucket: Bucket, | |
certificate: ICertificate, | |
url: string | |
) { | |
const oai = new OriginAccessIdentity(this, "WebsiteOAI"); | |
bucket.grantRead(oai); | |
const originConfig: SourceConfiguration = { | |
s3OriginSource: { | |
s3BucketSource: bucket, | |
originAccessIdentity: oai, | |
}, | |
behaviors: [{ isDefaultBehavior: true }], | |
}; | |
const errorConfigurations: CfnDistribution.CustomErrorResponseProperty[] = [ | |
{ | |
errorCode: 403, | |
responsePagePath: "/", | |
responseCode: 200, | |
}, | |
{ | |
errorCode: 404, | |
responsePagePath: "/index.html", | |
responseCode: 200, | |
}, | |
]; | |
const viewerCertificate = ViewerCertificate.fromAcmCertificate( | |
certificate, | |
{ | |
aliases: [url], | |
} | |
); | |
const distribution = new CloudFrontWebDistribution( | |
this, | |
"WebsiteDistribution", | |
{ | |
originConfigs: [originConfig], | |
errorConfigurations, | |
viewerCertificate, | |
} | |
); | |
return { oai, distribution }; | |
} | |
private getRoute53HostedZone(url: string) { | |
const hostedZone = HostedZone.fromLookup(this, "HostedZone", { | |
domainName: url, | |
}); | |
const certificate = new DnsValidatedCertificate(this, "Certificate", { | |
hostedZone, | |
domainName: url, | |
region: "us-east-1", | |
validation: CertificateValidation.fromDns(hostedZone), | |
}); | |
return { hostedZone, certificate }; | |
} | |
private getRoute53Records( | |
distribution: IDistribution, | |
hostedZone: IHostedZone, | |
url: string | |
) { | |
new ARecord(this, "Alias", { | |
zone: hostedZone, | |
recordName: url, | |
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)), | |
}); | |
new HttpsRedirect(this, "Redirect", { | |
zone: hostedZone, | |
recordNames: [`www.${url}`], | |
targetDomain: url, | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment