- I decided to use the
Fn::Functionform in most cases, as I didn't want to deal with when you're allowed or not allowed to nest them. If you want them all in the short format, you can flip it twice with cfn_flip - When writing, I think about it from the bottom the the top / from the inside to the outside
- There is a summary at the end, with the steps instead of the values as comments
The trick is that splitting on a token, or seeking to the first / second / ... token in a string is very similar.
If we split on something unique, we get everything before token and everything after token
!Split ['b:', "a:1,c:3,b:2"]
#-> ["a:1,c:3,", "2"]
!Split ['c:', "a:1,c:3,b:2"]
#-> ["a:1,", "3,b:2"]You can see that if we don't select the last value, we end up with more than we needed. We can do that same thing again with a different token, and instead of keeping the last part, only keeping the second part.
Dealing wiht two different cases is annoying, so we need a way to have the same behaviour for any element. Adding a trailing , would accomplish that, by adding unneeded data to the end, making it look as if there is another element there.
# Note that in both cases we end up with the value we need as the first element in the list
!Split ['b:', "a:1,c:3,b:2,"]
#-> ["a:1,c:3,", "2,"]
!Split [',', "2,"]
#-> ["2", ""] # I'm not sure if you get an empty last element or not
!Split ['c:', "a:1,c:3,b:2,"]
#-> ["a:1,", "3,b:2,"]
!Split [',', "3,b:2,"]
#-> ["3", "b:2", ""] # I'm not sure if you get an empty last element or notThe goal is to transform the !GetAtt Firewall.EndpointIds output to a per-az endpoint id.
I'll be filling in concrete values to show the process
We start with this
!GetAtt Firewall.EndpointIds
#-> ["us-west-2c:vpce-111122223333", "us-west-2a:vpce-987654321098", "us-west-2b:vpce-012345678901"]We can't do a Split on a list, so we need to convert this to a string first. The / is arbitrary, but it easier to reason about (and safer) if it's not part of the strings in the list
!Join ['/', ["us-west-2c:vpce-111122223333", "us-west-2a:vpce-987654321098", "us-west-2b:vpce-012345678901"]]
#-> "us-west-2c:vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2b:vpce-012345678901"To make sure we don't have a special case when we select the last element, we can add a / to the end. I added one to the front as well, because I wasn't sure if you'd be able to split at the start of a string. That wasn't needed, but it doesn't hurt either
"Fn::Sub":
- "/${x}/"
- x: "us-west-2c:vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2b:vpce-012345678901"
#-> "/us-west-2c:vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2b:vpce-012345678901/"Now we can do our trick, in an ideal world, we'd use us-west-2a: as a token, but you can't use functions there, and we don't want to hardcode the region. a: is unique enough for this case though. It's the second element in this list that's important for us. Notice how it always starts with vpce-...
"Fn::Split":
- "a:"
- "/us-west-2c:vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2b:vpce-012345678901/"
#-> ["/us-west-2c:vpce-111122223333/us-west-2", "vpce-987654321098/us-west-2b:vpce-012345678901/"]
"Fn::Split":
- "b:"
- "/us-west-2c:vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2b:vpce-012345678901/"
#-> ["/us-west-2c:vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2". "vpce-012345678901/"]
"Fn::Split":
- "c:"
- "/us-west-2c:vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2b:vpce-012345678901/"
#-> ["/us-west-2", "vpce-111122223333/us-west-2a:vpce-987654321098/us-west-2b:vpce-012345678901/"]In more concrete terms, the second element in that list starts with the string we need and a /
We can first select that element
"Fn::Select":
- 1
- ["/us-west-2c:vpce-111122223333/us-west-2", "vpce-987654321098/us-west-2b:vpce-012345678901/"]
#-> "vpce-987654321098/us-west-2b:vpce-012345678901/"Split it again
"Fn::Split":
- '/'
- "vpce-987654321098/us-west-2b:vpce-012345678901/"
#-> ["vpce-987654321098", "us-west-2b:vpce-012345678901/"]And only keep the first element
"Fn::Select":
- 0
- ["vpce-987654321098", "us-west-2b:vpce-012345678901/"]
#-> "vpce-987654321098"# Read comments from bottom to top
"Fn::Select": # keep the first element
- 0
- "Fn::Split": # Split on the seperator, to remove everything after the value
- '/'
- "Fn::Select": # Keep the part that contains the value
- 1
- "Fn::Split": # Split on the (end of the) key we want to select
- "a:"
- "Fn::Sub": # Make the last element look the same as any other element (after the split on x:)
- "/${x}/"
# Create a string we can manipulate
- x: !Join ['/', !GetAtt Firewall.EndpointIds]