org-formation / org-formation-cli

Better than landingzones!
MIT License
1.41k stars 130 forks source link

Reference resources created with `ForeachAccount` #250

Open mbarneyjr opened 3 years ago

mbarneyjr commented 3 years ago

Reference resources created with ForeachAccount

When using the ForeachAccount declarative to provision a resource per account in the referenced OrganizationBinding, it's difficult (maybe impossible?) to dynamically reference the generated resources.

Let's say I want to create a peering connection for each spoke in a hub-and-spoke account model, I would have the following defined to create the N-number of peering connections in the spoke:

  PeeringConnection:
    Type: AWS::EC2::VPCPeeringConnection
    OrganizationBinding: !Ref HubAccountBinding
    ForeachAccount: !Ref SpokeAccountBinding
    Properties:
      ...

But from there I lose the ability to reference those resources on an individual level. This snippet produces generated resources that have logical IDs like: PeeringConnection000000000000, PeeringConnection111111111111, and PeeringConnection222222222222, so there's no good way to dynamically reference those (as far as I can tell). You'd need to be able to somehow specify the account when referencing the peering connection.

So let's say, for each PeeringConnection associated with an account from SpokeAccountBinding, I want to reference that in a route table within the SpokeAccountBinding, I'd imagine something along the lines of this would be enough information to derive what needs to be generated:

  PeeringRoute:
    Type: AWS::EC2::Route
    OrganizationBinding: !Ref SpokeAccountBinding
    ForeachAccount: !Ref HubAccountBinding
    Properties:
      ...
      VpcPeeringConnectionId: !Sub ${CurrentAccount.Resources.PeeringConnection}

This isn't exactly what you'd need, as the resource I want to reference is in the "CurrentAccount", but there's N-number of those resources I could reference, so we need a way to refine our selection to one of them. We need some way to say "Get me the !Ref/!GetAtt of a resource created in X account for Y account" - where the "for" part is really critical, as you want to reference the resource made through a ForeachAccount declaration, specifically one that was made from the given account. Like a CurrentAccount.Resources[AWSAccount].PeeringConnection, or something along those lines. It does get tricky because in this case you could pass two separate sets of bindings, which would make things extra weird - not that you should, but the error handling might be tricky

I'm happy to try to provide more clarity into what I mean, it's pretty theoretical, I had to go over it in my head a few times before I really knew what I needed 😅

OlafConijn commented 3 years ago

hi Barney!

yes, i get it. i also get your headache when thinking about this.

what i think would be interesting is to see whether it would be possible to unify the context used by ForeachAccount & bindings with the text-templating solution. I also do enjoy thinking about extending the org-formation syntax a lot but it leads us to an increasingly complex place.

my question would be: if we were to unify "bindings" and "templatingContext" how would that look?

thanks

mbarneyjr commented 3 years ago

Hmm, templating hadn't crossed my mind. My first thought to make them more integrated would be to be able to reference an OrganizationBinding's accounts like we do with the ${CurrentAccount} or ${AWSAccount} GetAtts (here), so maybe something like this?

{% for logicalId, account in MyAccountBinding %}
  SomeResource:
    Type: AWS::Some::Resource
    Properties:
      Name: resource-{{ account.AccountId }}-{{ account.Tags.budget }}
{% endfor %}

I'm not sure if the bindings would be available to the templating engine at the time it runs or not, but if we could do something like the above, that'd be pretty sweet