7

I know how to invoke lambda functions that I've named and already exist as Lambda Functions but how can I have a FunctionA invoke FunctionB that I'm defining in AWS SAM template together, and won't know the name beforehand, aka dynamically.

Is there a way to pass the name of FunctionB as part of the SAM template before it gets created so the template would know the name of the full name of FunctionB, before creating FunctionA?

I see a lot of questions about testing this locally only

credizian
  • 469
  • 5
  • 20
  • 1
    Have you tried passing its ARN or name as an environment variable? – kichik Mar 25 '20 at 05:11
  • 1
    You just reference it in the `FunctionA`? Can you paste relevant part of the template. Not sure what is the problem. – Marcin Mar 25 '20 at 05:16
  • @kichik that's what I needed to do. I didn't realize the environment variable path. Thanks! If you post it as an answer, I'll mark as answer – credizian Mar 25 '20 at 06:41

2 Answers2

12

SAM is different than CloudFormation. SAM has a shortcut. The SAM resource type AWS::Serverless::Function simplifies this.

In this example SAM template, the example resource 'CallerFunction' has:

  1. A scoped policy to invoke the function 'microservice'
  2. An environment variable with the function name
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  A SAM template where lambda function caller invokes lambda function microservice.

Resources:
  CallerFunction:
    Type: AWS::Serverless::Function 
    Properties:
      Description: 'A lambda that invokes the function microservice'
      CodeUri: caller/
      Handler: app.handler
      Runtime: nodejs10.x
      Policies: 
        - LambdaInvokePolicy:
            FunctionName:
              !Ref MicroserviceFunction
      Environment:
        Variables:
          MICROSERVICE_FUNCTION: !Ref MicroserviceFunction
  MicroserviceFunction:
    Type: AWS::Serverless::Function 
    Properties:
      Description: 'A microservice lambda'
      CodeUri: microservice/
      Handler: index.handler
      Runtime: nodejs10.x
      Policies: CloudWatchLogsFullAccess

Have fun with serverless!

starpebble
  • 504
  • 3
  • 7
4

You can pass the other function's name or ARN as an environment variable. For example:

Resources:
  FunctionA:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.6
      Role: !Sub ${FunctionRole.Arn}
      Environment:
        Variables:
          # pass FunctionB ARN as environment variable
          FUNCTION_B_ARN: !Sub ${FunctionB.Arn}
      Code:
        ZipFile: |
          import os
          def handler(event, context):
            # use environment variable
            print(os.getenv("FUNCTION_B_ARN"))
  FunctionB:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.6
      Role: !Sub ${FunctionRole.Arn}
      Code:
        ZipFile: |
          def handler(event, context):
            print("hello world")
  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
        Version: '2012-10-17'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
kichik
  • 33,220
  • 7
  • 94
  • 114
  • 1
    +1. As a side note one can also reference it in the FunctionB code block: `${FunctionB.Arn}` when you use `!Sub | ` in `ZipFile`. – Marcin Mar 25 '20 at 08:05
  • I used !GetAtt instead of !Sub - not sure if one is more efficient – credizian Mar 25 '20 at 17:04