philolegein
philolegein

Reputation: 1515

GetAtt Outputs vs. ImportValue

I'm sure I'm missing something quite obvious here, but what is the difference between getting an output value in a parent template of a stack with GetAtt and ImportValue? Is there a time and place where you can only use one or the other? Are there places where they're interchangeable, but one is preferable for some reason?

For example, in a parent template:

myvpc:
  Type: AWS::CloudFormation::Stack
  <some stuff to pass to a child template>

anotherresource:
  Type: AWS::CloudFormation::Stack
  Properties:
    Parameters:

Then either

      Subnet:
        !GetAtt [mypvc, Outputs.publicsubnet1]

or

      Subnet:
        !ImportValue 'MyVpcStackName:publicsubnet1'

Like I said, I'm probably missing something obvious, but I keep reading both document pages, and ... I'm not getting something.

Upvotes: 3

Views: 1938

Answers (2)

philolegein
philolegein

Reputation: 1515

OK, I think I at least partially figured this out.

Short answer: don't use ImportValue in a nested stack, because you can't, because you don't know the sub-stack's name.

Long answer: Apparently it comes down to information sharing. I don't know why I couldn't find this yesterday, but this doc says "if you want to isolate information sharing to within a nested stack group, we suggest that you use nested stacks [and GetAtt]. To share information with other stacks (not just within the group of nested stacks), export values [and ImportValue]." But it's not actually just about whether you want to share information, it's about what information you have available. Although it's not specified in Specifying Stack Name and Parameters, the CLI documentation explains that "The name must be unique in the region in which you are creating the stack.". To use ImportValue, you need to know the name of the stack from which you're importing, but when you create a nested stack, the sub-stacks are given randomly generated suffixes, to satisfy this constraint, so you don't actually know the name.

So, if you have

infrastructure-parent:
  -> vpc
  -> security-groups
  -> bastion
  -> NAT
  -> ALB
database-parent:
  -> RDS
  -> phpMyAdmin

For bastion to use something from security-groups, you use GetAtt. But for RDS to use something from vpc, you use Export/ImportValue, both because you can't use GetAtt, and because you don't know the name of the generated vpc stack (ergo, you create a vpcStackName parameter in RDS (and database-parent) to allow passing the name).

I guess the tricky part comes (and this is where my original confusion was generated) when you try to then nest multiple levels. So, if you take the above, and try to do

environment-parent:
  -> infrastructure-parent
  -> database-parent

the environment-parent needs to know the stack name that got generated for vpc, so it can pass it as a parameter to database-parent (which then passes it to RDS). Thus, the RDS and database-parent templates don't need to change, but the vpc template needs to Output the AWS::StackName pseudo parameter. infrastructure-parent can then get this using GetAtt, and then Output it again so that it's available for GetAtt in environment-parent, which can then pass it as a parameter to database-parent.

Upvotes: 4

Jason Wadsworth
Jason Wadsworth

Reputation: 8887

I think part of what is confusing you is that you probably are doing things with substacks that are illadvised. According to CloudFormation Best Practices

Use Nested Stacks to Reuse Common Template Patterns

If you do that, you shouldn't be exporting things from the substack, and you don't have the confusion.

Given what you are doing the reason you should still avoid the ImportValue is that you won't be able to delete the stack because of a self reference. I'm not sure you could even create it because I think it might evaluate the ImportValue early, but I'm not 100% sure on that.

Upvotes: 0

Related Questions