cfn-language-discussion
cfn-language-discussion copied to clipboard
Fn::Map intrinsic function
Resource Name
No response
Details
A lot of complaints about CloudFormation verbosity are things like having to replicate subnets for each availability zone. An Fn::Map function, which allowed basic parameterization of resources or properties, would allow for concise, semantically-meaningful expression of duplication.
A twitter thread on this topic
An example from here
Resources:
Fn::Map::EfsMountTargets:
# this assigns each item from Subnets to x while iterating
Map: {x: !Ref Subnets}
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSVolume
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId: !Ref x
Outputs:
Fn::Map::EfsMountIpAddress:
Map: {x: !Ref Subnets}
Description: !Sub "Ip Address for ${x}"
# this might be better without the !Sub, assuming !GetAtt/Ref knows to resolve that in the Map
Value: !GetAtt !Sub "EfsMountTargets.${x}.IpAddress"
This could be equivalent to
Resources:
# the part after the . can be anything; as long as the same input keeps the same resource name
# using the value as-is might help with linking mapped resources (see outputs)?
EfsMountTarget.eu-west-1a:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSVolume
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId: eu-west-1a
EfsMountTarget.eu-west-1b:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSVolume
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId: eu-west-1a
Outputs:
EfsMountIpAddress.eu-west-1a:
Description: Ip Address for eu-west-1a
Value: !GetAtt EfsMountTargets.eu-west-1a.IpAddress
EfsMountIpAddress.eu-west-1b:
Description: Ip Address for eu-west-1b
Value: !GetAtt EfsMountTargets.eu-west-1b.IpAddress
The above assumes that Map was a first-class property of resources, for convenience. The general-purpose intrinsic function could look like this, including the ability to zip multiple lists together:
# with !Ref AZs being [a, b]
!Map:
- AZ: !Sub "${AWS::Region}${x} - ${y}"
- x: !Ref AZs
y: [ one, two ]
would be equivalent to
- !Sub "${AWS::Region}a - one"
- !Sub "${AWS::Region}b - two"
Agree that map is the way to go over loops. You should be able to prototype this as a transform, correct? That way, you could validate the notation on concrete examples.
Question: could it be possible to collect attributes from all mapped resources into a list?
For example, the following YAML expression:
!GetAttr EfsMountTarget.IpAddress.IpAddress
would become
[ !GetAttr EfsMountTarget.eu-west-1a.IpAddress.IpAddress, !GetAttr EfsMountTarget.IpAddress.eu-west-1b.IpAddress ]
@bjorg as long as you still have the list of things you're mapping, that should be possible.
Assuming the syntax of
# with !Ref AZs being [a, b]
!Map:
- AZ: !Sub "${AWS::Region}${x}"
- x: !Ref AZs
you could write that as
!Map:
- Ip: !GetAtt EfsMountTarget.$x.IpAddress
- x: !Ref AZs
sidenote @benkehoe : I forgot why that's not
# with !Ref AZs being [a, b]
!Map:
- !Sub "${AWS::Region}${x}"
- x: !Ref AZs
Sometimes you need to map over an array where each element has more than one value. Maybe you need to map over an array of subnets that contains subnet IDs and AZs together. Each map iteration may need both of these values for different properties of the resource. Do you have a good idea of how to accomplish this?
The idea was that the second parameter to Fn::Map is a mapping from key to list value, and there could be multiple keys, and then it would match the values in the list. Potentially there would need to be a parameter that controlled that behavior (e.g., zip to shortest, zip to longest, all combinations)
# with !Ref AZs being [a, b]
!Map:
- AZ: !Sub "${AWS::Region}${x} - ${y}"
- x: !Ref AZs
y: [ one, two ]
would be equivalent to
- !Sub "${AWS::Region}a - one"
- !Sub "${AWS::Region}b - two"
My suggestion would be to make the multi-prop capability explicit in the GH issue summary. Maybe as simple as making the examples multi-prop would work. It’s obvious in hindsight, but wasn’t on my first read!
@benkehoe Thank you very much for your feedback! Since this repository is focused on resource coverage, I'm transferring this issue over to a new GitHub repository dedicated to CloudFormation template language issues.
And we have an RFC for adding looping functionality to CFN template.