Skip to content
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

(ssm): can not import parameter name from another stack #17094

Closed
mariusingjer opened this issue Oct 21, 2021 · 8 comments · Fixed by #25749
Closed

(ssm): can not import parameter name from another stack #17094

mariusingjer opened this issue Oct 21, 2021 · 8 comments · Fixed by #25749
Labels
@aws-cdk/aws-ssm Related to AWS Systems Manager bug This issue is a bug. effort/small Small work item – less than a day of effort p1

Comments

@mariusingjer
Copy link

mariusingjer commented Oct 21, 2021

What is the problem?

cdk generates input parameters in a stack when importing parameter store values.

Reproduction Steps

    configSqs = (scope: cdk.Construct) =>
        ssm.StringParameter.fromStringParameterAttributes(
            scope,
            'import-sqs-string-param',
            {
                simpleName: true,
                parameterName: cdk.Fn.importValue('some-exported-value-holding-the-param-name'),
            }
        );

        const sqsConfig = configSqs(this);
        sqsConfig.grantRead(this.ingestLambda);

This renders perfectly in terms of IAM:

            {
              "Action": [
                "ssm:DescribeParameters",
                "ssm:GetParameters",
                "ssm:GetParameter",
                "ssm:GetParameterHistory"
              ],
              "Effect": "Allow",
              "Resource": {
                "Fn::Join": [
                  "",
                  [
                    "arn:",
                    {
                      "Ref": "AWS::Partition"
                    },
                    ":ssm:eu-west-1:123456789012:parameter/",
                    {
                      "Fn::ImportValue": "some-exported-value-holding-the-param-name"
                    }
                  ]
                ]
              }
            }

But i also generates a parameter construct in the stack (I have no idea why):

  "Parameters": {
    "importsqsstringparamParameter": {
      "Type": "AWS::SSM::Parameter::Value<String>",
      "Default": {
        "Fn::ImportValue": "some-exported-value-holding-the-param-name"
      }
    },

What did you expect to happen?

CDK not to create input parameters on the CF-stack, Why does it create an input parameter to the cloudformation stack?

PS

I know you can explicity reference the parameter cross stack, but then you get into the automagically creation of exports problem

What actually happened?

CDK creates input parameters on the CF-stack which breaks when deploying with: "Template format error: Every Default member must be a string".

CDK CLI Version

1.124.0

Framework Version

No response

Node.js Version

12

OS

ubuntu

Language

Typescript

Language Version

No response

Other information

No response

@mariusingjer mariusingjer added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Oct 21, 2021
@github-actions github-actions bot added the @aws-cdk/aws-ssm Related to AWS Systems Manager label Oct 21, 2021
@ryparker
Copy link
Contributor

ryparker commented Oct 21, 2021

Changing this to a guidance issue since this is asking for an explanation of why and is not reporting broken functionality.

Let me know if you think this should stay as a bug.

@ryparker ryparker added guidance Question that needs advice or information. and removed bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Oct 21, 2021
@ryparker
Copy link
Contributor

ryparker commented Oct 25, 2021

Changing this back to a bug after reproducing the issue.

Reproduction code:

import { App, Stack, Fn, CfnOutput } from '@aws-cdk/core';
import { StringParameter } from '@aws-cdk/aws-ssm';

const app = new App();

const PARAM_NAME = 'service-token-param-name';

const stackA = new Stack(app, 'StackA');
new CfnOutput(stackA, 'OutputParamName', {
  exportName: PARAM_NAME,
  value: 'service-token'
});

const stackB = new Stack(app, 'StackB');
stackB.addDependency(stackA);
StringParameter.fromStringParameterAttributes(stackB, 'import-string-param', {
  simpleName: true,
  parameterName: Fn.importValue(PARAM_NAME),
});

StackA CFN:

{
  "Outputs": {
    "OutputParamName": {
      "Value": "service-token",
      "Export": {
        "Name": "service-token-param-name"
      }
    }
  },
  ...
}

StackB CFN:

{
  "Parameters": {
    "importstringparamParameter": {
      "Type": "AWS::SSM::Parameter::Value<String>",
      "Default": {
        "Fn::ImportValue": "service-token-param-name"
      }
    }
  },
  ...
}

Output on deploy:

$ cdk deploy --all
StackA
StackA: deploying...

 ✅  StackA (no changes)

Outputs:
StackA.OutputParamName = service-token

Stack ARN:
arn:aws:cloudformation:us-east-1:834948088465:stack/StackA/ce30dd80-35e3-11ec-942d-0a5b44970d99
StackB
StackB: deploying...
StackB: creating CloudFormation changeset...

 ❌  StackB failed: Error [ValidationError]: Template format error: Every Default member must be a string.
    at Request.extractError (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/protocol/query.js:50:29)
    at Request.callListeners (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/request.js:688:14)
    at Request.transition (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /Users/parkerzr/GitHub/aws-cdk-sample-sms/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/request.js:690:12)
    at Request.callListeners (/Users/parkerzr/GitHub/aws-cdk-sample-ssm/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
  code: 'ValidationError',
  time: 2021-10-25T23:00:40.438Z,
  requestId: '21ae1aa2-9c7a-477c-ba28-9849749b9d63',
  statusCode: 400,
  retryable: false,
  retryDelay: 182.38007825955194
}
Template format error: Every Default member must be a string.
error Command failed with exit code 1.

@ryparker ryparker added bug This issue is a bug. effort/small Small work item – less than a day of effort p1 and removed guidance Question that needs advice or information. labels Oct 25, 2021
@markusl
Copy link
Contributor

markusl commented Oct 26, 2021

We have seen this too, and it sometimes prevents using import on existing SSM parameters

@njlynch njlynch removed their assignment Oct 28, 2021
@toby-lego
Copy link

This is also causing problems when trying to use the output of "CfnMapping.findInMap" as the parameter name

@amcappelli
Copy link

I am also running into this issue, using cdk v2.44.0. Would really appreciate a fix!

@tmokmss
Copy link
Contributor

tmokmss commented Dec 4, 2022

I also encountered the same error on a different situation (code below).
I suspect if parameterName contains any token, it always results in the same error.

    const keyPair = new ec2.CfnKeyPair(this, 'KeyPair', {
      keyName: 'someKey',
    });
    // cfn will create this parameter implicitly: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-keypair.html
    const privateKey = ssm.StringParameter.fromStringParameterAttributes(
      this,
      'KeyParam',
      {
        parameterName: `/ec2/keypair/${keyPair.attrKeyPairId}`,
        simpleName: false,
      }
    );
// this will result in the error:
//     ValidationError: Template format error: Every Default member must be a string

@StackTrac3
Copy link

I had this issue when I was using fromStringParameterAttributes instead of fromSecureStringParameterAttributes for a Secure String.

@mergify mergify bot closed this as completed in #25749 Jun 30, 2023
mergify bot pushed a commit that referenced this issue Jun 30, 2023
…lved token (#25749)

Previously, when we import a SSM parameter by `ssm.StringParameter.fromStringParameterAttributes`, we use `CfnParameter` to get the value. 

```json
  "Parameters": {
    "importsqsstringparamParameter": {
      "Type": "AWS::SSM::Parameter::Value<String>",
      "Default": {
        "Fn::ImportValue": "some-exported-value-holding-the-param-name"
      }
    },
```

However, `Parameters.<Name>.Default` only allows a concrete string value. If it contains e.g. intrinsic functions, we get an error like this from CFn: `Template format error: Every Default member must be a string.`

This PR changes the behavior of `fromStringParameterAttributes` method. Now it uses `CfnDynamicReference` instead of `CfnParameter` if a parameter name contains unresolved tokens.

Since previously the case when `Token.isUnresolved(attrs.parameterName) == true` just resulted in a deployment error, this is not a breaking change.

Closes #17094

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-ssm Related to AWS Systems Manager bug This issue is a bug. effort/small Small work item – less than a day of effort p1
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants