Due to some security constraints I'm dealing with, I need to create a Step Function which can execute a Lambda that is identified in the JSON input to the Step Function. I have created such a Step Function, using the CallAwsService construct, and I call it with a JSON like the below;
{
"lambda": "arn:aws:lambda:us-east-2:314995866118:function:prefix-dev-t-sfn-check-copy-file-to-bucket",
"payload": {
"file": "raw_data.csv",
"year": "2024",
"month": "05",
"day": "30"
}
}
However, when I do this the Step Function Fails with the below error. The error says that the ARN I am calling the Step Function with doesn't match the needed Regex, but looking at it I cannot see any error.
Value '{"type":0,"value":"arn:aws:lambda:us-east-2:314995866118:function:prefix-dev-t-sfn-check-copy-file-to-bucket"}' at 'functionName' failed to satisfy constraint: Member must satisfy regular expression pattern: (arn:(aws[a-zA-Z-]*)?:lambda:)?([a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-.]+)(:($LATEST|[a-zA-Z0-9-]+))?
I declare the step function in my code with the below TypeScript CDK definition;
import { Construct } from 'constructs';
import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
import { Role } from 'aws-cdk-lib/aws-iam';
import { Stack } from "aws-cdk-lib";
import { CallAwsService } from 'aws-cdk-lib/aws-stepfunctions-tasks';
export interface ExecuteNominatedLambdaProps {
environment: string
constructPrefix:string
sfnRole: Role
}
export class ExecuteNominatedLambda extends Construct {
stateMachine: sfn.StateMachine
constructor(scope: Stack, id: string, props: ExecuteNominatedLambdaProps) {
super(scope, id);
// Create Call-AWS object to invoke a Lambda given in JSON
const lambdaInvocation = new CallAwsService(
scope,
"Invoke Nominated Lambda",
{
service: "lambda",
action: "invoke",
parameters: {
FunctionName: sfn.TaskInput.fromJsonPathAt("$.lambda"),
InvocationType: "RequestResponse",
Payload: sfn.TaskInput.fromJsonPathAt("$.payload")
},
iamResources: ["*"],
iamAction: "lambda:InvokeFunction",
resultSelector: {
result: sfn.JsonPath.stringAt('$.Payload'),
},
resultPath: '$.checkStepFunctions',
}
);
// Create the step function's definition and name
const sfn_chain = sfn.DefinitionBody.fromChainable(lambdaInvocation)
const sfn_name = `${props.constructPrefix}-invoke-nominated-lambda`
this.stateMachine = new sfn.StateMachine(
scope,
`${id}-sfn`,
{
definitionBody: sfn_chain,
role: props.sfnRole,
stateMachineName: sfn_name
}
);
}
};
Can anybody see what I am doing wrong?
In writing out the question I figured out the answer, so here it is for anyone who wants to do the same thing.
In my original Step Function definition, I defined the parameters of the Lambda using the below object;
parameters: {
FunctionName: sfn.TaskInput.fromJsonPathAt("$.lambda"),
InvocationType: "RequestResponse",
Payload: sfn.TaskInput.fromJsonPathAt("$.payload")
}
Because I used the function sfn.TaskInput.fromJsonPathAt
for the FunctionName
and Payload
parameters, these are technically pulled as JSON objects which I believe are encoded as byte-strings, but the field FunctionName
must be a plain string.
I fixed the error I was getting by updating the parameters object to the following;
parameters: {
FunctionName: sfn.JsonPath.stringAt("$.lambda"),
InvocationType: "RequestResponse",
Payload: sfn.JsonPath.objectAt("$.payload")
}
Using the JsonPath
functions rather than the TaskInput
functions meant I could ensure that the parameters were of the required types.