Sarat Kota
Sarat Kota

Reputation: 61

Inline Lambda CFT "Fn::Join" Error - JSON

I'm writing a cloud formation template in JSON format. The template has one parameter of type CommaDelimitedList of FSx file system IDs and a Lambda function resource which refers the parameter for FSx file system IDs and checks their status. I'm writing this Lambda function inline in the template.

I'm getting the below error:

Template error: every Fn::Join object requires two parameters, (1) a string delimiter and (2) a list of strings to be joined or a function that returns a list of strings (such as Fn::GetAZs) to be joined.

Here's my template:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "FSIds": {
      "Type": "CommaDelimitedList",
      "Description": "Enter the file system IDs separated by commas"
    }
  },
  "Resources": {
    "FSxStatusLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "FunctionName": "test-FSx",
        "Architectures": [ "x86_64" ],
        "Description": "Lambda function that checks FSx file systems status and sends notification",
        "Handler": "index.handler",
        "Code": {
          "ZipFile": {
            "Fn::Join": [
              "\n",
              [
                  "import boto3",
                  "fsx = boto3.client('fsx')",
                  "#Publish message to SNS topic",
                  "def publish_message(FS_ID , status):",
                  "    sns = boto3.client('sns')",
                  "    topic = \"arn:aws:sns:us-east-1:123456789:FSx-Notification\"",
                  "    msg = \"{FS_ID} is in {status} state\".format(FS_ID=FS_ID , status=status)",
                  "    response = sns.publish(",
                  "        TopicArn = topic,",
                  "        Message = msg",
                  "        )",
                  "#Lambda handler function",
                  "def lambda_handler(event, context):",
                  "    res = fsx.describe_file_systems(",
                  "        FileSystemIds = ",
                  {
                    "Ref": "FSIds"
                  },
                  "        )",
                  "    for i in range(len(res[\"FileSystems\"])):",
                  "        FS_ID = res[\"FileSystems\"][i][\"FileSystemId\"]",
                  "        status = res[\"FileSystems\"][i][\"Lifecycle\"]",
                  "        if status == \"MISCONFIGURED\" or status == \"FAILED\":",
                  "            publish_message(FS_ID , status)",
              ]
            ]
          }
        },
        "Role": "arn:aws:iam::123456789:role/FSx-LambdaRole",
        "Runtime": "python3.9",
        "Timeout": "60",
        "Tags": [
          {
            "Key": "Owner",
            "Value": "God"
          }
        ]
      }
    }
  }
}

While creating stack, I'm giving the input to the parameter as: fs-123,fs-456

Please help!!

Upvotes: 1

Views: 122

Answers (1)

Erwin
Erwin

Reputation: 952

The error is caused, indirectly, by the CommaDelimitedList type of your parameter. It causes FSIds to be a list/array and not a string, which then means that this entry in your list of strings for the Fn::Join is not a string:

    {
        "Ref": "FSIds"
    },

If you change the parameter type to String, it will create the stack for you.

Alternatively, you may keep the CommaDelimitedList and change the above to this:

    {
        "Fn::Join": [ ",", { "Ref": "FSIds" } ]
    },

Either one of those will at least get you past the error you're currently encountering.

Update:

Since, as you noted, FileSystemIds actually needs to be a list, you've got a few options:

  1. Change the CF template, which is a pain but can be done. Something like this (UNTESTED):
                {
                  "Fn::Join": [
                    "",
                    [
                      "        FileSystemIds = [\"",
                      {
                        "Fn::Join": [
                          "\", \"",
                          {
                            "Ref": "FSIds"
                          }
                        ]
                      },
                      "\""
                    ]
                  ]
                },
  1. Similar, but let Python do some of the work with str.split(), for instance like this (note that both of these assume FSIds is still a CommaDelimitedList):
                  {
                      "Fn::Join": [
                          "",
                          [
                              "        FileSystemIds = str.split(\"",
                              { "Fn::Join": [ ",", { "Ref": "FSIds" } ] },
                              "\", \",\")"
                          ]
                      },
                  },
  1. Pass the parameter along in a different manner that doesn't involve crafting the precise Python code via CF, such as an environment variable for the Lambda: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-environment.html

Upvotes: 1

Related Questions