2

Was looking at using the SSM Parameter Store SDK to grab secrets for a lambda function. I'm also using epsagon to wrap the async handler function. Epsagon needs to be initialized with some secrets, and then is used to wrap the handler function:

import * as epsagon from 'epsagon'

epsagon.init({
  token: EPSAGON_ACCOUNT_TOKEN,
})

export const lambdaHandler = epsagon.lambdaWrapper(async (event) => {
  // do stuff
  
})

Started using aws-parameter-cache to grab config values from SSM Param Store, but since they are resolved with an API call, it takes an await to get the values fully resolved.

import { ssmParameter } from 'aws-parameter-cache'

const param = ssmParameter({ name: 'foo' })
const value = await param.value; // <-- can only be done inside an async function (nodejs12)

Since we don't yet have top level await in nodejs12, is there a way to resolve the variables outside of the handler function? Is it possible to wait for the API call for await param.value to finish so that I can initialize epsagon with a value stored in SSM Param Store?

import * as epsagon from 'epsagon'
import { ssmParameter } from 'aws-parameter-cache'
const ssmParam = ssmParameter({ name: 'epsagonToken' })

const epsagonToken = await ssmParam.value // fails since outside of async func

epsagon.init({
  token: epsagonToken,
})

export const lambdaHandler = epsagon.lambdaWrapper(async (event) => {
  const epsagonToken = await ssmParam.value // works here but too late
})

Would this "just work" in nodejs 14.3.0 with top-level await? Custom runtime?

Or maybe some form of never-rejecting top-level async function, like in the top answer to this: how-can-i-use-async-await-at-the-top-level?

Need the handler to be the callback to the top-level async function--from what I've read this is essentially how top-level async works in 14.3. Looking for way to store all secrets in SSM Param store and reduce cf template ENV variable mappings.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
notbrain
  • 3,366
  • 2
  • 32
  • 42

1 Answers1

3

Basically there is no easy way to do top-level await in this case, but there are some easy workarounds around it. For example, here is an implementation of another wrapper that you can use to initialize Epsagon:

import * as epsagon from 'epsagon'
import { ssmParameter } from 'aws-parameter-cache'
const ssmParam = ssmParameter({ name: 'epsagonToken' })

const withEpsagon = (wrapped) => {
  let epsagonInited = false
  const epsagonizedFunction = epsagon.lambdaWrapper(wrapped)
  return async (event, context, callback) => {
    if (!epsagonInited) {
      const epsagonToken = await ssmParam.value
      epsagon.init({
        token: epsagonToken,
      })
      epsagonInited = true
    }
    return epsagonizedFunction(event, context, callback)
  }
}

export const lambdaHandler = withEpsagon(async (event) => {
    // your code here
})

This code will resolve the SSM parameter on its first execution (right after a cold start, which is a time you would have to spend on the cold start anyway), and memorize that it already initialized Epsagon so you don't have to waste time on every time the Lambda is invoked.

Gal Bashan
  • 112
  • 6