How to save up on AWS Parameter Store with serverless framework
Tags: aws, todayilearned, javascript, tutorial
Published: 2022-05-14 14:50:28

## The Problem:
It is a bad practice and even a security risk to use hard-coded values within our code that reference an API Key or a Database Password. Alternatively, we can have values that can change over time because of a business decision or an unforeseen situation; this will mean that we have to change our code every time a value needs to be changed. For this, we can use the AWS Systems Manager service called Parameter Store.

[Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) is a capability of AWS Systems Manager that provides secure, hierarchical storage for configuration data management and secrets management. We can store data such as passwords, database strings, Amazon Machine Image (AMI) IDs, and license codes as parameter values. We can store values as plain text or encrypted data. 

However, what happens when we have to use several variables? It is difficult to maintain them, but the problem doubles with several development environments. We can easily reach the hard cap limit of 10,000 standard parameters per account and region.

## How do we solve this?
We can save several parameter stores; if we start writing them in JSON format, we can have them organized in a clear and organized way.

Let us start creating a Parameter Store with our JSON values. I will use my first and last name as values for this example.

```
/dev/myapp/INFO >> {"firstName":"David","lastName":"Llerena"}
```
![Parameter Store](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f8zi2qwlkdfjf3xv3rj7.png)
 
Create a Typescript serverless Lambda Project with serverless framework. Let's add the following dependencies:

```json
//package.json
{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "aws-sdk": "2.952.0",
    "@types/aws-lambda": "^8.10.95",
    "serverless": "^3.15.2",
    "serverless-plugin-typescript": "^2.1.2",
    "typescript": "^4.6.3"
  },
  "author": "",
  "license": "ISC"
}
```
Now let's create some javascript code in a file named `helperScript.js`, Create a function that will call SSM service and read a Parameter store which we would like to parse and read each value of the JSON values. [**ResolveConfigurationProperty**](https://www.serverless.com/framework/docs/providers/aws/guide/variables#reference-variables-in-javascript-files) statement will reference the stage and name of our service, declared in our `serverless.yml` file.

```javascript
//helperScript.js
const SSM = require("aws-sdk/clients/ssm");

module.exports.getParameters = async ({ resolveConfigurationProperty }) => {
  const prefix = await resolveConfigurationProperty(["custom", "prefix"]);
  const region = await resolveConfigurationProperty(["provider", "region"]);
  const ssm = new SSM({ region });
  const policy = await ssm
    .getParameter({
      Name: prefix + "/INFO",
      WithDecryption: true,
    })
    .promise();
  const res = JSON.parse(policy.Parameter.Value);
  return {
    firstName: res.firstName,
    lastName: res.lastName

  };
};
```
In our `serverles.yml` file, we will reference this `helperScript.js` to call the variables used in our code under the environment statement. Give the lambda the necessary permissions to access the SSM service. It is recommended only to give access to the resources we are using.

> If we are using a serverless version minor to version 3, we have to add this statement `variablesResolutionMode: 20210326` under service declaration
```yaml
#serverless.yml
service: myapp

plugins:
  - serverless-plugin-typescript

provider:
  name: aws
  runtime: nodejs16.x
  stage: dev
  region: us-east-1
  environment:
    FIRST_NAME: ${file(./helperScript.js):getParameters.firstName}
    LAST_NAME: ${file(./helperScript.js):getParameters.lastName} 
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - ssm:GetParameter
        - ssm:GetParameters
        - ssm:GetParametersByPath
      Resource: "*"
custom:
  prefix: /${self:provider.stage}/${self:service}
functions:
  hello:
    handler: handler.hello
```
Finally, let's use this Parameter Store in our lambda code.

```typescript
//handler.ts
import { Handler } from 'aws-lambda';

export const hello: Handler = (event: any) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'My First name is '+`${process.env.FIRST_NAME}`+' and my last name is '+`${process.env.LAST_NAME}`
      },
      null,
      2
    ),
  };

  return new Promise((resolve) => {
    resolve(response)
  })
}
```
And that's it!, if we execute our lambda it should look like this:
![Lambda Execution](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e6x8jtxy1bb4kppnyy12.png)

This will surely help us save up some Parameter Stores and keep them neat and clean. However, we should consider the size that these variables could get. We might take into consideration the use of [Advanced Parameters](https://docs.amazonaws.cn/en_us/systems-manager/latest/APIReference/API_PutParameter.html#API_PutParameter_RequestSyntax).

Reference: [Serverless Framework Variables](https://www.serverless.com/framework/docs/providers/aws/guide/variables#reference-variables-in-javascript-files)