Open rix0rrr opened 1 year ago
This isn't called out in the documentation or RFC, but since the language extension is a transform / macro, it can't access Property Values from resources (or rather, only handles them opaquely). This happens because it has to run at change-set generation).
As an example, this does not output what you want, but is a valid way to use ToJsonString with GetAtt (!Join will convert the List to a String):
Transform: AWS::LanguageExtensions
Resources:
ZoneA5DE4B68:
Type: AWS::Route53::HostedZone
Properties:
Name: banana.com.
Outputs:
Output:
Value:
Fn::ToJsonString:
someString: !Join
- ','
- Fn::GetAtt:
- ZoneA5DE4B68
- NameServers
The output would be something like {"someString":"ns-202.awsdns-25.com,ns-840.awsdns-41.net,ns-1768.awsdns-29.co.uk,ns-1281.awsdns-32.org"}
I know why this happening. I am reporting this as an uncovered need so CloudFormation can have an internal discussion on whether {Fn::ToJsonString}
should be a proper intrinsic function or not.
Hey @rix0rrr, before I dive too deep into this issue, just want to double-check if the indentation issue in your example is accidental or intentional. I assume you mean this? (Output a sibling of Resources, not a child)
Transform: AWS::LanguageExtensions
Resources:
ZoneA5DE4B68:
Type: AWS::Route53::HostedZone
Properties:
Name: banana.com.
Outputs:
Output:
Value:
Fn::ToJsonString:
someList:
Fn::GetAtt:
- ZoneA5DE4B68
- NameServers
Hey @rix0rrr, (I'm putting aside the question if Fn::ToJsonString should be natively supported (without Transform). That's a legitimate ask, but let's discuss it in the other issue (#105) you have opened.)
When I use the template above where I adjusted the indentation, then the Transform succeeds and yields the output below. Since it can't resolve the Fn::GetAtt
expression, it converts it into an Fn::Sub
so that it can be resolved later. That's the intended behaviour. Let me know if it doesn't behave as you expected.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
"Output": {
"Value": {
"Fn::Sub": [
"{\"someList\":\"${44b47ae35a7d41dc95bd5991efc31fab}\"}",
{
"44b47ae35a7d41dc95bd5991efc31fab": {
"Fn::GetAtt": [
"Zone",
"NameServers"
]
}
}
]
}
}
},
"Resources": {
"Zone": {
"Type": "AWS::Route53::HostedZone",
"Properties": {
"Name": "banana.com"
}
}
}
}
I'll close this issue. Feel free to reopen, if you think we're missing something.
Hi @MalikAtalla-AWS, you are right, I messed up my indentation. Sorry for the confusion, but you figured out what I meant to type.
I know that the transform works, but a deployment of that transformed template does not work. The reason is that ${Zone.Nameservers}
returns an array, and { Fn::Sub }
does not like that.
I tried to deploy your transformed template:
$ aws cloudformation update-stack --stack-name TestarraysStack --template-body file://./transformed.json
And it does not deploy:
What I wanted as output is
"{ \"someList\": [\"ns1.amazonaws.com\", \"ns2.amazonaws.com\", \"ns3.amazonaws.com\"] }"
^^^^^ ^^^^
And there is no way that:
Fn::Sub:
- "{ \"someList\": \" <.....> \" }"
# ^^^^^ ^^^^^
- ....
Is going to give me that, because the quotes it adds around the value are already incorrect. I know the transform assumes that all values it can't see yet are going to be strings, but that is exactly the problem!
There is no way this issue can be solved by a transform. { Fn::ToJsonString }
needs access to the actual value to do the right jsonification operation.
You might be tempted to say you could trace the types of all attributes and try to do something clever in the transform such as omitting the quotes...
...but in CloudFormation Registry resources it's possible to {Fn::GetAtt}
complex objects, and arrays of complex objects, and there's definitely no way that a transform is going to know the shape of format string to produce to accurately stringify any data structure of variable size.
(We struggle with this in CDK as well, which is why I'm hoping to offload the work onto a CloudFormation intrinsic 🥹)
Having the same issue to retrieve the AmqpEndpoints attribute from AWS::AmazonMQ::Broker which returns and object and can't stringfy it to save on a parameter store.
RabbitMQEndpointParameter:
Type: AWS::SSM::Parameter
Properties:
Description: "AWS RabbitMQ Endpoint"
Name: !Sub "/myPath/rabbitmq/endpoint"
Type: StringList
Value:
Fn::ToJsonString:
!GetAtt AWSRabbitMQ.AmqpEndpoints
Community Note
Tell us about the bug
When
Fn::ToJsonString
is given an array retrieved from a resource using{ Fn::GetAtt }
, the deployment fails with the following error:Expected behavior
I want to see a JSON-ified representation of the array.
Observed behavior
Deployment fails with an error.
Test cases
Additional context
I'm from the CDK team. It would make our life A LOT simpler if we could rely on CloudFormation built-ins to produce JSON strings from object literals (given that 99.9% of AWS APIs run on JSON 😅), but limitations of the
Fn::ToJsonString
functions make it unreliable, and therefore impossible for us to use (since the CDK middleware will not have the context that a human author would have on whether it's okay to use the function in this particular use case or not).