Deploy S3 hosted application using CodePipeline
Tags: codepipeline, s3, deply, action
Published: 2023-03-14 10:26:24

A while ago I wrote how you can host your own [single page application on S3](https://xebia.com/blog/hosting-a-single-page-application-or-website-on-s3/). But how will you get your application on the S3 bucket? There are a couple of options here, you could upload it by hand? But we both know that is not the real solution here. No we want to automate this process! In this blog post I will show you how you can automate this using [AWS CodePipeline](https://aws.amazon.com/codepipeline/).

## The Pipeline

AWS Codepipeline uses different stages, I often use `Source`, `Build` and `Deploy` stages. In some cases I split the Deploy stage into a Development, Testing, Acceptance and Production deployment (also known as DTAP). If you want to know more about how you can set this up you can read my [building applications with pipelines](https://xebia.com/blog/building-applications-with-pipelines/) blog. But in the end it is up to you and what makes sense to your use-case.

When you deploy your infrastructure using [CloudFormation.](https://aws.amazon.com/cloudformation/) You can make use of the outputs within the CodePipeline. Another option is to use a naming convention. I like to use the outputs as it removes the need to define a name upfront. Making it more robust when you re-use snippets or deploy your infrastructure more than once.

```yaml
Outputs:
  ApplicationBucketName:
    Value: !Ref ApplicationBucket
```

The next thing you need to define is a namespace on the action that deploys your infrastructure.

```yaml
- Name: ExecuteChangeSet
  Region: eu-west-1
  RunOrder: 2
  RoleArn: !Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role
  Namespace: DevelopmentVariables
  ActionTypeId:
      Category: Deploy
      Owner: AWS
      Provider: CloudFormation
      Version: "1"
  Configuration:
      ActionMode: CHANGE_SET_EXECUTE
      RoleArn: !Sub arn:aws:iam::${DevelopmentAccountId}:role/cloudformation-execution-role
      StackName: !Sub ${ProjectName}-development
      ChangeSetName: !Sub ${ProjectName}-development-ChangeSet
```

By default CodePipeline will load the outputs in the given namespace. In this example that is `DevelopmentVariables`, so the `ApplicationBucketName` is available as: `#{DevelopmentVariables.ApplicationBucketName}`.

## Deploy to S3

AWS provides a [S3 Deploy action](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-S3Deploy.html) you can use this action to deploy an artifact in your pipeline to S3\. You can create this artifact in a  CodeBuild Project or you can use the source artifact. 

I am using a cross account deployment strategy. For this reason I need to allow my _cross-account-role_ to allow uploads to the S3 buckets. I am using a BucketPolicy for this:

```yaml
- Sid: AllowPipelineToUpload
  Effect: Allow
  Action: s3:PutObject
  Principal:
    AWS: !Sub arn:aws:iam::${AWS::AccountId}:role/cross-account-role
  Resource: !Sub ${ApplicationBucket.Arn}/*
```

Note that the role and the bucket are living in the same account. The pipeline lives in my build/deployment account. So in the pipeline we need to configure the upload to S3:

```yaml
- Name: Ireland-Uploadapplication
  Region: eu-west-1
  RunOrder: 3
  RoleArn: !Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role
  InputArtifacts:
    - Name: application
  ActionTypeId:
    Category: Deploy
    Owner: AWS
    Provider: S3
    Version: "1"
  Configuration:
    BucketName: "#{DevelopmentVariables.ApplicationBucketName}"
    Extract: true
    CacheControl: max-age=0, no-cache, no-store, must-revalidate
```

In this example I will use my artifact called _application_ and extract the content in the S3 bucket. It will assume the role that we specify as `RoleArn` to perform the upload. I will also set the `CacheControl` so that [CloudFront](https://aws.amazon.com/cloudfront/) knows that it needs to serve the new content.

## Conclusion

It is easy to use the S3 Deploy action to upload your content to a S3 bucket. It removes the need of using a CodeBuild project to upload the content. This will reduce cost and complexity, by not maintaining an extra CodeBuild project.