React continuous deployment pipeline from Github to S3 with AWS CodePipeline and AWS CodeBuild

0
937
CloudFormation template

Introduction

This CloudFormation template will create a stack for a full deployment pipeline for your React app with AWS CodePipeline and AWS Codedeploy.

The pipeline will react to git push performed to a branch on a Github repository and it will build the React project and upload it to an S3 static website.

The template contains everything you need to setup this deployment pipeline:

  • 2 S3 buckets (one for codepipeline artifacts and one for the static site – configured with static website hosting and the necessary bucket policy)
  • An AWS CodePipeline preconfigured with everything needed to run the pipeline
  • An AWS CodeDeploy build project that will build your React app (if you want just a static html site this can be skipped)
  • 2 IAM Roles (one for AWS CodePipeline and one for AWS CodeBuild) configured with the necessary permissions

If you need to configure this with a custom domain name, with an SSL certificate and CloudFront CDN then check out this template which contains that setup.

Configuration

When you upload the template to CloudFormation and try to create the stack you will be prompted to add the following information:

  1. Branch. This is the branch from your github repo that you want to deploy
  2. Repository owner. This your github user name and the second part of the repo url (bold underlined): https://github.com/majestic-cloud/docs
  3. Repository. This is the project name. This is the last part of your github repo url (bold underlined): https://github.com/majestic-cloud/docs
  4. GithubOAuthToken. This is your github personal access token. To get this sign in to your Github account then click on your user’s icon in the top right part of the screen, then go to Settings -> Developer settings -> Personal access tokens (or you can go directly to this link: https://github.com/settings/tokens) and then click on Generate new token. After you configure the permissions (check the first checkbox named repo that gives access to all the “repo” permissions) paste the obtained token into the CloudFormation console.

Deployment

You deploy this stack with CloudFormation and once your stack is created you can find in the Outputs section the URL of your static website where the React app is being deployed.

The template

AWSTemplateFormatVersion: 2010-09-09
Parameters: 
  Branch:
    Type: String
    Default: "Type here your branch name (usually master or main)"
  RepoOwner:
    Type: String
    Default: "type the github user name"
  Repository:
    Type: String
    Default: "type the github project name"
  GithubOAuthToken:
    Type: String
    Description: "Github access token"
    Default: "type your github access token here"
Resources:
  ReactToS3CodePipeline:
    Type: 'AWS::CodePipeline::Pipeline'
    Properties:
      RoleArn: !GetAtt CodePipeLineRole.Arn
      ArtifactStore:
        Location: !Ref PipelineBucket
        Type: S3
      Stages:
        - 
          Name: Source
          Actions: 
            - 
              Name: SourceAction
              ActionTypeId: 
                Category: Source
                Owner: ThirdParty
                Provider: GitHub
                Version: 1
              OutputArtifacts: 
                - 
                  Name: TheApp
              Configuration:
                Owner: !Ref RepoOwner
                Repo: !Ref Repository
                Branch: main
                OAuthToken: !Ref GithubOAuthToken
        - 
          Name: Build
          Actions: 
            - 
              Name: BuildAction
              ActionTypeId: 
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              InputArtifacts: 
                - 
                  Name: TheApp
              OutputArtifacts: 
                - 
                  Name: TheBuiltApp
              Configuration:
                ProjectName: !Ref CodeBuild
        -
          Name: Deploy
          Actions:
            -
              Name: DeployAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: S3
              InputArtifacts:
                -
                  Name: TheBuiltApp
              RunOrder: 1
              Configuration:
                BucketName: !Ref DeployBucket
                Extract: true
  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - 
            Effect: Allow
            Principal:
              Service:
                - "codebuild.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: /service-role/
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: "2012-10-17"
            Statement: 
              - 
                Effect: Allow
                Action:
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetBucketVersioning"
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                  - "s3:PutObjectVersionAcl"
                  - "s3:DeleteObject"
                Resource: 
                  - !GetAtt PipelineBucket.Arn
                  - !Join ['', [!GetAtt PipelineBucket.Arn, "/*"]]
              - 
                Effect: Allow
                Action:
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetBucketVersioning"
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                  - "s3:PutObjectVersionAcl"
                  - "s3:DeleteObject"
                Resource: 
                  - !GetAtt DeployBucket.Arn
                  - !Join ['', [!GetAtt DeployBucket.Arn, "/*"]]
              -
                Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "cloudfront:CreateInvalidation"
                Resource:
                  - "*"
  CodePipeLineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - 
            Effect: Allow
            Principal:
              Service:
                - "codepipeline.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: "2012-10-17"
            Statement: 
              - 
                Effect: Allow
                Action:
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetBucketVersioning"
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                  - "s3:PutObjectVersionAcl"
                  - "s3:DeleteObject"
                Resource: 
                  - !GetAtt PipelineBucket.Arn
                  - !Join ['', [!GetAtt PipelineBucket.Arn, "/*"]]
              -
                Effect: Allow
                Action:
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:GetBucketVersioning"
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                  - "s3:PutObjectVersionAcl"
                  - "s3:DeleteObject"
                Resource:
                  - !GetAtt DeployBucket.Arn
                  - !Join ['', [!GetAtt DeployBucket.Arn, "/*"]]
              - 
                Effect: Allow  
                Action:
                  - "codebuild:BatchGetBuilds"
                  - "codebuild:StartBuild"
                Resource: "*"
  CodeBuild:
    Type: 'AWS::CodeBuild::Project'
    Properties:
      Name: !Sub ${AWS::StackName}-CodeBuild
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Artifacts:
        Type: CODEPIPELINE
        Name: MyProject
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Type: LINUX_CONTAINER
        Image: "aws/codebuild/amazonlinux2-x86_64-standard:2.0"
      Source:
        Type: CODEPIPELINE
        BuildSpec: !Sub |
          version: 0.1
          phases:
            pre_build:
              commands:
                - echo This will run npm install to install the dependencies...
                - npm install
            build:
              commands:
                - echo Actually run the build process
                - npm run build
          artifacts:
            files:
              - '**/*'
            base-directory: build
  PipelineBucket: 
    Type: 'AWS::S3::Bucket'
    Properties: {}
    DeletionPolicy: Retain
  DeployBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      AccessControl: PublicRead
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
    DeletionPolicy: Retain
  DeployBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement:
          - Sid: PublicReadForGetBucketObjects
            Effect: Allow
            Principal: '*'
            Action: 's3:GetObject'
            Resource: !Join
              - ''
              - - 'arn:aws:s3:::'
                - !Ref DeployBucket
                - /*
      Bucket: !Ref DeployBucket
Outputs:
  Bucket:
    Description: 'Deploy Bucket Name'
    Value: !Ref DeployBucket
  BucketURL:
    Description: 'Deploy bucket (Static website) URL'
    Value: !GetAtt 'DeployBucket.WebsiteURL'

Find the above template on Github: https://github.com/majestic-cloud/react-deployment-pipeline/blob/main/cfn-templates/reactToS3v3_noCloudFront.yaml

Leave A Reply

Please enter your comment!
Please enter your name here