How to pass the Amplify app ID to a function? How to do app introspection from backend functions?

0

Background

Amplify apps are easily extensible with Lambda functions, using amplify add function. Great!

Problem

How can I access the Amplify app ID from the Lambda function code? There are a lot of scenarios where I need that string in order to locate resources or access secrets in SSM.

More generally

How can my function do introspection on the app? How can I get the app ID from the Lambda function? Is there a service? Am I supposed to pass the information (somehow) through the CloudFormation template for the function?

Due diligence

I've spent days trying to figure this out, and I have at least learned the secret, undocumented way to get anything in a nested CloudFormation stack's outputs into the parameters for my CloudFormation stack, so that I can create environment variables that my Lambda function can see.

That does not solve my original problem of finding the top-level app ID. Or any information about the top-level app. If I could find the stack name for the top-level CloudFormation for the stack then I could learn a lot of things. I can't.

How to pass stack outputs from app resources into function stack parameters

I've spent days trying to figure this out, and I have at least learned the secret, undocumented way to use dependsOn in the backend-config.json to get the outputs from the CloudFormation stacks for other resources in the Amplify app and feed those into the parameters for my stack for my function:

 "function": {
    "MyFunctionName": {
      "build": true,
      "providerPlugin": "awscloudformation",
      "service": "Lambda",
      "dependsOn": [
        {
          "category": "api",
          "resourceName": "Data",
          "attributes": [
            "GraphQLAPIIdOutput"
          ]
        }
      ],
    }
  }
}

That creates a new parameter for your function that's named using a pattern that's not documented anywhere, from what I can tell: [category][resource name][CloudFormation stack output name]. You can reference that in your CloudFormation stack for your function to create an environment variable that your function code can access:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    ...
    "secretsPathAmplifyAppId": {
      "Type": "String"
    }
    ...
  "Resources": {
    ...
        "Environment": {
          "Variables": {
            "AMPLIFY_APP_ID": {
              "Ref": "secretsPathAmplifyAppId"
            },

Using the AmplifyAppId in amplify-meta.json doesn't work

If I could access the provider / cloudformation data from a dependsOn then I could get the app ID into my function's stack. But that doesn't work. I spent some time eliminating that possibility.

Using secretsPathAmplifyAppId

There is a side effect of using amplify update function to add secrets. If you add any secret to the function then you will get a new parameter as an input to your function's CloudFormation stack: secretsPathAmplifyAppId

I did that and added a dummy secret that I don't really need, in order to get that CloudFormation stack parameter containing the Amplify App ID that I do need. And then I referenced that in my CloudFormation template for my function:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    ...
    "env": {
      "Type": "String"
    },
    "s3Key": {
      "Type": "String"
    },
    ...
    "secretsPathAmplifyAppId": {
      "Type": "String"
    }

That works, right? No!

If I create a new app in Amplify, perhaps deploying it to a staging or production account for the first time, then I'll get the error Parameters: [secretsPathAmplifyAppId] must have values from the initial build when I press "Save and Deploy" on the "Host your web app" form. This is because using secretsPathAmplifyAppId relies on the Amplify CLI adding the value to the team-provider-info.json file. For a new app's first deployment, "the team-provider-info.json file is not available in the Admin UI deployment job", as described in https://github.com/aws-amplify/amplify-cli/issues/8513 . And there is apparently no solution.

WHY IS THIS SO HARD?!?

The Amplify documentation implies that it's not difficult to add a Lambda function and do whatever. I'm a Lambda pro and a code pro, and I can do whatever. But only if I can pass context information to my code.

How can an Amplify app's Lambda functions do introspection on the app?

TaoRyan
asked 2 years ago1351 views
1 Answer
0
Accepted Answer

Here's how I did it. AWS, please make this easier. IMHO, the app ID should be available to a function by default. If not, then it should be simpler than what I had to go through. It should at least be documented. And the documentation about what environment variables exist appears to be incorrect. Or else maybe I misunderstood it and that still seems like a problem.

Variable handoff from build time to runtime

The overall technique is to pass the AWS_APP_ID environment variable from the build-time environment to the Lamda runtime environment where it will be available to the Lambda function.

AWS_APP_ID is available at build time

This page in the Amplify documentation lists a set of environment variables that are supposed to be available at build time. It's not true. The only variable in the list that's available at build time is AWS_APP_ID. That's enough, though.

Interpolate AWS_APP_ID into CloudFormation template

I cited the variable in my CloudFormation template for my function like this:

        "Environment": {
          "Variables": {
            "AWS_APP_ID": "{{AWS_APP_ID}}",

Then I set up my build to use an off-the-shelf NPM to interpolate the variable into that. The {{}} syntax comes from that NPM. There is probably a way to do this with sed or something that could make the build process faster and not dependent on that NPM.

Access environment variable from Lambda function normally

Then you can access the app ID from the Lambda function in the normal way for the function's runtime:

const app_id = process.env.AWS_APP_ID
console.log(`app_id: ${app_id}`)
TaoRyan
answered 2 years ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions