Open1

CDKでNextjsをSSGしたやつホスティング

watanywatany
import * as cdk from "aws-cdk-lib";
import * as acm from "aws-cdk-lib/aws-certificatemanager";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as iam from "aws-cdk-lib/aws-iam";
import * as route53 from "aws-cdk-lib/aws-route53";
import * as route53Targets from "aws-cdk-lib/aws-route53-targets";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as s3deploy from "aws-cdk-lib/aws-s3-deployment";

import { Construct } from "constructs";

export class CdnStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const originDomainName = "example.com";
    const domainName = `hello.${originDomainName}`;

    const myHostedZone = route53.HostedZone.fromLookup(this, "MyZone", {
      domainName: originDomainName,
    });

    const myCertificate = new acm.Certificate(this, "mySiteCert", {
      domainName,
      validation: acm.CertificateValidation.fromDns(myHostedZone),
    });
    const accessLogsBucket = new s3.Bucket(this, "AccessLogsBucket", {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });

    const myBucket = new s3.Bucket(this, "MyBucket", {
      serverAccessLogsBucket: accessLogsBucket,
      serverAccessLogsPrefix: "logs",
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });
    const cloudfrontOai = new cloudfront.OriginAccessIdentity(
      this,
      "CloudFrontOAI",
    );

    // 静的ホスティング用S3バケットに対して必要なアクセスポリシーを作成
    const bucketPolicy = new s3.BucketPolicy(this, "WebsiteBucketPolicy", {
      bucket: myBucket,
    });
    bucketPolicy.document.addStatements(
      new iam.PolicyStatement({
        actions: ["s3:GetObject"],
        effect: iam.Effect.ALLOW,
        principals: [
          new iam.CanonicalUserPrincipal(
            cloudfrontOai.cloudFrontOriginAccessIdentityS3CanonicalUserId,
          ),
        ],
        resources: [`${myBucket.bucketArn}/*`],
      }),
    );

    const script: string = `
  function handler(event) {
    var request = event.request;
    var uri = request.uri;

    // URIが '/app' または '/web' で始まる場合、何も変更せずにそのままリクエストを処理する
    if (uri.startsWith('/app') || uri.startsWith('/web')) {
        return request;
    }

    // URIが '/' の場合、'/web/index.html'にリダイレクトする
    if (uri === '/') {
        request.uri = '/web/index.html';
    } else {
        // それ以外のURIには '/web' をプレフィックスとして追加する
        request.uri = '/web' + uri;
    }

    return request;
}
`;

    const cfFunction = new cloudfront.Function(this, "Function", {
      code: cloudfront.FunctionCode.fromInline(script),
    });
    const distribution = new cloudfront.Distribution(this, "myDist", {
      defaultBehavior: {
        origin: new origins.S3Origin(myBucket),
        functionAssociations: [
          {
            function: cfFunction,
            eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
          },
        ],
      },
      domainNames: [`${domainName}`],
      certificate: myCertificate,
    });
    new s3deploy.BucketDeployment(this, "DeployWebsite", {
      sources: [s3deploy.Source.asset("next/out")],
      destinationBucket: myBucket,
      destinationKeyPrefix: "web", // optional prefix in destination bucket
    });
    new route53.ARecord(this, "cdnRecord", {
      zone: myHostedZone,
      target: route53.RecordTarget.fromAlias(
        new route53Targets.CloudFrontTarget(distribution),
      ),
      recordName: domainName,
    });
  }
}