In an AWS CloudFormation template, what is the difference between Ref and Fn::GetAtt when applied to a resource, and how do you know which one gives you that resource's ARN?
answer
- one is the type's default value
- the other names a specific attribute
- the resource docs decide, not a rule
- ARN is almost never the default
- role Ref hands back the name
basics
~20 sRef returns one default value chosen by the resource type — usually its name or physical ID — while Fn::GetAtt returns a specific named attribute of that resource. Only the resource type's documentation says which value each one produces.
solid answer
~40 sEvery CloudFormation resource type defines two things: what `Ref` returns for it, and which attribute names `Fn::GetAtt` accepts. `Ref` gives you a single default value that the type picks — for `AWS::S3::Bucket` it is the bucket name, for `AWS::IAM::Role` the role name, for `AWS::EC2::Instance` the instance ID, and for `AWS::SQS::Queue` it is the queue URL rather than a name. `Fn::GetAtt` takes a logical ID plus an attribute, like `!GetAtt DataBucket.Arn` or `!GetAtt Db.Endpoint.Address`, and returns exactly that attribute. So the ARN is almost always a `GetAtt` on the `Arn` attribute, not a `Ref`. `Ref` also works on parameters, where it returns the parameter's value; `Fn::GetAtt` does not — it only addresses resources. Both create an implicit dependency, so the referenced resource is created first.
code
yaml · 27 linesAWSTemplateFormatVersion: '2010-09-09'
Resources:
DataBucket:
Type: AWS::S3::Bucket
ReaderRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: read-objects
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:GetObject
Resource: !Sub '${DataBucket.Arn}/*'
Outputs:
BucketName:
Value: !Ref DataBucket
BucketArn:
Value: !GetAtt DataBucket.Arngo deeper
Be able to say that Ref returns a resource's default value while Fn::GetAtt returns a named attribute, and that an ARN normally comes from GetAtt with the Arn attribute.
Explain that the return value is defined per resource type, quote a couple of the surprising ones such as an SQS queue returning its URL, and show the dotted attribute path for nested values like an RDS endpoint.
Show how these references build the dependency graph, and be ready to debug a failed deployment back to a Ref used where an attribute was required, or a circular reference between two resources.
Talk about how reference style shapes a template estate: constructing ARNs by hand with Fn::Sub versus reading them from attributes, and what that costs you when a partition, region or account boundary changes.
## The two lookups a template can do A CloudFormation template is written before any of the resources exist, so almost every interesting value — an ARN, a DNS name, a generated bucket name — is unknown when you type the file. Intrinsic functions are the placeholders that get filled in during deployment, and the two you reach for constantly are `Ref` and `Fn::GetAtt`. `Ref` takes one argument: a logical name. If that name belongs to a **parameter**, `Ref` returns the value supplied for that parameter at deploy time. If it belongs to a **resource**, `Ref` returns the one value that the resource type declares as its default return value. `Fn::GetAtt` takes two arguments: a resource's logical ID and the name of an attribute the resource type publishes. It returns exactly that attribute. ```yaml Outputs: Name: Value: !Ref DataBucket # the bucket name Arn: Value: !GetAtt DataBucket.Arn # arn:aws:s3:::my-stack-databucket-1a2b3c ``` ## "What does Ref return" has no general answer This is the part candidates get wrong. There is no rule like "Ref returns the ID" or "Ref returns the ARN". Each resource type in the AWS resource and property reference has a **Return values** section that states, for that type alone, what `Ref` yields and which `Fn::GetAtt` attributes exist. A few worth knowing because they come up: - `AWS::S3::Bucket` — `Ref` returns the bucket name; `GetAtt` offers `Arn`, `DomainName`, `RegionalDomainName`, `WebsiteURL`. - `AWS::IAM::Role` — `Ref` returns the **role name**, `GetAtt ... .Arn` returns the ARN. Passing `!Ref MyRole` where an ARN is expected is the single most common CloudFormation bug of this shape. - `AWS::EC2::Instance` — `Ref` returns the instance ID. - `AWS::SQS::Queue` — `Ref` returns the **queue URL**, not a name and not an ARN; the ARN is `GetAtt ... .Arn` and the name is `GetAtt ... .QueueName`. Because the answer is per-type, the honest interview answer is "I look it up" — and knowing that you have to look it up is the point being tested. ## Nested attributes and syntax forms Some attributes are structured, and `Fn::GetAtt` addresses them with a dot path. An RDS instance publishes `Endpoint.Address` and `Endpoint.Port`: ```yaml DbHost: !GetAtt AppDatabase.Endpoint.Address ``` In YAML there are two spellings. The short form `!GetAtt Logical.Attribute` splits on the first dot; the long form takes an explicit list, `Fn::GetAtt: [Logical, Endpoint.Address]`, which is what JSON templates must use. `Ref`'s long form is `Ref: Logical`. Inside `Fn::Sub` you get both behaviours in one string: `${Logical}` behaves like `Ref` and `${Logical.Attribute}` behaves like `Fn::GetAtt`, which is why `!Sub '${DataBucket.Arn}/*'` is the idiomatic way to build an object-level ARN. ## Both build the dependency graph A `Ref` or `Fn::GetAtt` pointing at another resource does more than fetch a value: it tells CloudFormation that this resource depends on that one. CloudFormation orders creation from that graph and creates unrelated resources in parallel; on delete it works the graph in reverse. That is why an explicit `DependsOn` is only needed when a real ordering requirement exists that no reference expresses. It also means a mutual reference is fatal — if A refs B and B refs A, the stack fails with a circular dependency error, and the fix is to remove one of the references rather than to add ordering. ## Where each one is legal `Ref` works on parameters, resources and pseudo parameters (`!Ref AWS::Region`). `Fn::GetAtt` works only on resources in the same template — there is no `GetAtt` on a parameter, and no `GetAtt` on a resource that lives in another stack. Both may be used in resource properties, in `Outputs`, and inside other intrinsic functions. Neither may be used in the `Conditions` section to reach a resource attribute, because conditions are evaluated before anything is created. ## The habit to build When a property wants an ARN, reach for `GetAtt ... .Arn` first and only fall back to `Ref` if the type's documentation says `Ref` returns an ARN. When a property wants a name or an ID, `Ref` is usually right. And when a deployment fails with a validation error about a malformed ARN or an unrecognised identifier, the first thing to check is whether a `Ref` was used where an attribute was needed.
- Does the choice between Ref and Fn::GetAtt change the order in which CloudFormation creates resources?Not the choice between them — but using either one against another resource creates an implicit dependency, so the referenced resource is created first and deleted last. Resources with no reference between them are created in parallel in no guaranteed order. That is also why two resources referencing each other fail with a circular dependency error.
- Why does Ref on an AWS::SQS::Queue surprise people?Because it returns the queue **URL**, not a name or an ARN. Anything that wants a queue ARN — an SNS subscription, a Lambda event source mapping, an IAM policy Resource — needs `!GetAtt MyQueue.Arn`, and passing the URL there fails validation or produces a policy that matches nothing.
- How would you reference an RDS instance's hostname from elsewhere in the same template?With a dotted attribute path: `!GetAtt AppDatabase.Endpoint.Address`, and `!GetAtt AppDatabase.Endpoint.Port` for the port. In JSON, or when the attribute itself contains a dot, use the list form `Fn::GetAtt: [AppDatabase, Endpoint.Address]` because the short form splits on the first dot only.
saying these in an interview costs you the question
- Claiming Ref always returns the resource ARN
- Saying Ref and Fn::GetAtt are interchangeable
- Using Fn::GetAtt on a template parameter
- Guessing attribute names instead of checking the resource type docs
- Putting !Ref MyRole in a policy Resource field expecting an ARN