DRAFT この記事は下書きです。本番環境では公開されません。
博士の視点
【できる】Amazon S3 で作るサーバーレスウェブサイト (2)
土居 意弘
サーバーレスクラウド中小企業スタートアップAWS
サーバーレスでウェブサーバーを作ってみます。
Primo in altis pelle alumnae
Lorem markdownum obvius in seque opus, est bicorni forte; laeva. Iurant patria beatam semel communis et atra qua fugit, solet invicti cui inter patulas regibus remolliat volumina sorori? Quidem miscentem regna interea natura in adligat, aenum onere placere lympha. Sunt tantum intentare exhortatus avidas Scythides lacrimis imitatus prohibent terraeque donec ulterius thalamosque fero comitantibus. Tela cervicem insiluit locis, falsa et umida ulterius digitos excipiunt!
import { Fn, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as route53 from "aws-cdk-lib/aws-route53";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as s3deploy from "aws-cdk-lib/aws-s3-deployment";
import { Construct } from "constructs";
import * as targets from "aws-cdk-lib/aws-route53-targets";
import * as certificatemanager from "aws-cdk-lib/aws-certificatemanager";
import * as iam from "aws-cdk-lib/aws-iam";
import { tagMyConstruct } from "./tagging";
import * as crypto from "crypto";
export class WebsiteStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// Retrieve environment configurations.
const environment = this.node.tryGetContext("environment");
const envParams = this.node.tryGetContext(environment);
// Make temporary variable.
const siteFqdn = `${envParams.subDomainPrefix}.${envParams.rootDomainName}`;
// S3 bucket as a web server.
const websiteBucket = new s3.Bucket(this, "WebsiteBucket", {
// publicReadAccess: true,
websiteIndexDocument: "index.html",
websiteErrorDocument: "404.html",
autoDeleteObjects: true,
removalPolicy: RemovalPolicy.DESTROY,
websiteRoutingRules: [
{
condition: {
httpErrorCodeReturnedEquals: "403",
},
httpRedirectCode: "302",
hostName: siteFqdn,
protocol: s3.RedirectProtocol.HTTPS,
},
],
});
tagMyConstruct(this, websiteBucket);
// Make referer key.
const sha1sum = crypto.createHash("sha1");
sha1sum.update(siteFqdn);
const siteFqdnSha1 = sha1sum.digest("base64");
// Policy to limit access origin.
const websiteBucketPolicyStatement = new iam.PolicyStatement({
sid: `Allow access originating from ${siteFqdn}.`,
principals: [new iam.AnyPrincipal()],
actions: ["s3:GetObject"],
effect: iam.Effect.ALLOW,
resources: [`${websiteBucket.bucketArn}/*`],
conditions: {
StringLike: {
"aws:Referer": siteFqdnSha1,
},
},
});
websiteBucket.addToResourcePolicy(websiteBucketPolicyStatement);
// Make basic auth function if required.
const cfFunctions: cloudfront.FunctionAssociation[] = (() => {
if (envParams.basicAuth) {
// CloudFront Functionリソースの定義
const basicAuthFunction = new cloudfront.Function(
this,
"BasicAuthFunction",
{
functionName: `basic-authentication-${environment}`,
code: cloudfront.FunctionCode.fromFile({
filePath: "../lambda/basic-auth.ts",
}),
}
);
return [
{
eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
function: basicAuthFunction,
},
];
}
return [];
})();
// Certificate
const certificate = certificatemanager.Certificate.fromCertificateArn(
this,
"WebsiteCertificate",
envParams.certificateArn
);
// LogBucket
const logBucketArn = Fn.importValue(envParams.logBucketReference);
const logBucket = s3.Bucket.fromBucketArn(this, "LogBucket", logBucketArn);
// Cloudfront
const distribution = new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: {
origin: new origins.S3Origin(websiteBucket, {
customHeaders: {
Referer: siteFqdnSha1,
},
}),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
functionAssociations: cfFunctions,
},
certificate,
domainNames: [siteFqdn],
comment: `Samurai Apps Website ${envParams.envName}`,
priceClass: cloudfront.PriceClass.PRICE_CLASS_200,
logBucket,
logFilePrefix: envParams.logPrefix,
});
tagMyConstruct(this, distribution);
// S3 deployment with refreshing cloudfront's cache.
new s3deploy.BucketDeployment(this, "WebsiteDeployment", {
// sources: [s3deploy.Source.asset("../src/public")],
sources: [s3deploy.Source.asset("../src/site/public")],
destinationBucket: websiteBucket,
distribution,
distributionPaths: ["/*"],
});
// DNS configuration.
const zone = route53.HostedZone.fromLookup(this, "HostedZone", {
domainName: envParams.rootDomainName,
});
const aliasRecord = new route53.ARecord(this, "AliasRecord", {
zone,
recordName: envParams.subDomainPrefix,
target: route53.RecordTarget.fromAlias(
new targets.CloudFrontTarget(distribution)
),
});
tagMyConstruct(this, aliasRecord);
}
}
これにて御免!
画像クレジット: UnsplashのTaylor Vickが撮影した写真