How to Use Environment Variables to Store Secrets in AWS Amplify Backend
Tags: amplify, backend, aws, serverless
Published: 2021-05-19 08:31:01

---
title: How to Use Environment Variables to Store Secrets in AWS Amplify Backend
published: true
date: 
tags: amplify, backend, aws, serverless
canonical_url: https://www.mokkapps.de/blog/how-to-use-environment-variables-to-store-secrets-in-aws-amplify-backend/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bh0lnmw7pu01jn3x69g2.jpg
---

The [twelve-factor app](https://12factor.net/) is a known methodology for building software-as-a-service apps. One factor describes that an application’s configuration should be stored in the environment and not in the code to enforce a strict separation of config from code.

In this article, I want to demonstrate how you can add sensitive and insensitive configuration data to an [AWS Amplify](https://aws.amazon.com/amplify/) backend using environment variables and [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/).

## Table of Contents[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#table-of-contents)

- [Types of configuration](#types-of-configuration)
- [Init Amplify](#init-amplify)
- [Add insensitive configuration data](#add-insensitive-configuration-data)
- [Add sensitive data using AWS Secrets Manager](#add-sensitive-data-using-aws-secrets-manager)
- [Conclusion](#conclusion)

## Types of configuration[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#types-of-configuration)

There exist many types of configuration data, for example:

- timeouts 
- connection strings
- external API configurations like URLs and endpoints
- caching
- hosting configuration for URL, port, or schema
- file system paths
- framework configuration
- libraries configuration
- business logic parameters
- and many more

Apart from the type, configuration data can also be categorized as sensitive or insensitive.

### Sensitive vs Insensitive[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#sensitive-vs-insensitive)

Sensitive configuration data is anything that can be potentially exploited by a third party and therefore must be protected from unauthorized access. Examples of such sensitive data are API keys, usernames, passwords, emails, etc. This data should not be part of your version control. Insensitive configuration data is for example a timeout string for a backend endpoint that can be safely added to the version control.

## Init Amplify[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#init-amplify)

You need an AWS account and the Amplify CLI installed and configured to be able to follow the following steps. [Check out the official docs](https://docs.amplify.aws/start/getting-started/installation/q/integration/js) to set up the prerequisites.

Let’s start by creating a new Amplify project:

```
mkdir amplify-env-config-demo
cd amplify-env-config-demo

▶ amplify init
? Enter a name for the project amplifyenvconfigdemo
? Initialize the project with the above configuration? Yes
? Select the authentication method you want to use: AWS profile
? Please choose the profile you want to use default
```

Next, we can add an API which we will use to add some environment configuration.

```
▶ amplify add api
? Please select from one of the below mentioned services: REST
? Provide a friendly name for your resource to be used as a label for this category in the project: amplifyenvconfigdemoapi
? Provide a path (e.g., /book/{isbn}): /handle
? Choose a Lambda source Create a new Lambda function
? Provide an AWS Lambda function name: amplifyenvconfigdemofunction
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World
? Do you want to configure advanced settings? No
? Do you want to edit the local lambda function now? No
? Restrict API access No
? Do you want to add another path? No
```

We create a simple [Node.js](https://nodejs.org/) lambda function based on the “Hello World” Amplify template. It will provide a REST API with an endpoint at the path `/handle`.

Amplify CLI generated the “Hello World” function code at `amplify/backend/function/amplifyenvconfigdemofunction/src/index.js`:

```js
exports.handler = async event => {
  // TODO implement
  const response = {
    statusCode: 200,
    // Uncomment below to enable CORS requests
    // headers: {
    // "Access-Control-Allow-Origin": "*",
    // "Access-Control-Allow-Headers": "*"
    // },
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};
```

## Add insensitive configuration data[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#add-insensitive-configuration-data)

As we now have a running API, we can add some insensitive configuration data as environment variables to our Amplify backend.

Therefore, we need to modify the `amplify/backend/function/amplifyenvconfigdemofunction/amplifyenvconfigdemofunction-cloudformation-template.json` file. It includes a `Parameters` object where we can add a new environment variable. In our case we want to add a string variable that can be accessed with the key `MyEnvVariableKey` and has the value `my-environment-variable`:

```json
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Lambda Function resource stack creation using Amplify CLI",
  "Parameters" : {
    ...
    "env": {
      "Type": "String"
    },
    "s3Key": {
      "Type": "String"
    },
    "MyEnvVariableKey" : { "Type" : "String", "Default" : "my-environment-variable" } },
  ...
}
```

We also need to modify the `Resources > Environment > Variables` object in this file to be able to map our new environment key to a variable that is attached to the global `process.env` variable and is injected by the Node.js runtime:

```json
{
  "Resources": {
    "Environment": {
      "Variables": {
        "ENV": {
          "Ref": "env"
        },
        "REGION": {
          "Ref": "AWS::Region"
        },
        "MY_ENV_VAR": { "Ref": "MyEnvVariableKey" } }
    }  
  }
}
```

Finally, we need to run `amplify push` to build all of our local backend resources and provision them in the cloud.

Now we can access this variable in our lambda function by accessing the global `process.env` variable:

```js
exports.handler = async event => {
  console.log('MY_ENV_VAR', process.env.MY_ENV_VAR);
  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};
```

## Add sensitive data using AWS Secrets Manager[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#add-sensitive-data-using-aws-secrets-manager)

AWS provides the [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) that helps to “protect secrets needed to access your applications, services, and IT resources”. We will use this service to be able to access sensitive data from our backend.

First, we need to click on “Store a new secret” to create a new secret:

[![Store new secret](https://www.mokkapps.de/static/a17d843a6b4ad7b6f85fe61f34070a61/15ec7/aws-secrets-manager-store-new-secret.jpg "Store new secret")](/static/a17d843a6b4ad7b6f85fe61f34070a61/0f98f/aws-secrets-manager-store-new-secret.jpg)<figcaption>Store new secret</figcaption>

Next, we click “Other type of secret” and enter key and value of our secret in the corresponding “Secret key/value” inputs:

[![Secret type](https://www.mokkapps.de/static/e68f8bbf03db69ff21cefe4b68709cef/15ec7/aws-secrets-manager-type.jpg "Secret type")](/static/e68f8bbf03db69ff21cefe4b68709cef/0f98f/aws-secrets-manager-type.jpg)<figcaption>Secret type</figcaption>

It is possible to add multiple key/value pairs to a secret. A new pair can be added by clicking the ”+ Add row” button.

In the next screen we need to add a name and some other optional information to our secret:

[![Secret name and description](https://www.mokkapps.de/static/015276392d16106d3012812e209405ca/15ec7/aws-secrets-manager-name-and-description.jpg "Secret name and description")](/static/015276392d16106d3012812e209405ca/0f98f/aws-secrets-manager-name-and-description.jpg)<figcaption>Secret name and description</figcaption>

Let’s finish the wizard by skipping all the following screens by clicking the “Next” button.

Now we can open the secret and inspect its values inside the AWS Secrets Manager:

[![Secret details](https://www.mokkapps.de/static/6dff6e152324bf201457d519f1c771d9/15ec7/aws-secrets-manager-secret-details.jpg "Secret details")](/static/6dff6e152324bf201457d519f1c771d9/0f98f/aws-secrets-manager-secret-details.jpg)<figcaption>Secret details</figcaption>

We need to copy the “Secret ARN” value as we need to add a new configuration object in our Cloudformation configuration file `amplifyenvconfigdemofunction-cloudformation-template.json`:

```json
"lambdaexecutionpolicy": {
  "DependsOn": ["LambdaExecutionRole"],
  "Type": "AWS::IAM::Policy",
  "Properties": {
  "PolicyName": "lambda-execution-policy",
  "Roles": [{ "Ref": "LambdaExecutionRole" }],
  "PolicyDocument": {
    "Version": "2012-10-17",
    "Statement": [
        { "Effect": "Allow", "Action": ["secretsmanager:GetSecretValue"], "Resource": { "Fn::Sub": ["arn:aws:secretsmanager:${region}:${account}:secret:key_id", { "region": { "Ref": "AWS::Region" }, "account": { "Ref": "AWS::AccountId" } }] } } ]
    }
  }
}

```

Again, we need to run `amplify push` to build all of our local backend resources and provision them in the cloud.

Now we need to add some JavaScript code to be able to access the secret inside our Node.js lambda function.

First, we need to add the AWS SDK to `amplify/backend/function/amplifyenvconfigdemofunction/src/package.json` by running:

```
npm install aws-sdk
```

Then we can use the `SecretsManager` to get our secret values by passing the “Secret name” which we defined in the AWS Secrets Manager:

```j
const AWS = require('aws-sdk');const secretsManager = new AWS.SecretsManager();
exports.handler = async event => {
  console.log('MY_ENV_VAR', process.env.MY_ENV_VAR);

  const secretData = await secretsManager .getSecretValue({ SecretId: 'dev/demoSecret' }) .promise(); const secretValues = JSON.parse(secretData.SecretString); console.log('DEMO_API_KEY', secretValues.DEMO_API_KEY);
  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};
```

## Conclusion[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#conclusion)

In this article, I demonstrated how you can add sensitive and insensitive environment configuration to your AWS Amplify backend. You can also watch [this video from Nader Dabit](https://www.youtube.com/watch?v=T3vy3ksa4oc) if you prefer a visual tutorial.

If you liked this article, follow me on [Twitter](https://twitter.com/mokkapps) to get notified about new blog posts and more content from me.