
Overview
I used AWS CDK to create a static site with CloudFront + S3. Additionally, I used CloudFront Functions to add Basic authentication and processing to append index.html to requests that do not include a filename or extension in the URL. I also added a custom domain, so this is a memo of the process.
While somewhat incomplete, the source code is available in the following repository.
The intended use is to prepare an .env file like the following and run cdk deploy.
CERT_ARN=arn:aws:acm:xxxx
RECORD_NAME=aaa.bbb.com
BUCKET_NAME=aaa.bbb.com
REGION=us-east-1
ACCOUNT=yyyy
DOMAIN_NAME=bbb.com
The explanation for each item is as follows.
| Item | Description | Example |
|---|---|---|
| CERT_ARN | Certificate ARN | arn:aws:acm:xxxx |
| RECORD_NAME | Domain name to configure | aaa.bbb.com |
| BUCKET_NAME | S3 bucket name for file storage | aaa.bbb.com |
| REGION | Region name | us-east-1 |
| ACCOUNT | AWS account number (12-digit number) | 123456789012 |
| DOMAIN_NAME | Hosted zone name | bbb.com |
Stack
The following Stack was created.
import {
Stack,
StackProps,
RemovalPolicy,
aws_cloudfront,
aws_cloudfront_origins,
aws_iam,
} from "aws-cdk-lib";
import { Construct } from "constructs";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as iam from "aws-cdk-lib/aws-iam";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as route53 from "aws-cdk-lib/aws-route53";
import * as targets from "aws-cdk-lib/aws-route53-targets";
import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
import * as dotenv from "dotenv";
dotenv.config();
export class StaticBasicStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const recordName = process.env.RECORD_NAME || "";
const domainName = process.env.DOMAIN_NAME || "";
const bucketName = process.env.BUCKET_NAME || "";
const cert = process.env.CERT_ARN || "";
// ホストゾーンIDを取得
const hostedZoneId = route53.HostedZone.fromLookup(this, "HostedZoneId", {
domainName,
});
// S3バケットを作成
const websiteBucket = new s3.Bucket(this, "WebsiteBucket", {
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true,
bucketName,
});
// CloudFront用のOrigin Access Identityを作成
const originAccessIdentity = new cloudfront.OriginAccessIdentity(
this,
"OriginAccessIdentity",
{
comment: `${bucketName}-identity`,
}
);
// S3バケットポリシーを設定
const webSiteBucketPolicyStatement = new iam.PolicyStatement({
actions: ["s3:GetObject"],
effect: iam.Effect.ALLOW,
principals: [
new aws_iam.CanonicalUserPrincipal(
originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId
),
],
resources: [`${websiteBucket.bucketArn}/*`],
});
websiteBucket.addToResourcePolicy(webSiteBucketPolicyStatement);
// CloudFront Functionの設定
const cfFunction = new aws_cloudfront.Function(this, "CloudFrontFunction", {
code: aws_cloudfront.FunctionCode.fromFile({
filePath: "assets/redirect.js",
}),
});
// 証明書を取得
const certificate = Certificate.fromCertificateArn(
this,
"Certificate",
cert
);
// CloudFrontの設定
const distribution = new aws_cloudfront.Distribution(this, "distribution", {
domainNames: [recordName ],
certificate,
comment: `${bucketName}-cloudfront`,
defaultRootObject: "index.html",
defaultBehavior: {
allowedMethods: aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: aws_cloudfront.CachedMethods.CACHE_GET_HEAD,
cachePolicy: aws_cloudfront.CachePolicy.CACHING_OPTIMIZED,
viewerProtocolPolicy:
aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
origin: new aws_cloudfront_origins.S3Origin(websiteBucket, {
originAccessIdentity,
}),
functionAssociations: [
{
function: cfFunction,
eventType: aws_cloudfront.FunctionEventType.VIEWER_REQUEST,
},
],
},
priceClass: aws_cloudfront.PriceClass.PRICE_CLASS_ALL,
});
// Route53レコード設定
new route53.ARecord(this, "Route53RecordSet", {
// ドメイン指定
recordName,
// ホストゾーンID指定
zone: hostedZoneId,
// エイリアスターゲット設定
target: route53.RecordTarget.fromAlias(
new targets.CloudFrontTarget(distribution)
),
});
}
}
Summary
There may be some areas where consideration was insufficient, but I was able to experience the convenience of AWS CDK. We hope some parts of this serve as a reference for others.



Comments
…