question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItΒ collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

(aws-ssm): stringListValue returns unsplit list as string instead of list of strings

See original GitHub issue

What is the problem?

When using a StringListParameter imported from an existing string list parameter with StringListParameter.fromStringListParameterName, the resulting value from ipListParam.stringListValue ends up being a single string with the unsplit list in it, instead of the expected list of strings.

My use-case is using the StringListParameter to store a list of IP addresses to be used in a ResourcePolicy, like so:

const ipListParam = new StringListParameter(this, "ip-list", {
      stringListValue: ["x.x.x.1", "x.x.x.2"],
      parameterName: "ipList",
});
const apiResourcePolicy = new PolicyDocument({
      statements: [
        new PolicyStatement({
          effect: Effect.ALLOW,
          principals: [new AnyPrincipal()],
          resources: ["execute-api:/*/*/*"],
          actions: ["execute-api:Invoke"],
          conditions: {
            IpAddress: {
              "aws:SourceIp": ipListParam.stringListValue,
            },
          },
        }),
      ],
});

Reproduction Steps

I created a new Typescript CDK project using aws-cdk-lib=2.15.0, and deployed the following stack to create the parameter, REST API Gateway, and resource policy: Sample 1 (working):

import { aws_apigateway, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib";
import { EndpointType } from "aws-cdk-lib/aws-apigateway";
import {
  AnyPrincipal,
  Effect,
  PolicyDocument,
  PolicyStatement,
} from "aws-cdk-lib/aws-iam";
import { StringListParameter } from "aws-cdk-lib/aws-ssm";
import { Construct } from "constructs";

export class StringlistTestStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const ipListParam = new StringListParameter(this, "ip-list", {
      stringListValue: ["x.x.x.1", "x.x.x.2"],
      parameterName: "ipList",
    });
    ipListParam.applyRemovalPolicy(RemovalPolicy.RETAIN);
    const apiResourcePolicy = new PolicyDocument({
      statements: [
        new PolicyStatement({
          effect: Effect.ALLOW,
          principals: [new AnyPrincipal()],
          resources: ["execute-api:/*/*/*"],
          actions: ["execute-api:Invoke"],
          conditions: {
            IpAddress: {
              "aws:SourceIp": ipListParam.stringListValue,
            },
          },
        }),
      ],
    });

    const api = new aws_apigateway.RestApi(this, "pudim-api", {
      policy: apiResourcePolicy,
      endpointTypes: [EndpointType.REGIONAL],
    });
    const item = api.root.addResource("item");
    item.addMethod(
      "GET",
      new aws_apigateway.HttpIntegration("http://www.pudim.com.br")
    );
  }
}

Then, I removed ipList from the stack while retaining the parameter, and imported it by its name again. Sample 2 (not working):

import { aws_apigateway, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib";
import { EndpointType } from "aws-cdk-lib/aws-apigateway";
import {
  AnyPrincipal,
  Effect,
  PolicyDocument,
  PolicyStatement,
} from "aws-cdk-lib/aws-iam";
import { StringListParameter } from "aws-cdk-lib/aws-ssm";
import { Construct } from "constructs";

export class StringlistTestStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const ipListParam = StringListParameter.fromStringListParameterName(
      this,
      "ip-list",
      "ipList"
    );
    const apiResourcePolicy = new PolicyDocument({
      statements: [
        new PolicyStatement({
          effect: Effect.ALLOW,
          principals: [new AnyPrincipal()],
          resources: ["execute-api:/*/*/*"],
          actions: ["execute-api:Invoke"],
          conditions: {
            IpAddress: {
              "aws:SourceIp": ipListParam.stringListValue,
            },
          },
        }),
      ],
    });

    const api = new aws_apigateway.RestApi(this, "pudim-api", {
      policy: apiResourcePolicy,
      endpointTypes: [EndpointType.REGIONAL],
    });
    const item = api.root.addResource("item");
    item.addMethod(
      "GET",
      new aws_apigateway.HttpIntegration("http://www.pudim.com.br")
    );
  }
}

What did you expect to happen?

The API should be accessible for the IP addresses in the StringListParameter ipList.

I deployed and tested the endpoint resulting from Sample 1, and it worked as expected. The generated resource policy associated with the REST API Gateway was also correct (note the list in aws:SourceIp:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:eu-central-1:<account>:<api-id>/*/*/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "x.x.x.1",
                        "x.x.x.2"
                    ]
                }
            }
        }
    ]
}

What actually happened?

The API was not accessible, and the generated resource policy now contained a comma-separated string of IP addresses.

Generated resource policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:eu-central-1:<account>:<api-id>/*/*/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "x.x.x.1,x.x.x.2"
                }
            }
        }
    ]
}

I had the same problem with CDK 1.147.0.

CDK CLI Version

2.15.0 (build 151055e)

Framework Version

?

Node.js Version

v16.14.0

OS

macOS 12.2.1 (21D62)

Language

Typescript

Language Version

Typescript 3.9.7

Other information

When deploying, this is the change to the policy as presented by CDK:

IAM Statement Changes
β”Œβ”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   β”‚ Resource           β”‚ Effect β”‚ Action             β”‚ Principal β”‚ Condition                                                    β”‚
β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ - β”‚ execute-api:/*/*/* β”‚ Allow  β”‚ execute-api:Invoke β”‚ AWS:*     β”‚ "IpAddress": {                                               β”‚
β”‚   β”‚                    β”‚        β”‚                    β”‚           β”‚   "aws:SourceIp": "{\"Fn::Split\":[\",\",\"${iplist29786949. β”‚
β”‚   β”‚                    β”‚        β”‚                    β”‚           β”‚ Value}\"]}"                                                  β”‚
β”‚   β”‚                    β”‚        β”‚                    β”‚           β”‚ }                                                            β”‚
β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ + β”‚ execute-api:/*/*/* β”‚ Allow  β”‚ execute-api:Invoke β”‚ AWS:*     β”‚ "IpAddress": {                                               β”‚
β”‚   β”‚                    β”‚        β”‚                    β”‚           β”‚   "aws:SourceIp": "{\"Fn::Split\":[\",\",\"{{resolve:ssm:ipL β”‚
β”‚   β”‚                    β”‚        β”‚                    β”‚           β”‚ ist}}\"]}"                                                   β”‚
β”‚   β”‚                    β”‚        β”‚                    β”‚           β”‚ }                                                            β”‚
β””β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:5
  • Comments:10 (6 by maintainers)

github_iconTop GitHub Comments

4reactions
skinny85commented, May 6, 2022

@iwt-kschoenrock I was able to reproduce the issue, on a smaller example.

Here’s what I did:

  1. Wrote this CDK code:

        const ipListParam = new ssm.StringListParameter(this, "ip-list", {
            stringListValue: ["val1", "val2"],
            parameterName: "ipList",
        });
    

    Ran cdk deploy.

  2. Changed the code to:

        const ipListParam = new ssm.StringListParameter(this, "ip-list", {
            stringListValue: ["val1", "val2"],
            parameterName: "ipList",
        });
    
        const importedIpList = ssm.StringListParameter.fromStringListParameterName(
          this, 'ImportedIpList', "ipList");
    
        new s3.CfnBucket(this, 'CfnBucket', {
            corsConfiguration: {
                corsRules: [
                    {
                        allowedHeaders: importedIpList.stringListValue,
                        allowedMethods: ['GET'],
                        allowedOrigins: ['*'],
                    },
                ],
            },
        });
    

    Ran cdk deploy.

And here’s what I see in the AWS Console for S3, in the β€œCross-origin resource sharing (CORS)” section:

[
    {
        "AllowedHeaders": [
            "val1,val2"
        ],
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]

Clearly, it should be "AllowedHeaders": ["val1", "val2"] instead.

So it does look like there’s some issue with this, either in the CDK, or in the CloudFormation support.

1reaction
peterwoodworthcommented, Jul 20, 2022

The relevant part of the generated template can be seen here:

       "AllowedHeaders": {
        "Fn::Split": [
         ",",
         "{{resolve:ssm:ipList}}"
        ]
       },

We would expect the dynamic reference to be resolved, then have the split be applied. However, this is not what happens

If you test this with splitting by : instead of ,, this is what the allowed headers will resolve to in the S3 console:

        "AllowedHeaders": [
            "{{resolve",
            "ssm",
            "ipList}}"
        ],

This confirms that the split is occurring before the dynamic value is resolved. Since we rely on CloudFormation to keep these values secure, I’m not aware of any workarounds here. I’ve reported this to CloudFormation to see what they say P68236683

Read more comments on GitHub >

github_iconTop Results From Across the Web

class StringListParameter (construct) Β· AWS CDK
Returns a string representation of this construct. static fromStringListParameterName(scope, id, stringListParameterName), Imports an external parameter of typeΒ ...
Read more >
AWS Systems Manager Parameter Store: Using StringList as ...
Probably the most obvious is to use the SSM StringList type and require that the string list is ordered, for example mailInfo =...
Read more >
Fn::Split - Amazon CloudFormation - δΊšι©¬ι€ŠδΊ‘η§‘ζŠ€
Use the Amazon CloudFormation Fn::Split function to split a string into a list of strings.
Read more >
Convert string to list in Python | Flexiple Tutorials
Strings can be converted to lists using list() . ... Once split, it returns the split string in a list, using this method...
Read more >
Convert a Comma Separated String to a List in Java - Baeldung
So, if we have empty strings that we want to be included in our list, then we should use the splitPreserveAllTokens instead of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found