Sharing Secret Environment Variables with Google Cloud Build and Angular Universal
Tags: angular, github, googlecloud, cloudrun
Published: 2021-12-25 23:18:23

If you deploy your Angular Application to a Google Cloud Run Container, you should do it through Github Actions and Google Cloud Build. This is what creates the CI/CD (Continuous Integration and Continuous Delivery) deployments easily.

Obviously this makes more sense for Angular Universal than Angular, since you need a node server or container to run the former.

## Local Version

Angular Universal is not made to use the `dotenv` package, despite what articles tell you:
- https://javascript.plainenglish.io/setup-dotenv-to-access-environment-variables-in-angular-9-f06c6ffb86c0
- https://ferie.medium.com/how-to-pass-environment-variables-at-building-time-in-an-angular-application-using-env-files-4ae1a80383c

**Bonus Tip** - Right Click - Open in New Private Window on all these medium articles that won't display... that is the reason I love dev.to!

Getting it to work is a pain in the butt. So, why not just use simple JSON!?

1. Create `environment.prod.json` and `environment.json` in your `src/environments` folder
2. Ignore the json files:

**.gitignore**
```typescript
# Config Files
/src/environments/*.json
```

3\. Update your `environment.ts` files
```typescript
import * as firebase_env from './environment.prod.json';

export const environment = {
  production: true,
  firebase: firebase_env
};
```
4\. Add the appropriate JSON data to the `environment.prod.json` file(s).

```typescript
{
  "key": "value",
  ...
}
```
Notice you need to have quotes in the key.

5\. Enable JSON imports

Edit `tsconfig.json` and add 
```javascript
{
  "compilerOptions": {
    ...
    "resolveJsonModule": true,
    ...
}
```



## Deployment

This post is not about Deployment itself. You need to know the basics of Cloud Run, Cloud Container Registry, Github Actions, and Cloud Build.

That being said, you don't have to understand much but how to enable them.

Here are a few links for that:
- https://fireship.io/courses/angular/ssr-deploy-cloud-run/
- https://towardsdatascience.com/deploy-to-google-cloud-run-using-github-actions-590ecf957af0
- https://javascript.plainenglish.io/your-guide-for-deploying-an-angular-app-on-google-cloud-aint-complete-f6d5998332fb

## Cloud Build

Cloud Build pretty much does everything for you. You can get Github Actions working through Github, but it is much easier to go through Cloud Build.

Basically go to [Cloud Build](https://console.cloud.google.com/cloud-build/), make sure it is enabled, click Setup Build Trigger, name it, and chose your repository. You will be prompted to login to Github, allow access etc.

You may need to change the `main` branch to `master` depending on your setup.

## YAML File

You can create your own `cloudbuild.yaml`, but you don't have to. It is done automatically for you once you connect your repository.

![Cloud Build](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z35ymiuv1y1wdkvk8qbq.png)

Once you have all this configured, click the `Open Editor` button.

There, you can modify your inline YAML file. Now add the `build-arg` to the build step like so:

```typescript
steps:
  - name: gcr.io/cloud-builders/docker
    args:
      - build
      - '--build-arg'
      - _FIREBASE=$_FIREBASE
      - '--no-cache'
      - '-t'
      - '$_GCR_HOSTNAME/$PROJECT_ID/$REPO_NAME/$_SERVICE_NAME:$COMMIT_SHA'
      - .
      - '-f'
      - Dockerfile
    id: Build
```

In this case, I am adding the `_FIREBASE` variable which will be passed to the docker file.

You may also need to add `timeout: 1600s` to the bottom of the file if you believe the build will take more than 10 minutes. Angular Builds can take a while due to all the optimizations etc.

Now, click DONE

![Substitution Variables](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zgtfbe2jxaf9wsqtdg7f.png)

Add your Secret Variable as you want. Since we are using JSON, make sure the key is wrapped in quotes.

## Dockerfile

Finally, use this Dockerfile if you use Angular Universal

```go
FROM node:14-slim
ARG _FIREBASE
WORKDIR /usr/src/app
COPY . .
RUN npm i
RUN npm audit fix
RUN echo "$_FIREBASE" >> src/environments/environment.prod.json 
RUN npm run build:ssr
CMD ["npm", "run", "serve:ssr"]
```

Basically, it creates the environment file from the substitution variable. You need to add the `ARG` for this to work. You can do any variables you like.

I have not seen any other tutorial do it quite like this, so this is a unique approach, and much simpler IMHO than using .env files.

## Client API Keys? Why do we need to do all this?

If you read the correct answer on [stackoverflow](https://stackoverflow.com/questions/37482366/is-it-safe-to-expose-firebase-apikey-to-the-public#:~:text=EXPOSURE%20OF%20API%20KEYS%20ISN,project%20keys%20to%20domains%20%2F%20IP's.), officially it explains that you don't need to do any of this. Your client API key is going to be viewable, or gettable, as your frontend source is not secure. You can run firebase commands in console.log, and do way more [see Fireship Video](https://www.youtube.com/watch?v=b7PUm7LmAOw). If you use Supabase, Firebase, or any API with a client key, it is hackable. Firebase should be secured with App Check and Security Rules, Supabase with Row Level Policies, etc, etc.

That being said, I still don't want someone easily copying and pasting my configuration code into their project on every fork or clone. Another layer of security is always good.

If your project is a backend project, this is especially good.

- More [Sveltekit](https://dev.to/jdgamble555/sharing-secret-environment-variables-with-sveltekit-and-vercel-4fie) and Vercel Sharing Varaibles

- More [Angular Universal](https://dev.to/jdgamble555/deploy-angular-universal-app-to-firebase-functions-49mm) setup

If you have a professional APP, you may want to look into Cloud Run CDN, which is Vercel's Edge equivalent.  See [here](https://cloud.google.com/cdn/docs/setting-up-cdn-with-serverless).

Hope this helps someone,

J