🗂

CloudFront+S3構成の静的ファイル配信をCDKだけで構築する

2023/07/22に公開

背景

単純なHTMLファイルや、画像ファイル、JSONファイルなどを公開したい時ってありますよね〜(唐突

そんな時にS3バケットをPUBLIC設定にして、ファイルをアップロードすれば公開することは可能ですが、S3バケットのPUBLIC設定はセキュリティ観点で非推奨だったりしますし、カスタムドメインをつけたいケースも多いかと思います。

構築するAWSリソース、アーキテクチャはこのようなものです。

CDKで実装

CDKのプロジェクト全体はこちらにあります。
https://github.com/kodai305/cdk-staticfile-deploy

S3バケット作成

ここは特に特別なことはしていません。

    const bucket = new s3.Bucket(this, 'S3Bucket', {
      bucketName: 'cdk-staticfile-deploy-202307',
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });

※RemovelPolicyはこちらが参考になります。
https://dev.classmethod.jp/articles/aws-cdk-s3-delete-policy/

CloudFront作成

CloudFrontでは、Origin Access Identity (OAI)という機能が提供されていましたが、セキュリティ面の機能強化や細かいポリシー設定ができる Origin Access Control (OAC)が2022年8月に発表されてからは、OACの利用が推奨されています。

以下のブログでわかりやすく解説されています。
https://dev.classmethod.jp/articles/amazon-cloudfront-origin-access-control/

CloudFrontのOAC機能をCDKで実装していけばいいのですが、このissueにあるように、OACのL2コンストラクタはまだ実装されていません。
https://github.com/aws/aws-cdk/issues/21771

そのため、このissueを参考にして、OAC版CloudFrontを実装していきます。

    // OAC
    const cfnOriginAccessControl = new cloudfront.CfnOriginAccessControl(this, 'OriginAccessControl', {
      originAccessControlConfig: {
          name: 'OriginAccessControlForContentsBucket',
          originAccessControlOriginType: 's3',
          signingBehavior: 'always',
          signingProtocol: 'sigv4',
          description: 'Access Control',
      },
    }); 
    
    // Cloudfront(distribution)
    const origin = new cloudfront_origins.S3Origin(bucket);
    const distribution = new cloudfront.Distribution(this, 'DistributionId', {
      defaultRootObject: 'index.html',
      defaultBehavior: {
          origin: origin,
      },
    });

    // Policy 
    const bucketPolicyStatement = new iam.PolicyStatement({
      actions: ['s3:GetObject'],
      effect: iam.Effect.ALLOW,
      principals: [
          new iam.ServicePrincipal('cloudfront.amazonaws.com')
      ],
      resources: [`${bucket.bucketArn}/*`]
    });
    bucketPolicyStatement.addCondition('StringEquals', {
      'AWS:SourceArn': `arn:aws:cloudfront::${cdk.Stack.of(this).account}:distribution/${distribution.distributionId}`
    });
    bucket.addToResourcePolicy(bucketPolicyStatement);
  
    const cfnDistribution = distribution.node.defaultChild as cloudfront.CfnDistribution;
    cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.OriginAccessControlId', cfnOriginAccessControl.getAtt('Id'));
    cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.DomainName', bucket.bucketRegionalDomainName);
    cfnDistribution.addOverride('Properties.DistributionConfig.Origins.0.S3OriginConfig.OriginAccessIdentity', "");
    cfnDistribution.addPropertyDeletionOverride('DistributionConfig.Origins.0.CustomOriginConfig');

S3へ配信するファイルをアップロード

BucketDeploymentを使うと、sourcesで指定したディレクトリ以下のファイルをdestinationBucketにアップロードします。こういったメソッドがあるので、AWSリソースだけCDKで定義してファイルはCLIやスクリプトでアップロードというような歯痒い状態にならず、CDKだけで全てを完結させることができます。

    new s3_deployment.BucketDeployment(this, 'S3Deployment', {
      sources: [s3_deployment.Source.asset('./static_files/')],
      destinationBucket: bucket,
      distribution,
      distributionPaths: ['/*']
    });

まとめ

基礎的な内容に留まってしましましたが、静的ファイル配信をCDKだけで行うことができるイメージが湧いたり、実装する際の参考になれば幸いです。

次回、「CloudFront+S3での静的ファイル配信にCloudFront Functionsでアレコレする」を書きたいと思います。

Discussion