Skip to content

Instantly share code, notes, and snippets.

@timveletta
Last active July 10, 2023 21:36
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save timveletta/4e751f4add532ed246642d061ad3fcb9 to your computer and use it in GitHub Desktop.
Save timveletta/4e751f4add532ed246642d061ad3fcb9 to your computer and use it in GitHub Desktop.
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,
});
}
}
new HostingStack(app, "HostingStack", {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
bucketName: "some-bucket-name",
url: "some-url.com",
});
@pgollucci
Copy link

Why no ipv6? or Waf?

@pgollucci
Copy link

What about www -> apex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment