-
Notifications
You must be signed in to change notification settings - Fork 4.6k
fix(ssm): malformed ARNs for parameters with physical names that use path notation #4842
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
2d96aef
b0e5fa5
47a1661
210d0b0
5494c88
8d8ee14
31a866b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { CfnCondition, Construct, Fn, IConstruct, Stack, Token } from "@aws-cdk/core"; | ||
|
|
||
| /** | ||
| * Renders an ARN for an SSM parameter given a parameter name. | ||
| * @param scope definition scope | ||
| * @param parameterName the parameter name to include in the ARN | ||
| * @param physicalName optional physical name specified by the user (to auto-detect separator) | ||
| */ | ||
| export function arnForParameterName(scope: IConstruct, parameterName: string, physicalName?: string): string { | ||
| const { sep, resourceName } = determineSepAndResourceName(); | ||
|
|
||
| validateParameterName(physicalName || parameterName); | ||
|
|
||
| return Stack.of(scope).formatArn({ | ||
| service: 'ssm', | ||
| resource: 'parameter', | ||
| sep, | ||
| resourceName, | ||
| }); | ||
|
|
||
| function validateParameterName(concreteName: string) { | ||
| // can't validate tokens | ||
| if (Token.isUnresolved(concreteName)) { | ||
| return; | ||
| } | ||
|
|
||
| if (concreteName.includes('/') && !concreteName.startsWith('/')) { | ||
| throw new Error(`Parameter names must be fully qualified (if they include "/" they must also begin with a "/"): ${concreteName}`); | ||
| } | ||
| } | ||
|
|
||
| function determineSepAndResourceName() { | ||
| // if the parameter name is a token | ||
| if (Token.isUnresolved(parameterName)) { | ||
|
|
||
| // if we have a concrete physical name, we can use it to determine the separator | ||
| if (physicalName && !Token.isUnresolved(physicalName)) { | ||
| return { | ||
| sep: physicalName.startsWith('/') ? '' : '/', | ||
| resourceName: parameterName | ||
| }; | ||
| } | ||
|
|
||
| // parameterName is a token and physical name is not helping us (either missing or a token itself) | ||
| // in this use case we will need to synthesize a CloudFormation condition that will be used to determine | ||
| // if the name has a "/" prefix or not. | ||
| const startsWithSlash = startsWithCondition(scope as Construct, parameterName, "/"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh shit. After thinking about it some more, I'm not sure this is going to work. At least, this won't work for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you are right: |
||
| return { | ||
| sep: '', | ||
| resourceName: Token.asString(Fn.conditionIf(startsWithSlash.logicalId, parameterName, `/${parameterName}`)) | ||
| }; | ||
| } | ||
|
|
||
| // parameterName is concrete, use it to determine the token | ||
| return { | ||
| sep: parameterName.startsWith('/') ? '' : '/', | ||
| resourceName: parameterName | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Gets or creates a CloudFormation condition that evaluates to "TRUE" if `parameterName` (treated as an opaque token) | ||
| * starts with a "/". | ||
| */ | ||
| function startsWithCondition(scope: Construct, value: string, startsWith: string) { | ||
| const id = `AWS::CDK::StartsWith(${startsWith})`; | ||
| return scope.node.tryFindChild(id) as CfnCondition || new CfnCondition(scope, id, { | ||
| expression: Fn.conditionEquals(Fn.select(0, Fn.split(startsWith, value)), "") | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| { | ||
| "Resources": { | ||
| "StringAutogenE7E896E4": { | ||
| "Type": "AWS::SSM::Parameter", | ||
| "Properties": { | ||
| "Type": "String", | ||
| "Value": "hello, world" | ||
| } | ||
| }, | ||
| "StringSimpleA681514D": { | ||
| "Type": "AWS::SSM::Parameter", | ||
| "Properties": { | ||
| "Type": "String", | ||
| "Value": "hello, world", | ||
| "Name": "simple-name" | ||
| } | ||
| }, | ||
| "StringPathD8120137": { | ||
| "Type": "AWS::SSM::Parameter", | ||
| "Properties": { | ||
| "Type": "String", | ||
| "Value": "hello, world", | ||
| "Name": "/path/name/foo/bar" | ||
| } | ||
| }, | ||
| "ListAutogenC5DA1CAE": { | ||
| "Type": "AWS::SSM::Parameter", | ||
| "Properties": { | ||
| "Type": "StringList", | ||
| "Value": "hello,world" | ||
| } | ||
| }, | ||
| "ListSimple9DB641CB": { | ||
| "Type": "AWS::SSM::Parameter", | ||
| "Properties": { | ||
| "Type": "StringList", | ||
| "Value": "hello,world", | ||
| "Name": "list-simple-name" | ||
| } | ||
| }, | ||
| "ListPath120D6FAB": { | ||
| "Type": "AWS::SSM::Parameter", | ||
| "Properties": { | ||
| "Type": "StringList", | ||
| "Value": "hello,world", | ||
| "Name": "/list/path/name" | ||
| } | ||
| } | ||
| }, | ||
| "Outputs": { | ||
| "StringAutogenArn": { | ||
| "Value": { | ||
| "Fn::Join": [ | ||
| "", | ||
| [ | ||
| "arn:", | ||
| { | ||
| "Ref": "AWS::Partition" | ||
| }, | ||
| ":ssm:", | ||
| { | ||
| "Ref": "AWS::Region" | ||
| }, | ||
| ":", | ||
| { | ||
| "Ref": "AWS::AccountId" | ||
| }, | ||
| ":parameter/", | ||
| { | ||
| "Ref": "StringAutogenE7E896E4" | ||
| } | ||
| ] | ||
| ] | ||
| } | ||
| }, | ||
| "StringSimpleArn": { | ||
| "Value": { | ||
| "Fn::Join": [ | ||
| "", | ||
| [ | ||
| "arn:", | ||
| { | ||
| "Ref": "AWS::Partition" | ||
| }, | ||
| ":ssm:", | ||
| { | ||
| "Ref": "AWS::Region" | ||
| }, | ||
| ":", | ||
| { | ||
| "Ref": "AWS::AccountId" | ||
| }, | ||
| ":parameter/", | ||
| { | ||
| "Ref": "StringSimpleA681514D" | ||
| } | ||
| ] | ||
| ] | ||
| } | ||
| }, | ||
| "StringPathArn": { | ||
| "Value": { | ||
| "Fn::Join": [ | ||
| "", | ||
| [ | ||
| "arn:", | ||
| { | ||
| "Ref": "AWS::Partition" | ||
| }, | ||
| ":ssm:", | ||
| { | ||
| "Ref": "AWS::Region" | ||
| }, | ||
| ":", | ||
| { | ||
| "Ref": "AWS::AccountId" | ||
| }, | ||
| ":parameter", | ||
| { | ||
| "Ref": "StringPathD8120137" | ||
| } | ||
| ] | ||
| ] | ||
| } | ||
| }, | ||
| "ListAutogenArn": { | ||
| "Value": { | ||
| "Fn::Join": [ | ||
| "", | ||
| [ | ||
| "arn:", | ||
| { | ||
| "Ref": "AWS::Partition" | ||
| }, | ||
| ":ssm:", | ||
| { | ||
| "Ref": "AWS::Region" | ||
| }, | ||
| ":", | ||
| { | ||
| "Ref": "AWS::AccountId" | ||
| }, | ||
| ":parameter/", | ||
| { | ||
| "Ref": "ListAutogenC5DA1CAE" | ||
| } | ||
| ] | ||
| ] | ||
| } | ||
| }, | ||
| "ListSimpleArn": { | ||
| "Value": { | ||
| "Fn::Join": [ | ||
| "", | ||
| [ | ||
| "arn:", | ||
| { | ||
| "Ref": "AWS::Partition" | ||
| }, | ||
| ":ssm:", | ||
| { | ||
| "Ref": "AWS::Region" | ||
| }, | ||
| ":", | ||
| { | ||
| "Ref": "AWS::AccountId" | ||
| }, | ||
| ":parameter/", | ||
| { | ||
| "Ref": "ListSimple9DB641CB" | ||
| } | ||
| ] | ||
| ] | ||
| } | ||
| }, | ||
| "ListPathArn": { | ||
| "Value": { | ||
| "Fn::Join": [ | ||
| "", | ||
| [ | ||
| "arn:", | ||
| { | ||
| "Ref": "AWS::Partition" | ||
| }, | ||
| ":ssm:", | ||
| { | ||
| "Ref": "AWS::Region" | ||
| }, | ||
| ":", | ||
| { | ||
| "Ref": "AWS::AccountId" | ||
| }, | ||
| ":parameter", | ||
| { | ||
| "Ref": "ListPath120D6FAB" | ||
| } | ||
| ] | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.