Imagine you want to create a dozen resources in the cloud — storage, a database, a network — without clicking anything in the console by hand. You describe what you want in a text file, hand it to AWS, and the cloud provisions everything itself. If tomorrow you need the same setup in a second region, you run the same file. This is infrastructure as code, and AWS CloudFormation is AWS's native tool for the job.
CloudFormation is free on its own (you pay only for the resources it creates) and is built into AWS — there is nothing extra to install. If you are just getting to know the idea of describing infrastructure with text, start with the overview article Infrastructure as Code, and for basic cloud concepts see the AWS fundamentals.
What CloudFormation Is
CloudFormation is a service that reads your template (a text file describing resources) and brings the real infrastructure into line with it. Create what is missing, change what differs, delete what is extra — all according to what is written in the file.
The core idea is being declarative. You do not write a step-by-step instruction "first create this, then that." You describe the result — which resources should exist — and CloudFormation figures out the order itself: it sees that the database depends on the network and creates the network first. It is like a shopping list versus a recipe: you say "what should be there," not "what to do step by step."
An analogy: the template is a blueprint of a house, and CloudFormation is the construction crew that builds everything from the blueprint. One blueprint — as many identical houses as you like.
What a Template Is Made Of
A template is written in YAML or JSON (YAML reads more pleasantly, so beginners are advised to use it). The file is divided into named sections:
AWSTemplateFormatVersion— the format version. The value is almost always the same:2010-09-09. It is optional, but it is conventional to include it.Parameters— input values that are substituted at launch. For example, an environment name or a server type. Thanks to them, one template works for both testing and production.Mappings— lookup tables of "key → value." A classic example is different image identifiers in different regions.Resources— the only mandatory section. This is where the resources themselves are listed: storage, servers, databases, networks.Outputs— what to return to the outside after deployment: a storage address, a network identifier. These values are visible in the console and can be passed to other stacks.
A minimal working template with a single S3 bucket:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
MyBucket:
Type: AWS::S3::Bucket
Every resource is described the same way: you give it a logical name (MyBucket — how you refer to it inside the template), specify its Type (what it is — AWS::S3::Bucket means an S3 bucket), and set Properties (settings for this specific resource). A type name always looks like AWS::Service::Type.
The same resource, but with settings:
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: Enabled
And here is a richer template — with a parameter on input and a value on output:
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
BucketName:
Type: String
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
Outputs:
BucketArn:
Value: !GetAtt MyBucket.Arn
What a Stack Is
A stack is a group of resources deployed from a single template and managed as one whole. Run a template — get a stack. All resources inside are created, updated, and deleted together.
This is the key convenience: delete a stack and CloudFormation removes everything that belonged to it, in the correct order. No forgotten storage that later shows up on the bill. A stack is a "basket" from which resources do not get lost.
Create a stack from a local template via the command line (AWS CLI works the same on any operating system):
aws cloudformation create-stack \
--stack-name my-first-stack \
--template-body file://template.yaml
Update it — the same stack, a changed template:
aws cloudformation update-stack \
--stack-name my-first-stack \
--template-body file://template.yaml
Delete the entire stack along with all its resources:
aws cloudformation delete-stack --stack-name my-first-stack
If something breaks during the process, CloudFormation by default rolls back the stack to its previous working state — no partially broken infrastructure is left behind.
Change Sets: Preview Before Applying
Updating live infrastructure blindly is scary: one careless change can recreate a database. To avoid this, there is a change set — a preview of what will happen before it is actually applied.
CloudFormation compares the current state of the stack with the new template and shows a list: what will be added, what will change, and — pay attention — what will be recreated (replacement). Recreating a resource often means data loss, so this preview saves you from expensive mistakes.
The work goes in three steps. First, create the change set:
aws cloudformation create-change-set \
--stack-name my-first-stack \
--change-set-name my-changes \
--template-body file://template.yaml
Then look at what is inside:
aws cloudformation describe-change-set \
--stack-name my-first-stack \
--change-set-name my-changes
And, if everything looks good, apply it:
aws cloudformation execute-change-set \
--stack-name my-first-stack \
--change-set-name my-changes
The habit of "change set first, execute later" is one of the main signs of careful work with CloudFormation in production environments.
Handy Template Features
So that a template is not just a set of hard-coded strings, CloudFormation has intrinsic functions — they substitute values on the fly. In YAML each one has a short form using !.
!Ref— substitute a parameter value or the resource itself (for example, the name of a created bucket).!GetAtt— take a resource attribute thatRefdoes not provide: for example,!GetAtt MyBucket.Arn— the bucket's full identifier.!Sub— build a string with substitutions:!Sub "${AWS::StackName}-bucket"inserts the stack name.!Join— join a list of strings with a separator:!Join ["-", [prod, bucket]]yieldsprod-bucket.
When infrastructure grows, a single file becomes unwieldy. Four mechanisms help here:
- Nested stacks — a template references another template as a resource of type
AWS::CloudFormation::Stack, specifying its address inTemplateURL. This way a large template is split into reusable pieces (the network separately, the database separately).
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-templates/network.yaml
- Cross-stack references — one stack publishes a value via
Exportin theOutputssection, and another picks it up with theFn::ImportValuefunction. Handy when the network lives in one stack and the servers in another.
Outputs:
VpcId:
Value: !Ref MyVPC
Export:
Name: !Sub "${AWS::StackName}-VPCID"
- StackSets — deploy a single template across many accounts and regions at once with one command. Indispensable in large organizations.
- Drift detection — a built-in check: someone edited a resource by hand in the console, and reality diverged from the template. CloudFormation shows these differences so you can restore order.
Where This Is Used
CloudFormation shows up almost everywhere AWS infrastructure is managed by code rather than by clicks: deploying networks (networking), servers (compute), serverless functions (serverless), databases (managed data). The template usually lives in the repository next to the application, and deployment happens automatically from a build pipeline — a change set is created and applied as a deployment step, which fits well with release strategies.
Typical beginner mistakes:
- Editing resources by hand in the console. It is worth building the habit of changing only the template. Manual changes create drift, and the next stack update may wipe them out.
- Updating a live stack without a change set. It is easy to accidentally recreate a database. First
create-change-setanddescribe-change-set, thenexecute-change-set. - One giant template. When a file grows past a couple of hundred lines, it is time to split it into nested stacks or link them via cross-stack.
- Deleting what you cannot afford to lose. For critical resources (a database, storage with data) you set a retention policy so that deleting the stack does not remove them.
What to learn next. CloudFormation is AWS's native approach; alongside it, it is worth looking at the cloud-agnostic Terraform and at AWS CDK, where you describe infrastructure in a regular programming language (TypeScript, Python, and others), and CDK then generates a CloudFormation template itself. To understand how any of these tools keeps track of what has been deployed, the article on state and delivery is useful. And if your resources are object stores, take a look at the section on object storage.