Injecting Angular Environment Variables at Build Time
Tags: aws, angular, devops, docker
Published: 2021-08-15 15:38:38

In this article, I assume that you are containerizing your Angular application and also have a CI/CD process in place. While the example in the article is also AWS specific, you can use the same principle across other cloud services.

By default, Angular injects the environment variable at the  application build time.

![deployment image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3m1knateh9syigwe29o2.png)

The above diagram depicts a regular flow for our application deployment. 
1. Our frontend app. inclusive of the environment variables 
   is pushed to the repo
2. Our build process picks it up, builds and deploy to our 
   server. 

While the above works very well, there are very unique cases where you would be required not to push your environment files to your application repository. If this happens, knowing that angular injects the environment variables at build time, we would need to find a way to inject the environment files during the build process. 

Below are unique reasons why you might be required not to deploy your environment files to your repository

1.Extra level of security: 
    Some companies have policies which prevent them from pushing applications environment variables to their repository whether private repos or not. Although it is worthy to note that the safest way to keep your secret is not to put them in your frontend applications. So, on no account should you place any secret on your Frontend application whether as an environment variable or inside the application.

2.DevOps flow: 
    There are situations where the parameters of your applications can vary based on different environments, while you may know the specifics for an environment file. e.g the dev environment, the production credentials may be required to be added to your application by your devOps team or your client. In order to avoid them making changes to your application or going through a PR flow (which could be necessary depending on your policies). You would want to isolate the environment file from your application.   

3.Multiple instances with dynamic variables: 
    There are scenarios where you would have a single repository for your application but multiple deployment instances that require different configuration files (environment variable). These types of variables could be styles, images, currency type, app settings, base url and many other variables that differ based on each instance. Below is a depiction.

![deployment image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8igd6q85gtukrugi2hw.png)

In the Image above, we have a single application repository that is deployed to multiple instances.

If we follow the flow of having our environment variables in the repository, there would be no way to set different configurations for each of the various instances except we implement the logic of setting variables in the application level based on users, which wouldn't be a 100% perfect solution if we need some configuration on application startup.

In my experience, I was faced with the three (3) situations above. 

## My Solution

1.    Add the environment path to your `.gitignore`. this ensures that you do not push your environment files to the repository.

2.    Create a private repo on AWS S3. 
    a. you can call it (Frontend Env. Variables)
    b. Create sub folders for each of your application
    c. upload the different environment files. eg (dev, staging and prod)
(In the case of multiple instances with dynamic variables, this should be replicated in each of the environments.

3.    Ensure that our build process has the permission to read from the s3 repository

4.    Modify the buildSpec file to copy the  file from the s3 repository to the application root folder during the build process.

### s3 folder structure 

![deployment image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mgzu0j3fjp5i37xl7eh7.png)

####Buildspec file
```
 build:
    commands:
      - echo Build started on `date`
      - printenv
      - aws s3 cp s3://frontend-env-variable/payment- application/ src/ --recursive
      - docker build -t payment-app --build-arg IMAGE_TAG=$IMAGE_TAG .
      - docker images -a
      - echo Building the Docker image...
      - docker tag $APP_NAME:latest $AWS_ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
      - docker images -a
      # - command
  post_build:
```

Before the docker file is invoked, We already have the environment file copied from the s3 folder to the app folder as seen above in our buildspec file. 

For the multi instance scenario with different environment variables in different cloud instances,

1. Create  separate buildspec files for each environment in your application root. eg. (instance1.buildspec.yml, instance2.buildspec.yml) and each of them will have the reference to the corresponding s3 path.

2. In your AWS CDK or codebuild (or whichever build process you have in place, specify the name of the buildspec file for the specific environment

![deployment image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g2h7td0ykmdnwu9bgmam.png)

With the above process, you can successfully 
1. Deploy your application to the repo without your environment files

2. Have your devOps, client or anyone for each of your business instances that needs to update the environment variables do so.



Just an extra info on what the docker file look file

```
#building angular App
FROM node:14.0.0 as node

WORKDIR /app
COPY package.json /app/
RUN npm install
COPY ./ /app/
ARG IMAGE_TAG=dev
RUN npm run build -- --prod --configuration $IMAGE_TAG

# building nginx
FROM public.ecr.aws/nginx/nginx:1.20-alpine

# FROM nginx:1.12.2-alpine
COPY --from=node /app/dist/payment-app /usr/share/nginx/html
COPY ./nginx-custom.conf /etc/nginx/conf.d/default.conf
```


Conclusion

I believe that there are other fun ways to inject your environment variables at build time. While the above works perfectly for me, I am open to know if you have a differ approach in solving this. 

Remember that this solution is not cloud environment dependent. 



### Do not add any secret to your frontend application