-
Notifications
You must be signed in to change notification settings - Fork 13
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
RFC 0009: Add looping functionality in CFN Templates #75
RFC 0009: Add looping functionality in CFN Templates #75
Conversation
I have a number of different pieces of feedback. NamingIn the issue I originally opened (#41), I proposed SyntaxIndex/value parameter namesI don't like the need to define the index and value parameter names. They're probably always going to be the same, it's just one more thing to type out and one more thing to go wrong. Could they be made optional, defaulting to some useful names? This brings me to my next point... Function parameter orderingThere are so many parameters to this function. I understand that all other intrinsic functions use positional parameters, but maybe this is a place to use named parameters. For example: !Foreach
Input: !Ref AmiIds
Value:
Type: AWS::EC2::Instance
Properties:
InstanceType: "m1.small"
ImageId: !Ref x
LogicalId: !Sub Instance${i}
ParameterNames: ["i", "x"] This would allow for multiple independently-optional parameters, like Use casesI think there's actually four use cases here.
Use cases 3 and 4 are both accomplished by the Here's I'd propose:
Reifying resource mappingIf this intrinsic function simply allows users to create multiple resources with an intrinsic function that shuffles YAML around, we're missing a giant opportunity. This proposal doesn't serve that purpose. I think it can be a separate RFC, but imagine something like Resources:
Subnets:
Type: CloudFormation::Map
Input: [...]
LogicalId: ... # actual logical id for the resource
ResourceValue: ...
# etc, similar syntax to the function If we agree that such a thing should exist, it will help focus the syntax of this intrinsic function on use cases 1-3 (where use case 3 serves to allow users to create a non-reified set of resources). |
|
NamingIssue #9 describes
This definition describes an iterator which implies functions like The term I agree with @benkehow |
Really appreciate all the feedback and engagement on this RFC! After some internal discussions, we've determined it needs significant rework. We're going to iterate a bit internally and then post a new revision for more feedback. Stay tuned! |
Hey all, I have posted a new revision addressing some of the feedback. This is a summary of the changes:
|
8b83aa0
to
a4a9628
Compare
RFCs/0009-Fn::For.md
Outdated
{ | ||
"Fn::Map": { | ||
"index": "Index", | ||
"value": "Value", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things I'm wondering:
- Would it be clearer to use
IndexParameter
andValueParameter
for the keys (to make it clear that you're configuring something you will!Ref
later) - Would it be clearer to use
Item
instead ofValue
? This might be related to the languages I've been exposed to, but I'd use Key-Value or Index-Item, not usingValue
prevents confusion with theKey
parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think "key" and "value" if they are both used should be outputs, currently "fragment" is used for the output value. We've got four parameters here:
- input value from the input collection (currently "value")
- index of the input value (currently "index")
- output value (currently "fragment")
- optional output key (currently "key")
Maybe making them more explicit makes sense?InputIndex
,InputValue
,OutputValue
,OutputKey
? It would leave open the possibility that in the future the collection could be an object, which would would then involve anInputKey
parameter as well.
RFCs/0009-Fn::For.md
Outdated
* Caveat: Make sure Logical Ids are alphanumeric characters | ||
|
||
#### SSM/AWS List Type Parameters | ||
* SSM/AWS List type parameters can be referenced in `Fn::Map`. Only List type parameter is allowed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add an example of using !Split to convert a string to a list?
RFCs/0009-Fn::For.md
Outdated
Properties: | ||
SecurityGroups: | ||
- Fn::Map: | ||
collection: [!Ref SecurityGroup] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!GetAtt x.DnsEntries
of a AWS::EC2::VPCEndpoint
is also an interesting example
Really appreciate your feedback @benbridts and @benkehoe |
re-posting this as a general comment so it doesn't get lost on an old revision: from @benbridts
I think "key" and "value" if they are both used should be outputs, currently "fragment" is used for the output value. We've got four parameters here:
Maybe making them more explicit makes sense? |
Re-posting this as a general comment so it doesn't get lost on an old revision: Both @benbridts and I commented that we think the parameters should be CamelCase to be consistent with CloudFormation conventions |
@MalikAtalla-AWS FYI amending the commit and pushing means comments on previous commits are no longer visible in the diff, I'd recommend creating new commits and only squashing when it's ready for merging |
(and collection, but that one seems to be clear)
the current Since the current default for I'm not the biggest fan of using "Output" in the names for fragment and key, but if you translates what happens to python, it makes sense to use def cfn_map(collection: List, key: str, fragment: str):
return {key.format(i=i, x=x): fragment.format(i=i, x=x) for i, x in enumerate(collection)}
# or with different names
def cfn_map(collection: List, key: str, value: str):
return {key.format(i=i, x=x): value.format(i=i, x=x) for i, x in enumerate(collection)} This leads me to thinking something like
|
Update: The AWS CloudFormation team really appreciates the interest and feedback we've received on this RFC! In the past months, our team did a deep dive to better understand specific customer pain points and brainstormed ideas with customers and community representatives. Based on this deep dive, we are updating this RFC with a proposal to add a single |
…ould be provided in the OutputKey
…including comments
Co-authored-by: Malik <60721392+MalikAtalla-AWS@users.noreply.github.com>
"Identifier", | ||
["Value1", "Value2"], ## Collection | ||
{ | ||
"OutputKey": "OutputValue" ## Ex: {"OutputKeyName${Identifier}": "OutputValue"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using only a single item in the object could be read as only permitting one item; it might be useful to include two items, or a comment indicating more items are possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense! I’ve updated the syntax to include a comment to indicate there can be multiple key-value pairs within the 3rd positional parameter
RFCs/0009-Fn::ForEach.md
Outdated
"Resources": { | ||
"Fn::ForEach::Buckets": [ | ||
"Identifier", | ||
["A", "B", "C"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this example might be better with the collection from a Mapping, so that it's not duplicated between the two?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that would be better, so I’ve updated the example accordingly
"Outputs": { | ||
"SecondInstanceId": { | ||
"Description": "Instance Id for InstanceB", | ||
"Value": {"Ref": "InstanceB"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels gross, having to reference a generated identifier, relative to something that is more like !Select [1, !Ref Instances]
(the ForEach name has to be a unique identifier in the template namespace, right?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the ForEach name has to be a unique identifier in the template namespace (see Constraint 3 in RFC), where based on the number of asks for the feature, !Ref on the ForEach name, which would return a list of logicalId’s generated by ForEach, can be implemented in the future (see GitHub issue here with details on Additional Enhancements to ForEach)
RFCs/0009-Fn::ForEach.md
Outdated
|
||
## Potential Follow-up Features | ||
|
||
Features like supporting iterating over a key-valur pair or allowing Ref/Fn::GetAtt on the UniqueLoopName are out of scope for this RFC. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo on "key-valur pair"
Needed in the FAQ, because people will try this immediately:
|
RFCs/0009-Fn::ForEach.md
Outdated
|
||
|FunctionName |Pros |Cons | | ||
|--- |--- |--- | | ||
|Fn::ForEach |Addresses valid feedback in GitHub pull requestIntrinsic function name indicates it’s a functional concept and not an imperative oneForEach Illustrates that we’re operating on a template fragment over a set of valuesGives customers familiar with coding concepts the idea that some form of “looping” (or replication) is done |Intrinsic function name is potentially not unclear to customers who aren’t familiar with coding concepts; however, the syntax of Fn::ForEach::<ResourceLogicalId> would give users an idea that within the function, we define the template fragment "for each" resource | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The formatting on these table entries is a mess
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! Looks like the resolved markdown looks ok, but I've updated the raw markdown to be clean as well.
RFCs/0009-Fn::ForEach.md
Outdated
@@ -0,0 +1,1461 @@ | |||
# Looping functionality for CFN Templates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy with Fn::ForEach
as a name, but I'd prefer to stay away from "loop" as much as possible, if there's a way to do that. At least use it as an opportunity to help users understand that they're using it in a functional programming way, rather than an imperative one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense! I've updated the title to not mention "looping", to ensure it isn’t misunderstood as being an imperative solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably impossible to avoid completely, but as a general direction I think it's wise
Are there outstanding concerns preventing the assigned reviewers from approving this? I'm looking forward to taking advantage of this capability, and am hoping it can soon move towards implementation. |
Thanks for the callout @rhbecker! Believe there aren't any outstanding concerns, so I'll re-request reviews and will merge the pull request if I receive the necessary approvals. |
Sorry to be a pest, but if I'm reading the tracking issue #9 correctly, in order to keep things moving, I think someone needs to add an |
You're right @rhbecker. Appreciate the reminder. We'll take care of that. |
🤔 Maybe instead of pestering you about labels (which still don't seem quite right on this or the tracking issue), I should instead ask whether implementation is fully unblocked. My fear is that this issue is stuck in some queue, due to a "paperwork" blockage, when it could otherwise be ready for some hungry CFN engineer to start working on. If it's already unblocked, or perhaps even already in progress, I can sit back, relax, and patiently (🤞) await completion. |
Hey @rhbecker, thanks for following up. This issue is and has been unblocked. We have set the status of the RFC to "implementing" in the main readme of this repo and I (just now) added the approved-label to this PR. (Maybe we should consider removing that line about the approved label from our readme. The info in the main readme and the fact that the PR is merged is probably indication enough about the status.) |
Language Enhancement Request For Comment
This is a request for comments about a new intrinsic function called
Fn::ForEach
to support a looping functionality in CloudFormation templates. See the tracking issue (#9) foradditional details.
Licensing
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.