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

CustomResource serviceToken must be a string even though return type is object #1421

Closed
danieljamesscott opened this issue Dec 21, 2018 · 3 comments

Comments

@danieljamesscott
Copy link

I'm migrating our existing hand-crafted CF templates to CDK (Java). We have some custom resources which I'm implementing by extending CustomResource.

I've run into an issue with CustomResourceProps.getServiceToken() which has a return type of Object, and want to return a function to match our old templates. Our old templates returned a Sub function like this:

{"Sub":"arn:aws:sns:${AWS::Region}:${AWS::AccountId}:CloudFormationInterface"}

Which I've implemented as a simple java object (Ref), with a single field. Then, I've overridden the CustomResourceProps.getServiceToken() to return a Ref object. When I synthesize the template, I get the following error:

software.amazon.jsii.JsiiException: While synthesizing test/PoolCustomResource: software.amazon.jsii.JsiiException: Supplied properties not correct for "CustomResourceProps"
  serviceToken: {"Sub":"arn:aws:sns:${AWS::Region}:${AWS::AccountId}:CloudFormationInterface"} should be a string

Is this expected behaviour? In general, is it expected that the CDK would be used to generate and execute the CF template? Or could it be used to generate the template, which is then deployed by some other method. I can see that if we expect the CDK to be used to generate and deploy the template, then there's no need to support the template functions, as we can dynamically change those as the template is created. However, we currently generate one template, and deploy it to different environments using parameters/functions to modify the behaviour. To use the CDK for this, I would need CDK support to generate templates containing template functions.

@eladb
Copy link
Contributor

eladb commented Dec 23, 2018

You should be able to just invoke .toString() on the FnSub (or any other token) and return the result. This is what we call a "stringified token". It lets you to represent CFN intrinsic functions that will resolve to a string at deploy-time as native strings at design-time. This allowed us to dramatically and pragmatically simplify the CDK's type-system (see #168). In most cases, this is almost transparent to users who use the CDK "natively" (as in, not coming from CFN mindset).

Thanks to this mechanism, you can simplify your solution even more. You could technically do this:

serviceToken: `arn:aws:sns:${new cdk.AwsRegion()}:${new cdk.AwsAccountId()}:CloudFormationInterface`

In java this will look like this I guess:

"arn:aws:sns: " + new AwsRegion()  + ":" + new AwsAccountId() + ":CloudFormationInterface"

In this usage, AwsRegion and AwsAccountId are both tokens, and many languages will implicitly invoke toString() when they are concatenated into strings.

The above will resolve to:

{
  "Fn::Join": [
    "",
    [
      "arn:aws:sns:",
      { "Ref": "AWS::Region" },
      ":",
      { "Ref": "AWS::AccountId" },
      ":CloudFormationInterface"
    ]
  ]
}

The best option would probably not to construct ARNs yourself, but use ArnUtils:

serviceToken: cdk.ArnUtils.fromComponents({
  service: 'sns',
  resource: 'CloudFormationInterface'
})

Which resolves to the following, which is even "more accurate" because it takes into account the AWS partition:

{
  "Fn::Join": [
    "",
    [
      "arn:",
      { "Ref": "AWS::Partition" },
      ":sns:",
      { "Ref": "AWS::Region" },
      ":",
      { "Ref": "AWS::AccountId" },
      ":CloudFormationInterface"
    ]
  ]
}

We are trying to build the CDK such that it can be used for both use cases, but optimizing for the "native" use case (the one you refer to "generate + deploy").

@eladb eladb closed this as completed Dec 23, 2018
@danieljamesscott
Copy link
Author

Thanks for all that info, very useful. I can see that I'm actually doing it the "wrong way".

As an aside, the toString call doesn't work because I need to serialize the object to json, not just print out the object reference. I could make it work by overriding toString, but building it the way you suggest should work out better.

Out of interest, is there a way to generate cloudformation which uses Sub functions? We have some fairly complex templates that we're converting from static files to being generated using the CDK and it's nice to be able to make sure the files match exactly.

@danieljamesscott
Copy link
Author

Ahh, sorry. I just saw FnSub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants