cfn-language-discussion
cfn-language-discussion copied to clipboard
Using Intrinsic Functions in JSON Keys
Community Note
- Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
- Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
- If you are interested in working on this issue or have submitted a pull request, please leave a comment
Security disclosures
If you think you’ve found a potential security issue, please do not post it in the Issues. Instead, please follow the instructions here or email AWS security directly.
Request
In CloudFormation certain resource properties in require input as a Map of String
such as DockerLabels, RequestTemplates. For these properties it's not possible to use intrinsic functions for the 'keys'. Requesting Support for Intrinsic functions in template JSON keys.
Tell us about the problem you are trying to solve. What are you trying to do, and why is it hard?
For example, I'd like to create an ECS Task Definition resource by specifying DockerLabels
as such:
- Using an intrinsic function in the JSON key:
DockerLabels:
!Sub "Production-${AppName}": !Ref Priority
Using this specification fails with a validation error Template format error: [/Resources/TaskDefinition/Type/ContainerDefinitions/0/DockerLabels] map keys must be strings; received a map instead
- Using the Fn::Sub at the property level as such:
DockerLabels:
Fn::Sub:
- |
Production-${AppName}: ${Priority}
- AppName: "Traffic"
Priority: "2"
This fails during resource creation with the error #/ContainerDefinitions/0/DockerLabels: expected type: JSONObject
Are you currently working around this issue?
The only workaround is to manually hardcode the map of strings or using a custom-macro to process the template parameters in to a dynamic-map.
What is the expect behavior with this new feature
DockerLabels:
!Sub "Production-${AppName}": !Ref Priority
This should be able to dynamically collapse to:
DockerLabels:
"Production-Logging" : 2
I'm not sure if YAML supports tags on keys, which would prevent this syntax. I believe the way YAML works, the entire value after the tag is passed to it. So:
DockerLabels:
!Sub "Production-${AppName}": !Ref Priority
can only really be equivalent to
DockerLabels:
Fn::Sub:
"Production-${AppName}": !Ref Priority
I think I'd prefer a more explicit form than your positional arguments, perhaps something like
DockerLabels:
!Sub
Key: Production-${AppName}
Value: ${Priority}
# Parameters: (if needed)
Aren't you missing a :
after !Sub
?
!Sub
is a YAML tag. It is a marker that the YAML parser uses to interpret the YAML data that follows the tag. So !SomeTag foo
is a YAML snippet that has the SomeTag tag applied to the string foo
. In CloudFormation's case, the parser pretty much only does one thing, which is convert !TagName <data>
to {"Fn::TagName": <data>}
(with the exception that !Ref
becomes "Ref"
not "Fn::Ref"
.
Currently, all the tags in use only allow the data following the tag to be a string (for a single parameter) or a list (for multiple parameters). My example uses an object, which is essentially using multiple named parameters.
Right, the unusual vertical space threw me off. Yes, that looks fine upon closer inspection (and I'm aware of YAML tags and how they generally work). Thanks for the quick and thorough explanation, Ben!
Being very specific to the property DockerLabels
. The property only accepts a map-of-strings so in that case the following specification will not work as Fn::Sub
returns a string and this case it would be a stringified JSON (For example: {\n \"Key\": \"test\",\n \"Value\": \"test\"\n}
)
DockerLabels:
!Sub
Key: Production-${AppName}
Value: ${Priority}
# Parameters: (if needed)
One solution I can think of is - the way the AWS::ECS::TaskDefinition
resource provider is implemented needs to change from accepting a Map of Strings
to using a List of Tags data-type. That way, I could author my template as such:
DockerLabels:
- Key: !Sub Production-${AppName}
Value: !Sub ${Priority}