1

I have a CF template that uses a Serverless function in Javascript to instantiate an AWS Connect instance as there is no actual CF resource for Connect. I use environment variables to pass in values from CF into the inline Javascript. Now I need the reverse: to extract some of those values back out of JS and pass them back to other parts of the CF template. So, the question is, how do I pass a variable from Javascript to CloudFormation? I suspect I'll need to use something like the Systems Manager Parameter store but perhaps there's an easier way? Specifically I need the value "serviceRole" in the below snippet available to pass to other CF resources:

CreateConnectInstance:
Type: AWS::Serverless::Function
Properties:
  Handler: index.handler
  Runtime: nodejs12.x
  Description: Invoke a function to create an AWS Connect instance.
  MemorySize: 128
  Timeout: 8
  Role: !GetAtt LambdaExecutionRole.Arn
  Tracing: Active
  Layers:
    - !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:layer:node_sdk:1"
  Environment:
    Variables:
      IdentityManagementType:
        Ref: IdentityManagementType
      InboundCallsEnabled:
        Ref: InboundCallsEnabled
      InstanceAlias:
        Ref: InstanceAlias
      OutboundCallsEnabled:
        Ref: OutboundCallsEnabled
      Region:
        Ref: Region
      #Optional Values
      ClientToken: !If
        - HasClientToken
        - !Ref ClientToken
        - !Ref "AWS::NoValue"
      DirectoryId: !If
        - HasClientToken
        - !Ref ClientToken
        - !Ref "AWS::NoValue"

  InlineCode: |
    var AWS = require('aws-sdk');
    var response = require('cfn-response');
    var connect = new AWS.Connect({apiVersion: '2017-08-08', region: process.env.Region});
    exports.handler = (event, context) => {
      console.log("Request Received:\n" + JSON.stringify(event));
      var instanceId;
      var serviceRole;
      var isInboundCallsEnabled = (process.env.InboundCallsEnabled == 'true');
      var isOutboundCallsEnabled = (process.env.OutboundCallsEnabled == 'true');
      var params = {
        InboundCallsEnabled: isInboundCallsEnabled,
        OutboundCallsEnabled: isOutboundCallsEnabled,
        IdentityManagementType: process.env.IdentityManagementType,
        ClientToken: process.env.ClientToken,
        DirectoryId: process.env.DirectoryId,
        InstanceAlias: process.env.InstanceAlias
      };
      connect.createInstance(params, function (err, data) {
        if (err) {
          let responseData = { Error: "Create Instance Failed" };
          console.log(responseData.Error + ":\n", err);
          response.send(event, context, "FAILED", responseData);
          instanceId = data.Id;
        }
        else {
          console.log("Connect Instance Creation Successful");
          console.log(JSON.stringify(data));
          response.send(event, context, "SUCCESS", data);
        }
      });
      connect.describeInstance({InstanceId: instanceId}, function (err,data) {
        console.log(JSON.stringify(data));
        serviceRole = data.Instance.ServiceRole; // ***NEED TO EXTRACT THIS VALUE to CF***
      });
    }

It's invoked in the same template by:

InvokeLambda:
  Type: AWS::CloudFormation::CustomResource
  DependsOn: CreateConnectInstance
  Version: "1.0"
  Properties:
    ServiceToken: !Sub ${CreateConnectInstance.Arn}
user2774004
  • 473
  • 2
  • 10

1 Answers1

1

When sending the "SUCCESS" response to CloudFormation, you can use the Data field of the response object to pass the Service Role as a key-value pair. This way, all resources that are created after the Custom Resource can access this information using Fn::GetAtt. From the docs:

After getting a SUCCESS response, AWS CloudFormation proceeds with the stack operation. If a FAILED response or no response is returned, the operation fails. Any output data from the custom resource is stored in the pre-signed URL location. The template developer can retrieve that data by using the Fn::GetAtt function.

berenbums
  • 1,239
  • 1
  • 5
  • 11
  • Thanks, that's the correct answer in general, however now I have found that no matter what I return to event.responseUrl the handler will just not close if it's specified as an async handler. So that leads to my new question: https://stackoverflow.com/questions/65417223/async-lambda-function-returning-promise-does-not-terminate-cloudformation-custo – user2774004 Dec 23 '20 at 15:42