Deploy your Angular app automatically in the cloud for free!

The days of manual deployments are long gone, and so are the days of managing a VM or server with Apache to host your Angular app. Deploy your Angular app in to AWS S3 in less than 30 minutes today!

Deploy your Angular app automatically in the cloud for free!

As developers we want our stuff running as quick and easy and cheap as possible. Why else would you keep working on that side project, am I right? So for this the best option I have found is to publish my frontend projects to Amazon's AWS S3. The first year is completely free and after that time it is still dirt cheap. So in this tutorial I will show you how you can deploy your Angular app to AWS S3 automatically with Gitlab CI!

Step 1: The build

First of all we want Gitlab CI to build the project so that it can be deployed in a later stage. For this we need to make a file called .gitlab-ci.yml in your repository. In this file we are going to make a stage to build the project:

build:
  stage: build
  image: node:lts-alpine3.11
 
  cache:
    key: build-cache
    paths:
      - node_modules/

  script: 
    - "npm install -g @angular/cli"
    - "npm ci"
    - "npm run build-prod"

  artifacts:
    paths:
      - dist/

As you can see we are using a node image to build the project. In the script section the real building magic kicks of: First we install the Angular CLI, next we run the CI-friendly npm install just like you would when you get a new project on your machine. And last but not least we run build-prod. If you've worked a lot with Angular CLI before you can tell this does not exist. Normally you would run ng build --prod. So the next thing to do is put that configuration in the package.json file under the scripts section: "build-prod": "ng build --prod",
When you commit the code with these adjustments you should see your code being build by Gitlab CI automatically!

To make the builds a bit more efficient we added a cache with a key. This means all the node modules don't have to be downloaded every time a build runs.

Step 2: Deployment stage

The next step is to add another stage to the .gitlab-ci.yml file that handles the deployment of the Angular app to AWS S3. For this we will be using the AWS CLI. Luckily, the lads and ladina's at Gitlab have provided us with a docker image on which said CLI is installed. So for the image of this stage use registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest. From there only one action is really required, which is copying your files to the bucket. This is done with the cli command aws s3 cp ./dist/my-app/ s3://$BUCKET_NAME/ --recursive. That is it, really! To be fancy I added the bucket as a Gitlab environment, but this is optional.

deploy:
  stage: deploy
  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
  dependencies:
    - build
  script:
    - echo "copying files to s3://$BUCKET_NAME/"
    - aws s3 cp ./dist/my-app/ s3://$BUCKET_NAME/ --recursive
    - echo "Copying files complete"

Notice the $BUCKET_NAME usage in the scripts. That is an environment variable that can be set in Gitlab's variables tab, but we'll get to that later. One other final step is to make the two stages not run simultaneously by adding the stages at the top of the file:

stages:
 - build
 - deploy

Okay, we're almost done with Gitlab. The last thing we need to do is to add the BUCKET_NAME variable, and two others for AWS authentication. You can find this in your project under Settings -> CI/CD -> Variables. Here you can add the following variables:

  1. BUCKET_NAME
  2. AWS_ACCESS_KEY_ID
  3. AWS_SECRET_ACCESS_KEY

These values will be filled in the following steps so don't worry about them for now.

Step 3: Setting up an IAM user with permissions

To make it possible for your Gitlab pipeline to deploy your Angular app to AWS S3 we are going to create a new user that has the permission to put new files in a bucket. If you are familiar with this already feel free to skip this step.

If you haven't already, create an account on AWS. Heads up: I said it was free and it truly is. AWS will ask you for your credit card, and will reserve a small amount (can't remember or find if it was a dollar or a cent), only to verify you. This money will not be actually be written off your card.

AWS can be managed with the online console, a CLI or even an SDK. For now we will use the online console for its ease of use. So login to your account and navigate to IAM. From here you first need to head to Policies and click on Create policy. Click on the tab JSON and add the following:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "darkthemecoder-s3-ci-policy",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "*"
        }
    ]
}

Click on Review policy and add a describing name, I went for the name ci-s3.

Now for the adding a user:

  • Go to Users and click on Add user.
  • Once again, give it a nice name. I chose ci-user.
  • Select Programmatic access and go to the next step.
  • Click on Attach existing policies directly and find your policy (You can use the Filter policies function and set it to customer managed). Select it and finish the wizard.
  • After creating a user you will get a screen with the Access key ID and the Secret access key. Copy those and add them to the corresponding variables in Gitlab. (If you closed this screen on accident: Don't worry, you can generate a new key in the options)

Step 4: Getting S3 set up

Okay you're almost there. Just two more little steps before you can git push your code and see it being built and deployed automatically. These steps are to create a new S3 bucket, and get it configured to host the website. In other words, make the files in your bucket public.

First, let's create this bucket, shall we?

  • Log in to your AWS account and go to https://s3.console.aws.amazon.com/s3.
  • From here you can press the button Create bucket which will open a wizard.
  • Select a region and enter a "DNS compliant" name, which simply excludes some characters that can mess up the domain name. When you got several words just use a - and you're golden.
  • In the next step, Configure options, you can leave everything unchecked as they are optional features that can cost extra.
  • In the Permissions step uncheck the Block all public access toggle, and acknowledge the warning message.
  • Review your settings in the next step and click Create bucket to do exactly what the button says.
Hooray, you've got a bucket! Now, very important: Set the BUCKET_NAME variable in your gitlab settings to the bucketname that you have just created.

To make the bucket a website host follow these steps:

  • Navigate into your bucket and click on the tab Properties.
  • Click on the card Static website hosting to open it.
  • Click on Use this bucket to host a website and make sure that Index document is set to index.html and hit Save.

Okay this is truly the last step: Navigate to Permissions and add the following json to the Bucket Policy button:

{
    "Version": "2012-10-17",
    "Id": "PublicSitePolicy",
    "Statement": [
        {
            "Sid": "WebsiteRead",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::YOUR-BUCKETNAME/*"
        }
    ]
}

You might have noticed the subtle YOUR-BUCKETNAME in there. Change that to your bucketname to make the policy work. Hit that Save button in the top right corner and it is all set! You can now start your pipeline by pushing your code and see your files appearing in the bucket.

To see your actual site, and not just the files; In your bucket go to Properties again and click on Static website hosting to see what the webaddress is of your now public bucket.

Oh oh oh.. also. You can now proudly say that your hobby projects run in the cloud. You're welcome ;)

Conclusion

I hope this tutorial helped you! If you got any feedback and/or questions or feel free to reach out to me on Twitter and of course, don't forget to follow me if you enjoyed this and want to be notified of coming tutorials.

Love, Darkthemecoder