A **template** is the declarative spec for a stack. It's a text file (YAML or JSON) that describes **what AWS resources you want and how they relate** — not how to create them. CloudFormation reads the template and figures out the rest.
## The mental model — the meal analogy
| Thing | Role |
|-------|------|
| **Template** | The recipe (text, edit it freely, copy it, share it, version-control it) |
| **Stack** | The cooked meal (the deployed instance, CFN-managed, has a state) |
| **Resources** | The food (the actual EC2 instances, S3 buckets, etc.) |
The recipe doesn't change when you cook from it. You can cook the same recipe into many meals. You can edit the recipe and re-cook to update what's on the plate.
## What a template is, concretely
A text file (`.yaml`, `.json`, or `.txt`), typically tens to thousands of lines, with up to **nine top-level sections** (covered in [[CFN Template Structure Nine Sections]]). Only `Resources` is required:
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: A web server
Parameters:
InstanceType:
Type: String
Default: t2.micro
Resources:
WebServer:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
ImageId: ami-0abcdef1234567890
Outputs:
ServerId:
Value: !Ref WebServer
```
That's a complete, valid template. CFN can deploy it as a stack.
## Declarative, not imperative
This is the key distinction from a shell script.
A **shell script** says: "Run `create-vpc`, then `create-subnet`, then `create-security-group`, then `run-instances` with these IDs from the previous calls."
A **template** says: "I want a VPC, two subnets, a security group, and an instance. The instance uses the security group. The subnets live in the VPC."
CFN reads the template, walks the references (`!Ref`, `!GetAtt`), builds a **dependency graph**, and figures out:
- What order to create things (VPC before subnets, subnets before instance)
- What can run in parallel (multiple subnets at once)
- What to pass where (the security group's physical ID into the instance's properties)
You never write the orchestration code. The template is the description; CFN is the engine.
## What CFN extracts from a template
When you submit a template, CFN parses it into:
1. **A dependency graph** — derived from `!Ref`, `!GetAtt`, `!Sub`, and explicit `DependsOn` declarations. Determines create / update / delete order.
2. **A logical resource set** — the resources you've named (`MyVPC`, `WebServer`) with their types and desired properties.
3. **A parameter contract** — what runtime inputs are required, with their types and validation rules.
4. **A condition logic tree** — boolean expressions controlling which resources/properties materialize.
5. **An output contract** — what values the stack will publish after creation.
The stack record (see [[CFN Stack Concept]]) is then built from this parse + the actual provisioning results.
## What a template is NOT
- **Not executable.** You can't "run" a template. You hand it to CFN, which runs it on your behalf.
- **Not stateful.** The template doesn't know whether it's been deployed. State lives in the stack.
- **Not coupled to a stack.** The same template can deploy as `webapp-dev`, `webapp-staging`, `webapp-prod` — three independent stacks from one file.
- **Not validated for semantics.** `validate-template` only checks JSON/YAML syntax. It does NOT check whether your AMI exists, your VPC has capacity, or your IAM permissions allow the operation. Those errors only surface at create time.
- **Not a deployment artifact in the CI sense.** A template doesn't get "released" or "promoted" — those are deployments of the template, each producing or updating a stack.
## Where templates live
Three places CFN can read from:
1. **Local file** — passed via `--template-body file://app.yaml` (size limit: ~51 KB)
2. **S3 bucket** — passed via `--template-url https://...` (size limit: 1 MB; required for large templates)
3. **Git repository** — via Git sync, CFN watches a branch and triggers stack updates on commit
Most teams keep templates in Git for review/history, then sync to S3 for deployment.
## One template, many stacks
The reuse pattern that makes templates powerful. The same `app.yaml` can produce:
```bash
aws cloudformation create-stack --stack-name app-dev --template-body file://app.yaml --parameters ParameterKey=EnvType,ParameterValue=dev
aws cloudformation create-stack --stack-name app-prod --template-body file://app.yaml --parameters ParameterKey=EnvType,ParameterValue=prod
aws cloudformation create-stack --stack-name app-eu --template-body file://app.yaml --parameters ParameterKey=EnvType,ParameterValue=prod --region eu-west-1
```
Three stacks, three sets of resources, one template. Differences come from `Parameters` and `Conditions` (see [[CFN Conditions Boolean Logic]]). The template stays under one source of truth in version control.
## A useful summary
> The template describes **what should exist**. The stack tracks **what does exist**. CloudFormation reconciles the two.
That reconciliation — figuring out the diff between desired and actual — is what stack updates, change sets, and drift detection are all built on.
## Related
- [[CFN Stack Concept]]
- [[CFN Template Structure Nine Sections]]
- [[CFN Template Format JSON vs YAML]]
- [[CFN Ref vs Fn-GetAtt]]
- [[CFN Conditions Boolean Logic]]
- [[CFN Change Sets Preview-Execute]]