1
votes

i'm using serverless framework(aws).

From cloudFormation when creating a database, writes to the env. variable endpoint of the database.

part of my serverless.yml file

  environment:    
      DATABASE_HOST:
          "Fn::GetAtt": [ServerlessRDS, Endpoint.Address]

This variable is available from the lambda level as it is already deployed on aws. But I want to have access to this variable from locally. I came across the idea that I would write this variable to the parameter store (aws systems manager).

So I attached the script to my serverless.yml file (using serverless-scriptable-plugin).

scriptHooks, part of my serverless.yml file :

after:aws:deploy:finalize:cleanup:
  - scripts/update-dbEndopint.js

Here's the script. Nothing special, writes an environment variable process.env.DATABASE_HOST to parameter stora.

const aws = require('aws-sdk');
const ssm = new aws.SSM();
(async () => {
  try {
    const params = {
      Name: `${process.env.AWS_SERVICE_NAME}-DATABASE_HOST-${
        process.env.AWS_STAGE
      }`,
      Value: `${process.env.DATABASE_HOST}`,
      Type: 'String',
      Overwrite: true,
    };
    await ssm.putParameter(params).promise();
    log(`[DATABASE_HOST]: ${process.env.DATABASE_HOST} `);
    log('Task done.');
  } catch (e) {
    throw e;
  }
})();

But after taking deploy the variable is undefined. This is because the variable value is only available later.

Do you know how to get me to get the base endpoint to parameter store?

1

1 Answers

1
votes

Your servreless.yml will set the environment variable for the function but not for the process.env of the scripts run by serverless-scriptable-plugin.

You'll need to save it as an output for your stack using something similar to this:

Resources:
  ServerlessRDS:
    ....

Outputs:
  ServerlessRDSEndpointAddress:
    Value:
      "Fn::GetAtt": [ServerlessRDS, Endpoint.Address]

Then in your script extract that value from the stack something like this:

const fs = require('fs');
const yaml = require('js-yaml');
const aws = require('aws-sdk');
const ssm = new aws.SSM();

const getStackName = (stage) => {
  const content = fs.readFileSync('serverless.yml');
  return `${yaml.safeLoad(content).service}-${stage}`;
};

const getStackOutputs = async (provider, stackName, stage, region) => {
  const result = await provider.request(
    'CloudFormation',
    'describeStacks',
    { StackName: stackName },
    stage,
    region,
  );

  const outputsArray = result.Stacks[0].Outputs;

  let outputs = {};
  for (let i = 0; i < outputsArray.length; i++) {
    outputs[outputsArray[i].OutputKey] = outputsArray[i].OutputValue;
  }

  return outputs;
};

(async () => {
  try {
    const provider = serverless.getProvider('aws');
    const { stage, region } = options;

    const { ServerlessRDSEndpointAddress } = await getStackOutputs(provider, getStackName(stage), stage, region)

    const params = {
      Name: `${process.env.AWS_SERVICE_NAME}-DATABASE_HOST-${
        process.env.AWS_STAGE
      }`,
      Value: `${ServerlessRDSEndpointAddress}`,
      Type: 'String',
      Overwrite: true,
    };
    await ssm.putParameter(params).promise();
    log(`[DATABASE_HOST]: ${ServerlessRDSEndpointAddress} `);
    log('Task done.');
  } catch (e) {
    throw e;
  }
})();

I'm not sure how saving the value in parameter store will allow you to access it locally though.

If you want to invoke the function locally you can use:

serverless invoke local -f functionName -e DATABASE_HOST=<DATABASE_HOST>

Or use dotenv for any other JavaScript code