aws / aws/aws-cdk

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

Open
#19,349 18 comments 12 reactions 1 assignee Claimed by @pahud View on GitHub
@aws-cdk/aws-ssm blocked bug needs-cfn p3
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### 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:

```typescript
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):
```typescript
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):
```typescript
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`:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:eu-central-1::/*/*/*",
"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:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:eu-central-1::/*/*/*",
"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}}\"]}" │
│ │ │ │ │ │ } │
└───┴────────────────────┴────────┴────────────────────┴───────────┴──────────────────────────────────────────────────────────────┘
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.