ホームメンバー問い合わせ

【できる】サーバーレスで作るウェブサーバー

By munpei
Published in サーバーレス
November 08, 2022
1 min read
【できる】サーバーレスで作るウェブサーバー

サーバーレスでウェブサーバーを作ってみます。

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);
  }
}

これにてご免!

画像クレジット: UnsplashTaylor Vickが撮影した写真

Tags

#サーバーレス#クラウド#中小企業#スタートアップ
Previous Article
【コスト削減】中小企業やスタートアップがサーバーレスを使うべき理由
munpei

munpei

サーバーレス サムライ

Related Posts

【コスト削減】中小企業やスタートアップがサーバーレスを使うべき理由
November 08, 2022
1 min

コンテンツ

イノベーションサーバーレスデジタル化

Social Media