guys, I’m starting to use SSM to handle my secrets...
# guide
c
guys, I’m starting to use SSM to handle my secrets on a side-project I’ve been working on and would like to hear what would be the best practice between: • let the secrets only on SSM and get them on my code whenever I need; • add the secrets to the Lambdas on a release step on my CI; I think the first step would be more secure, even though it might do lots of calls to SSM during an execution of a Lambda (which I don’t know if it’s performative or not). If anyone has another way to use SSM I’d also like to hear it :)
m
Definitely the first option and typically you cache the SSM parameters for a period of time .. more info here -> https://theburningmonk.com/2017/09/you-should-use-ssm-parameter-store-over-lambda-env-variables/
a
Use Middy, it has middleware which solves everything.
And has batches.
b
I agree with @Michael Wolfenden in that the first option is better; it's also more secure. Make sure your Lambda function has permissions attached to it to read from SSM. Grant AWS Lambda Access to SSM Parameter Store is a great read to learn how to grant access using AWS console. Permissions - List of IAM policies from the SST docs hints at how to do it using CDK. Here is a code snippet of something I put together(only the policy parts) for you @Carlos Daniel taken from our code base. It's basically Lambdas that wraps API calls to Hubspot. Disclaimer: the policy attachment code haven't been tested yet as I have just created it to demonstrate how it can be done. However, I have been thinking about this for a while now wanting to implementing it myself.
Copy code
import * as cdk from 'aws-cdk-lib';
import config from '../src/config';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as sst from '@serverless-stack/resources';
import * as apigatewayv2 from '@aws-cdk/aws-apigatewayv2-alpha';

class Serverless extends sst.Stack {
  constructor(scope: <http://sst.App|sst.App>, id: string, props?: sst.StackProps) {
    super(scope, id, props);

    const { stage, region, name } = scope;

    const { account } = this;

    const env = {
      STAGE: stage,
      REGION: region,
      ACCOUNT_ID: account,
    };

    // Setup Lambda handlers
    const lambda = {
      createDeal: new sst.Function(this, 'createDeal', {
        handler: 'src/deal/create.handler',
        environment: env,
      }),

      updateDeal: new sst.Function(this, 'updateDeal', {
        handler: 'src/deal/update.handler',
        environment: env,
      }),

      createContact: new sst.Function(this, 'createContact', {
        handler: 'src/contact/create.handler',
        environment: env,
      }),
    };

    /*
     * Instantiate a new IAM policy statement that allows reading of
     * a parameter that stores our Hubspot API key.
     */
    const ssmParameterPolicy = new iam.PolicyStatement({
      actions: [
        'ssm:GetParameter',
        'ssm:GetParameters',
        'ssm:GetParametersByPath',
      ],
      effect: iam.Effect.ALLOW,
      resources: [
        `arn:aws:ssm:${region}:${account}:parameter/${config.hubspotApiKey}`,
      ],
    });

    // Attach the policy statement to the Lambda functions
    lambda.createDeal.attachPermissions([ssmParameterPolicy]);
    lambda.updateDeal.attachPermissions([ssmParameterPolicy]);
    lambda.createContact.attachPermissions([ssmParameterPolicy]);

    // Create an HTTP API
    const api = new sst.Api(this, 'Api', {
      cors: {
        allowHeaders: ['Content-Type', 'Authorization'],
        allowMethods: [
          apigatewayv2.CorsHttpMethod.GET,
          <http://apigatewayv2.CorsHttpMethod.POST|apigatewayv2.CorsHttpMethod.POST>,
        ],
        allowOrigins: ['*'],
        maxAge: cdk.Duration.days(10),
      },
      routes: {
        'POST /deals': { function: lambda.createDeal },
        'PUT  /deals/{dealId}': { function: lambda.updateDeal },
        'POST /contacts': { function: lambda.createContact },
      },
      accessLog: true,
    });
    // Show the endpoint in the output
    this.addOutputs({
      ApiEndpoint: api.url,
    });
  }
}

// serverless app
export default function main(app: <http://sst.App|sst.App>) {
  // extract the meta from the app
  const { stage, region, name } = app;

  // instantiate a serverless stack
  new Serverless(app, 'serverless', {
    description: `Serverless stack for app: ${name} stage:${stage} in the ${region} region`,
  });
}
c
wow @Bjorn Theart that’s supercool! I’m going to implement it on my project, thanks a lot
thanks for the help guys
a
Why not just use the "permission" attribute provided by sst?
I never had to create a policy myself.
c
I was doing this way