DEV Community

Cover image for How to save up on AWS Parameter Store with serverless framework
David
David

Posted on

How to save up on AWS Parameter Store with serverless framework

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 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"}
Enter fullscreen mode Exit fullscreen mode

Parameter Store

Create a Typescript serverless Lambda Project with serverless framework. Let's add the following dependencies:

//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"
}
Enter fullscreen mode Exit fullscreen mode

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 statement will reference the stage and name of our service, declared in our serverless.yml file.

//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

  };
};
Enter fullscreen mode Exit fullscreen mode

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

#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
Enter fullscreen mode Exit fullscreen mode

Finally, let's use this Parameter Store in our lambda code.

//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)
  })
}
Enter fullscreen mode Exit fullscreen mode

And that's it!, if we execute our lambda it should look like this:
Lambda Execution

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.

Reference: Serverless Framework Variables

Discussion (0)