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

Fn::Map intrinsic function #41

Open
benkehoe opened this issue Oct 14, 2021 · 6 comments
Open

Fn::Map intrinsic function #41

benkehoe opened this issue Oct 14, 2021 · 6 comments
Labels
feature request Proposal for new features or requests NeedTriage other

Comments

@benkehoe
Copy link

benkehoe commented Oct 14, 2021

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"
@bjorg
Copy link

bjorg commented Oct 16, 2021

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 ] 

@benbridts
Copy link

@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

@txase
Copy link

txase commented Oct 16, 2021

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?

@benkehoe
Copy link
Author

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"

@txase
Copy link

txase commented Oct 16, 2021

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!

@lejiati
Copy link
Contributor

lejiati commented May 10, 2022

@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.

@lejiati lejiati transferred this issue from aws-cloudformation/cloudformation-coverage-roadmap May 10, 2022
@muneebar muneebar added feature request Proposal for new features or requests and removed enhancement New feature or request labels May 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Proposal for new features or requests NeedTriage other
Projects
None yet
Development

No branches or pull requests

8 participants