フロントエンド環境をS3にデプロイするCodePipelineをCloudFormationで作成する

2020.04.02

はじめに

フロントエンド環境や静的なサイトをS3 + CloudFrontで配信するというケースも多いかと思います。CodeCommitにホストされたフロントエンドのソースコードをビルドしてS3にデプロイするCodePipelineをCloudFormationで作成してみました。

前提と作成するリソース

デプロイ先のS3とCloudFrontとフロントエンドのソースコードをホストするCodeCommitリポジトリは作成されているものとします。

今回はそれ以外のリソースをCloudFormationで作成します。具体的には以下のリソースをCloudFormationで作成します。

  • CodeBuild
  • CodeBuildのIAMロール
  • ビルド成果物を格納するS3バケット
  • CodePipeline
  • CodePipelineのIAMロール

CloudFormationテンプレート

今回はリポジトリにpushしたタイミングでデプロイが実行されないようにPollForSourceChangesfalseにしています。

AWSTemplateFormatVersion: 2010-09-09
Description: CodePipeline deploy example
Parameters:
  ServiceName:
    Description: deploy-pipeline-example
    Type: String
    Default: deploy-pipeline-example

Resources:
  # ビルド成果物を格納するS3
  ArtifactsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${ServiceName}-artifacts
      LifecycleConfiguration:
        Rules:
          - Id: DeleteRule
            Status: Enabled
            ExpirationInDays: 7
  # CodeBuild
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub ${ServiceName}
      Artifacts:
        Type: CODEPIPELINE
      Source:
        Type: CODEPIPELINE
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:3.0
        PrivilegedMode: false
        Type: LINUX_CONTAINER
      ServiceRole: !GetAtt CodeBuildServiceRole.Arn
  # CodeBuildのIAMロール
  CodeBuildServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName:
        !Sub ${ServiceName}
      Policies:
        - PolicyName: !Sub ${ServiceName}-CodeBuild-ServiceRolePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  - !Sub arn:aws:logs:ap-northeast-1:${AWS::AccountId}:log-group:/aws/codebuild/${ServiceName}:*
                Effect: Allow
              - Action:
                  - codebuild:CreateReportGroup
                  - codebuild:CreateReport
                  - codebuild:UpdateReport
                  - codebuild:BatchPutTestCases
                Resource:
                  - !Sub arn:aws:logs:ap-northeast-1:${AWS::AccountId}:log-group:/aws/codebuild/${ServiceName}:*
                Effect: Allow
              - Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetBucketAcl
                  - s3:GetBucketLocation
                  - s3:ListBucket
                Resource:
                  - '*'
                Effect: Allow
              - Action:
                  - cloudformation:DescribeStackResources
                Resource:
                  - '*'
                Effect: Allow
              - Action:
                  - cloudfront:CreateInvalidation
                Resource:
                  - '*'
                Effect: Allow
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ''
            Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action: sts:AssumeRole
  # CodePipeline
  CodePipelineProject:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub ${ServiceName}
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      Stages:
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeCommit
              OutputArtifacts:
                - Name: SourceArtifact
              # デプロイもとのCodeCommitのリポジトリとブランチ
              Configuration:
                RepositoryName: <デプロイもとのリポジトリ>
                BranchName: master
                # push時に自動でデプロイされないようにする
                PollForSourceChanges: false
              RunOrder: 1
        - Name: Build
          Actions:
            - Name: BuildAction
              InputArtifacts:
                - Name: SourceArtifact
              OutputArtifacts:
                - Name: BuildArtifact
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName:
                  !Ref CodeBuildProject
              RunOrder: 2
      ArtifactStore:
        Type: S3
        Location:
          !Ref ArtifactsBucket
  # CodePipelineのIAMロール
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${ServiceName}-CodePipelineServiceRole
      Policies:
        - PolicyName: !Sub ${ServiceName}-CodePipelineServiceRolePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                - s3:GetObject
                - s3:GetObjectVersion
                - s3:GetBucketVersioning
                Resource:
                  - '*'
                Effect: Allow
              - Action:
                  - s3:PutObject
                Resource:
                  - arn:aws:s3:::codepipeline*
                Effect: Allow
              - Action:
                  - codecommit:CancelUploadArchive
                  - codecommit:GetBranch
                  - codecommit:GetCommit
                  - codecommit:GetUploadArchiveStatus
                  - codecommit:UploadArchive
                Resource:
                  - '*'
                Effect: Allow
              - Action:
                  - codebuild:BatchGetBuilds
                  - codebuild:StartBuild
                Resource:
                  - '*'
                Effect: Allow
              - Action:
                  - codedeploy:CreateDeployment
                  - codedeploy:GetApplication
                  - codedeploy:GetApplicationRevision
                  - codedeploy:GetDeployment
                  - codedeploy:GetDeploymentConfig
                  - codedeploy:RegisterApplicationRevision
                Resource:
                  - '*'
                Effect: Allow
              - Action:
                  - codestar-connections:UseConnection
                Resource:
                  - '*'
                Effect: Allow
              - Action:
                  - s3:*
                Resource:
                  - '*'
                Effect: Allow
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          -
            Effect: Allow
            Principal:
              Service:
                - codepipeline.amazonaws.com
            Action:
              - sts:AssumeRole

buildspec.yaml

次に、デプロイするリポジトリにbuildspec.yamlを作成します。

必要な依存ライブラリのインストールとテストを行った後にnpm run buildで静的ページが生成されるという想定で書いています。ビルド時にCloudFrontのキャッシュをクリアするコマンドも含めています。

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 12
    commands:
      # 静的サイトのビルドやテストなど
      - npm install
      - npm run test
      - npm run build
  build:
    commands:
      # npm run build の結果がdistディレクトリに出力されるという前提
      - aws s3 sync dist s3://<デプロイ先のS3バケット>
      - aws cloudfront create-invalidation --distribution-id <キャッシュクリアするCloudFrontのDestributionID> --paths '/*'

デプロイする

CodePipelineから作成されたパイプラインを選択して「変更をリリースする」を選択します。

デプロイが完了するとS3にファイルがアップロードされていることが確認できます。また、CloudFrontに割り当てているドメインにアクセスすると変更内容が反映されたサイトが表示されます。

参考