`Ref` and `Fn::GetAtt` are CloudFormation's two ways to read a value from another part of the template. They are not interchangeable — they return different things, and the resource-type docs are the only authority on which is needed.
## What `Ref` returns
`Ref` takes one argument: a logical name. What it returns depends on **what kind of thing the logical name refers to**:
| Logical name refers to | `Ref` returns |
|------------------------|---------------|
| A **Parameter** | The parameter's value (string, number, list, etc.) |
| A **Resource** | The resource's "default identifier" — usually the physical ID/name/ARN — varies per resource type |
| A **Pseudo parameter** | The pseudo parameter's value (e.g., `!Ref AWS::Region` → `us-east-1`) |
| A **Condition** | Reserved for use with the `Condition` key, not in property values |
The resource-default-identifier varies. Examples:
- `AWS::EC2::Instance` → instance ID (`i-1234...`)
- `AWS::S3::Bucket` → bucket name (`my-bucket`)
- `AWS::IAM::Role` → role name (not ARN)
- `AWS::SNS::Topic` → topic ARN (yes ARN, unlike IAM Role)
The choice of "default attribute" is per-resource and inconsistent — always check the resource's "Return values" section in the AWS docs.
## What `Fn::GetAtt` returns
`Fn::GetAtt` takes two arguments: logical resource name + attribute name. It returns a **specific named attribute** of that resource:
```yaml
!GetAtt MyBucket.Arn # ARN, when Ref returns just bucket name
!GetAtt MyDB.Endpoint.Address # nested attribute via dot notation
!GetAtt MyLB.DNSName # DNS name (not in Ref)
!GetAtt EC2Instance.AvailabilityZone
```
JSON syntax differs — list of two strings:
```json
{ "Fn::GetAtt": ["MyDB", "Endpoint.Address"] }
```
YAML supports the dotted shorthand `!GetAtt LogicalName.Attribute` or the long form `!GetAtt [LogicalName, Attribute]`.
## Decision rule
1. Need the resource's default identifier? → `!Ref`
2. Need any other attribute? → `!Fn::GetAtt`
3. Reading a Parameter's value? → always `!Ref`
4. Reading a pseudo parameter? → always `!Ref` (or via `!Sub`'s `${}` syntax)
When in doubt, look up the resource type's "Return values" section. It lists `Ref` (the default) and `Fn::GetAtt` (every other accessible attribute).
## Cascading effect
Both `Ref` and `Fn::GetAtt` create an **implicit dependency** between resources. CFN walks these references to determine creation order. When the referenced resource is replaced (see [[CFN Update Behaviors and the Replacement Trap]]), the referencing resource is updated too — even if its template definition didn't change. This cascade can be surprising.
To make a dependency explicit without using `Ref`/`GetAtt`, use the `DependsOn` resource attribute. To break a circular dependency, factor through Outputs/Exports.
## Related
- [[CFN Fn-Sub String Interpolation]]
- [[CFN Pseudo Parameters]]
- [[CFN Cross-Stack References Outputs and ImportValue]]
- [[CFN Update Behaviors and the Replacement Trap]]