AWS

In this article, you will find a recipe for how to read AWS parameters inside the NodeJS application with TypeScript.

Dependencies

To access AWS services you need to add aws-sdk (ver 2.473.0) to your project:

1
yarn add aws-sdk

Then you need to create instance of SSM to access AWS System Manager services:

1
2
3
  AWS.config.update({ region });
  AWS.config.apiVersions = { ssm: 'latest' };
  const ssm = new AWS.SSM();

Reading parameters from AWS

SSM has getParametersByPath function which partly fetches data from the Parameter store. To fetch all parameters you need to call this function several times e.g, via recursion:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const getParametersByPathRecursively = async (ssm: AWS.SSM, params) => {
  const data = await ssm.getParametersByPath(params).promise();
  let parameters = data.Parameters;

  if (data.NextToken) {
    parameters = [
      ...parameters,
      ...(await getParametersByPathRecursively(ssm, { ...params, NextToken: data.NextToken }))
    ];
  }

  return parameters;
};

Mapping

Then you need to map parameters to environment variables:

1
2
3
4
5
6
7
8
  const prefixLen = prefix.length + 1;
  const environmentVars = parameterList.reduce(
    (accumulator: {}, parameter: AWS.SSM.Types.Parameter) => ({
      ...accumulator,
      [parameter.Name.substr(prefixLen)]: parameter.Value
    }),
    {}
  );

Complete solution

For the complete solution, you need to merge mapped parameters with the process environment variables. Here is the complete of the solution with resolveEnvironment entry point.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
const getParametersByPathRecursively = async (ssm: AWS.SSM, params) => {
  const data = await ssm.getParametersByPath(params).promise();
  let parameters = data.Parameters;

  if (data.NextToken) {
    parameters = [
      ...parameters,
      ...(await getParametersByPathRecursively(ssm, { ...params, NextToken: data.NextToken }))
    ];
  }

  return parameters;
};

const getParametersByPath = async (
  path: string,
  region: string
): Promise<AWS.SSM.Types.ParameterList> => {
  AWS.config.update({ region });
  AWS.config.apiVersions = { ssm: 'latest' };
  const ssm = new AWS.SSM();

  const params = {
    Path: path,
    Recursive: true,
    WithDecryption: true
  };

  return getParametersByPathRecursively(ssm, params);
};

const mapParamsToEnvironmentVars = (prefix: string, parameterList: AWS.SSM.Types.ParameterList) => {
  const prefixLen = prefix.length + 1;
  return parameterList.reduce(
    (accumulator: {}, parameter: AWS.SSM.Types.Parameter) => ({
      ...accumulator,
      [parameter.Name.substr(prefixLen)]: parameter.Value
    }),
    {}
  );
};

const loadEnvVarsByPath = async (
  path: string,
  awsRegion: string,
  environment: NodeJS.ProcessEnv
) => {
  try {
    const parameterList = await getParametersByPath(path, awsRegion);

    environment = {
      ...environment,
      ...mapParamsToEnvironmentVars(path, parameterList)
    };
  } catch (err) {
    console.warn(`Can't resolve ssm pramas for path ${path}, error: ${JSON.stringify(err)}`);
  }

  return environment;
};

export const resolveEnvironment = async (
  environmentName: string,
  platformName: string,
  siteName: string  
): Promise<NodeJS.ProcessEnv> => {
  console.info(`Loading vars for '${environmentName}' environment...`);

  let environment = process.env;

  // Load params from AWS parameter store
  const awsRegion = 'eu-central-1';

  // Load site generic env vars
  const platformSitePath = `/${platformName}/site-generic/${environmentName}`;
  environment = await loadEnvVarsByPath(platformSitePath, awsRegion, environment);

  // Load site specific env vars
  const serviceSettingsPath = `/${platformName}/${siteName}/${environmentName}`;
  environment = await loadEnvVarsByPath(serviceSettingsPath, awsRegion, environment);

  return environment;
};