Skip to content

CloudFormation



Infrastructure as Code is the practice of managing and provisioning infrastructure through code instead of manual processes.

CloudFormation is AWS’s native Infrastructure as Code service. You write a template (JSON or YAML) describing what resources you want, and CloudFormation creates them in the correct order.


┌──────────────────────────────────────────────────────────────────────────────┐
│ CLOUDFORMATION ANATOMY │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ TEMPLATE │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ Parameters │ │ │
│ │ │ (Input values like environment name, instance type) │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ Resources │ │ │
│ │ │ (AWS components to create: VPC, EC2, S3, IAM, etc.) │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ Outputs │ │ │
│ │ │ (Values returned after creation: IPs, URLs, IDs) │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ STACK │ │
│ │ A running instance of a template - the actual created resources │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
ConceptWhat It IsReal Example
TemplateA YAML/JSON file that describes what to buildtemplate.yml
StackAll resources created from one templatecmtr-29vd4qvr-basic-infra
ResourceA single AWS componentAWS::EC2::VPC
ParameterA variable you pass when creating stackMaintainer: cmtr-29vd4qvr-maintainer
OutputA value returned after stack is createdInstance1PublicIP
Stack EventStatus update during creationCREATE_COMPLETE
Change SetPreview of changes before updating(Not used in this task)

---
AWSTemplateFormatVersion: "2010-10-09" # ═══ ALWAYS include this ═══
Description: "What this stack creates" # Human-readable description
Parameters: # ═══ INPUT VALUES ═══
MyParameter: # Values user provides
Type: String # when creating stack
Default: some-value
Resources: # ═══ WHAT TO CREATE ═══
MyResource: # Logical name (your choice)
Type: AWS::Service::ResourceType # AWS resource type
Properties: # Configuration
PropertyName: Value
Outputs: # ═══ RETURN VALUES ═══
MyOutput: # What to output
Value: !Ref MyResource # after creation
template.yml
├── Parameters (1)
│ └── Maintainer: cmtr-29vd4qvr-maintainer
├── Resources (17 total)
│ ├── Networking (9)
│ │ ├── VPC
│ │ ├── Subnet1, Subnet2
│ │ ├── Internet Gateway
│ │ ├── VPC-IGW Attachment
│ │ ├── RouteTable1, RouteTable2
│ │ └── RouteTableAssociation1, RouteTableAssociation2
│ │
│ ├── Security (1)
│ │ └── Security Group (SSH + HTTP)
│ │
│ ├── IAM (2)
│ │ ├── IAM Role (for SSM)
│ │ └── Instance Profile
│ │
│ └── Compute (2)
│ ├── EC2 Instance 1 (with Apache/httpd)
│ └── EC2 Instance 2 (with Apache/httpd)
└── Outputs (3)
├── Instance1PublicIP
├── Instance2PublicIP
└── VPCId

1.5 CloudFormation Functions (How Resources Talk to Each Other)

Section titled “1.5 CloudFormation Functions (How Resources Talk to Each Other)”
# Example: EC2 needs to know which Subnet to use
cmtr29vd4qvrInstance1:
Type: AWS::EC2::Instance
Properties:
SubnetId: !Ref cmtr29vd4qvrSubnet1 # ← Gets Subnet ID

What happens: CloudFormation replaces !Ref cmtr29vd4qvrSubnet1 with the actual Subnet ID (e.g., subnet-abc123).

# Example: Output the Public IP of an instance
Outputs:
Instance1PublicIP:
Value: !GetAtt cmtr29vd4qvrInstance1.PublicIp # ← Gets PublicIp attribute
UserData:
Fn::Base64: !Sub | # ← Encodes script as Base64
#!/bin/bash
yum install -y httpd
echo "Server running" > /var/www/html/index.html

Why Base64? EC2 UserData must be Base64-encoded.

cmtr29vd4qvrPublicRoute1:
Type: AWS::EC2::Route
DependsOn: cmtr29vd4qvrVPCGatewayAttachment # ← Wait for IGW to attach first

Why? You can’t create a route to an IGW before the IGW is attached to the VPC.


INTERNET
┌─────────────────────────┐
│ Internet Gateway (IGW) │
│ cmtr-29vd4qvr-igw │
└───────────┬─────────────┘
┌─────────────┴─────────────┐
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Route Table 1 │ │ Route Table 2 │
│ 0.0.0.0/0→IGW │ │ 0.0.0.0/0→IGW │
└───────┬────────┘ └───────┬────────┘
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Subnet 1 │ │ Subnet 2 │
│ 10.0.1.0/24 │ │ 10.0.2.0/24 │
│ eu-west-1a │ │ eu-west-1b │
└───────┬────────┘ └───────┬────────┘
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ EC2 Instance1 │ │ EC2 Instance2 │
│ Apache/httpd │ │ Apache/httpd │
└────────────────┘ └────────────────┘
1. User types: <http://54.123.45.67>
2. ▼
Internet Gateway receives request
3. ▼
Route Table says: "Send 0.0.0.0/0 traffic to IGW"
(For incoming, it routes to the subnet)
4. ▼
Subnet receives traffic
5. ▼
Security Group checks: "Is port 80 allowed? Yes!"
6. ▼
EC2 Instance serves the web page
"Hello from Region eu-west-1a"

┌──────────────────────────────────────────────────────────────────────────────┐
│ EC2 BOOT SEQUENCE │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ Time 0: Instance starts │
│ │ │
│ ▼ │
│ Time 0: AWS checks UserData │
│ │ │
│ ▼ │
│ Time 1: UserData script executes (ONCE only) │
│ │ ┌─────────────────────────────────────────────┐ │
│ │ │ #!/bin/bash │ │
│ │ │ yum update -y # Update OS │ │
│ │ │ yum install -y httpd # Install Apache │ │
│ │ │ systemctl start httpd # Start Apache │ │
│ │ │ systemctl enable httpd # Auto-start │ │
│ │ │ echo "<h1>Hello</h1>" > /var/www/html/index.html │
│ │ └─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Time 2: Apache (httpd) is running on port 80 │
│ │ │
│ ▼ │
│ Time 3: Instance is ready to serve web traffic │
│ │
│ ⚠️ Important: UserData runs ONLY on first boot! │
└──────────────────────────────────────────────────────────────────────────────┘

┌─────────────────┐
│ create-stack │ ← You run this command
└────────┬────────┘
┌─────────────────┐
│ CREATE_IN_ │
│ PROGRESS │ ← Resources being created
└────────┬────────┘
┌──────────────┴──────────────┐
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ CREATE_COMPLETE│ │ CREATE_FAILED │
│ ✅ Success! │ │ ❌ Error! │
└────────┬───────┘ └────────┬───────┘
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Stack is ready │ │ ROLLBACK_ │
│ to use! │ │ IN_PROGRESS │
└────────────────┘ └────────┬───────┘
┌────────────────┐
│ ROLLBACK_ │
│ COMPLETE │
│ (Everything │
│ deleted) │
└────────────────┘

Error MessageWhat It MeansHow to Fix
AlreadyExistsExceptionStack name already in useDelete old stack or use different name
Properties validation failedInvalid property in templateCheck AWS docs for correct property names
extraneous key [Tags] is not permittedThat resource doesn’t support TagsRemove Tags from that resource
ROLLBACK_COMPLETESomething failed during creationCheck stack events for specific error
CAPABILITY_NAMED_IAM requiredTemplate creates IAM resourcesAdd --capabilities CAPABILITY_NAMED_IAM


Terminal window
aws sts get-caller-identity

Expected output:

{
"UserId": "AROA...",
"Account": "537124972913",
"Arn": "arn:aws:sts::537124972913:assumed-role/..."
}
Terminal window
aws sts get-caller-identity --query "Account" --output text

You’ll need this for IAM role ARN.


┌──────────────────────────────────────────────────────────────────────────────┐
│ EXECUTION CHECKLIST │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ☐ Step 1: Create IAM Role for CloudFormation │
│ ☐ Step 2: Save template.yml file │
│ ☐ Step 3: Validate template │
│ ☐ Step 4: Create the stack │
│ ☐ Step 5: Monitor progress │
│ ☐ Step 6: Verify resources │
│ ☐ Step 7: Test HTTP endpoints │
│ ☐ Step 8: Submit task │
│ ☐ Step 9: Cleanup (delete stack when done) │
└──────────────────────────────────────────────────────────────────────────────┘

2.3 Step 1: Create IAM Role for CloudFormation

Section titled “2.3 Step 1: Create IAM Role for CloudFormation”

Creates a role that CloudFormation service can assume to create resources on your behalf.

Terminal window
aws iam create-role --role-name cmtr-29vd4qvr-cfn-role --assume-role-policy-document "{\\"Version\\":\\"2012-10-17\\",\\"Statement\\":[{\\"Effect\\":\\"Allow\\",\\"Principal\\":{\\"Service\\":\\"cloudformation.amazonaws.com\\"},\\"Action\\":\\"sts:AssumeRole\\"}]}"

Command 2: Attach AdministratorAccess Policy

Section titled “Command 2: Attach AdministratorAccess Policy”
Terminal window
aws iam attach-role-policy --role-name cmtr-29vd4qvr-cfn-role --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
Terminal window
aws iam get-role --role-name cmtr-29vd4qvr-cfn-role --query "Role.Arn" --output text

Copy this output! You’ll need it for Step 4.


Terminal window
notepad template.yml

Copy the complete template from Section 3 below, paste into Notepad, save and close.


Checks your template for syntax errors WITHOUT creating any resources.

Terminal window
aws cloudformation validate-template --template-body file://template.yml --region eu-west-1

Expected success output:

{
"Parameters": [
{
"ParameterKey": "Maintainer",
"DefaultValue": "cmtr-29vd4qvr-maintainer"
}
],
"Capabilities": ["CAPABILITY_NAMED_IAM"]
}
ErrorFix
Yaml not well-formedCheck indentation (use spaces, not tabs)
Unresolved resource dependenciesCheck !Ref values match resource names

Terminal window
aws cloudformation create-stack --stack-name cmtr-29vd4qvr-basic-infra --template-body file://template.yml --capabilities CAPABILITY_NAMED_IAM --role-arn "arn:aws:iam::YOUR_ACCOUNT_ID:role/cmtr-29vd4qvr-cfn-role" --region eu-west-1

Replace YOUR_ACCOUNT_ID with your actual account ID.

Terminal window
aws cloudformation create-stack --stack-name cmtr-29vd4qvr-basic-infra --template-body file://template.yml --capabilities CAPABILITY_NAMED_IAM --region eu-west-1

Expected output:

{
"StackId": "arn:aws:cloudformation:eu-west-1:537124972913:stack/cmtr-29vd4qvr-basic-infra/..."
}

Terminal window
aws cloudformation describe-stacks --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1 --query "Stacks[0].StackStatus" --output text
Terminal window
aws cloudformation wait stack-create-complete --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1

If this returns with no error → Stack created successfully!

Get the error details:

Terminal window
aws cloudformation describe-stack-events --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1 --query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[LogicalResourceId,ResourceStatusReason]" --output table

Then delete and retry:

Terminal window
aws cloudformation delete-stack --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1
aws cloudformation wait stack-delete-complete --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1

Terminal window
aws cloudformation describe-stacks --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1 --query "Stacks[0].Outputs" --output table
Terminal window
aws ec2 describe-instances --filters "Name=tag:Name,Values=cmtr-29vd4qvr-instance1,cmtr-29vd4qvr-instance2" --region eu-west-1 --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value|[0],InstanceId,PublicIpAddress,State.Name]" --output table
Terminal window
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=cmtr-29vd4qvr-vpc" --region eu-west-1
Terminal window
aws ec2 describe-subnets --filters "Name=tag:Name,Values=cmtr-29vd4qvr-subnet1,cmtr-29vd4qvr-subnet2" --region eu-west-1 --query "Subnets[*].[AvailabilityZone,Tags[?Key=='Name'].Value|[0]]" --output table
Terminal window
aws iam get-role --role-name cmtr-29vd4qvr-role --query "Role.RoleName" --output text

Terminal window
aws ec2 describe-instances --filters "Name=tag:Name,Values=cmtr-29vd4qvr-instance1" --region eu-west-1 --query "Reservations[0].Instances[0].PublicIpAddress" --output text
Terminal window
aws ec2 describe-instances --filters "Name=tag:Name,Values=cmtr-29vd4qvr-instance2" --region eu-west-1 --query "Reservations[0].Instances[0].PublicIpAddress" --output text
Terminal window
curl http://INSTANCE1_PUBLIC_IP

Expected: <h1>Hello from Region eu-west-1a</h1>

Terminal window
curl http://INSTANCE2_PUBLIC_IP

Expected: <h1>Hello from Region eu-west-1b</h1>


Once all verifications pass, submit the task!


Terminal window
aws cloudformation delete-stack --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1

Wait for deletion:

Terminal window
aws cloudformation wait stack-delete-complete --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Basic infrastructure with VPC, EC2 instances, and httpd"
Parameters:
Maintainer:
Type: String
Default: cmtr-29vd4qvr-maintainer
Description: Maintainer tag value
Resources:
# ═══════════════════════════════════════════════════════════════
# VPC - The network container for all resources
# ═══════════════════════════════════════════════════════════════
cmtr29vd4qvrVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: cmtr-29vd4qvr-vpc
- Key: Maintainer
Value: !Ref Maintainer
# ═══════════════════════════════════════════════════════════════
# SUBNETS - Two public subnets in different Availability Zones
# ═══════════════════════════════════════════════════════════════
# Subnet 1 - eu-west-1a
cmtr29vd4qvrSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref cmtr29vd4qvrVPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: eu-west-1a
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: cmtr-29vd4qvr-subnet1
- Key: Maintainer
Value: !Ref Maintainer
# Subnet 2 - eu-west-1b
cmtr29vd4qvrSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref cmtr29vd4qvrVPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: eu-west-1b
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: cmtr-29vd4qvr-subnet2
- Key: Maintainer
Value: !Ref Maintainer
# ═══════════════════════════════════════════════════════════════
# INTERNET GATEWAY - Connects VPC to the Internet
# ═══════════════════════════════════════════════════════════════
cmtr29vd4qvrIGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: cmtr-29vd4qvr-igw
- Key: Maintainer
Value: !Ref Maintainer
# Attach Internet Gateway to VPC
cmtr29vd4qvrVPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref cmtr29vd4qvrVPC
InternetGatewayId: !Ref cmtr29vd4qvrIGW
# ═══════════════════════════════════════════════════════════════
# ROUTE TABLES - Direct traffic to the Internet Gateway
# ═══════════════════════════════════════════════════════════════
# Route Table 1
cmtr29vd4qvrPublicRT1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref cmtr29vd4qvrVPC
Tags:
- Key: Name
Value: cmtr-29vd4qvr-public-rt1
- Key: Maintainer
Value: !Ref Maintainer
# Route: Send all traffic (0.0.0.0/0) to Internet Gateway
cmtr29vd4qvrPublicRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref cmtr29vd4qvrPublicRT1
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref cmtr29vd4qvrIGW
DependsOn: cmtr29vd4qvrVPCGatewayAttachment
# Associate Route Table 1 with Subnet 1
cmtr29vd4qvrSubnetRTAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref cmtr29vd4qvrSubnet1
RouteTableId: !Ref cmtr29vd4qvrPublicRT1
# Route Table 2
cmtr29vd4qvrPublicRT2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref cmtr29vd4qvrVPC
Tags:
- Key: Name
Value: cmtr-29vd4qvr-public-rt2
- Key: Maintainer
Value: !Ref Maintainer
# Route: Send all traffic (0.0.0.0/0) to Internet Gateway
cmtr29vd4qvrPublicRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref cmtr29vd4qvrPublicRT2
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref cmtr29vd4qvrIGW
DependsOn: cmtr29vd4qvrVPCGatewayAttachment
# Associate Route Table 2 with Subnet 2
cmtr29vd4qvrSubnetRTAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref cmtr29vd4qvrSubnet2
RouteTableId: !Ref cmtr29vd4qvrPublicRT2
# ═══════════════════════════════════════════════════════════════
# SECURITY GROUP - Firewall rules for EC2 instances
# ═══════════════════════════════════════════════════════════════
cmtr29vd4qvrSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: cmtr-29vd4qvr-sg
GroupDescription: Security group for SSH and HTTP
VpcId: !Ref cmtr29vd4qvrVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: cmtr-29vd4qvr-sg
- Key: Maintainer
Value: !Ref Maintainer
# ═══════════════════════════════════════════════════════════════
# IAM ROLE - Permissions for EC2 instances (SSM access)
# ═══════════════════════════════════════════════════════════════
cmtr29vd4qvrRole:
Type: AWS::IAM::Role
Properties:
RoleName: cmtr-29vd4qvr-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Tags:
- Key: Name
Value: cmtr-29vd4qvr-role
- Key: Maintainer
Value: !Ref Maintainer
# Instance Profile - Container for the IAM Role
cmtr29vd4qvrInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: cmtr-29vd4qvr-role
Roles:
- !Ref cmtr29vd4qvrRole
# ═══════════════════════════════════════════════════════════════
# EC2 INSTANCES - Virtual servers with Apache/httpd
# ═══════════════════════════════════════════════════════════════
# Instance 1 - in eu-west-1a
cmtr29vd4qvrInstance1:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-0442403fb8d244144
SubnetId: !Ref cmtr29vd4qvrSubnet1
SecurityGroupIds:
- !Ref cmtr29vd4qvrSG
IamInstanceProfile: !Ref cmtr29vd4qvrInstanceProfile
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from Region eu-west-1a</h1>" > /var/www/html/index.html
Tags:
- Key: Name
Value: cmtr-29vd4qvr-instance1
- Key: Maintainer
Value: !Ref Maintainer
# Instance 2 - in eu-west-1b
cmtr29vd4qvrInstance2:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-0442403fb8d244144
SubnetId: !Ref cmtr29vd4qvrSubnet2
SecurityGroupIds:
- !Ref cmtr29vd4qvrSG
IamInstanceProfile: !Ref cmtr29vd4qvrInstanceProfile
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from Region eu-west-1b</h1>" > /var/www/html/index.html
Tags:
- Key: Name
Value: cmtr-29vd4qvr-instance2
- Key: Maintainer
Value: !Ref Maintainer
# ═══════════════════════════════════════════════════════════════
# OUTPUTS - Values returned after stack creation
# ═══════════════════════════════════════════════════════════════
Outputs:
Instance1PublicIP:
Description: Public IP of Instance 1
Value: !GetAtt cmtr29vd4qvrInstance1.PublicIp
Instance2PublicIP:
Description: Public IP of Instance 2
Value: !GetAtt cmtr29vd4qvrInstance2.PublicIp
VPCId:
Description: VPC ID
Value: !Ref cmtr29vd4qvrVPC

ProblemCauseSolution
ROLLBACK_COMPLETEResource creation failedCheck stack events for specific error
AlreadyExistsExceptionStack name existsDelete old stack first
Template validation failsYAML syntax errorCheck indentation, use spaces not tabs
HTTP curl returns nothingUserData still runningWait 2-3 minutes, try again
Instance not in SSMSSM agent not readyWait 3-5 minutes
Tags is not permittedResource doesn’t support tagsRemove Tags from that resource

ActionCommand
Validate templateaws cloudformation validate-template --template-body file://template.yml --region eu-west-1
Create stackaws cloudformation create-stack --stack-name cmtr-29vd4qvr-basic-infra --template-body file://template.yml --capabilities CAPABILITY_NAMED_IAM --region eu-west-1
Check statusaws cloudformation describe-stacks --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1 --query "Stacks[0].StackStatus" --output text
Wait for completeaws cloudformation wait stack-create-complete --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1
Get errorsaws cloudformation describe-stack-events --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1 --query "StackEvents[?ResourceStatus=='CREATE_FAILED']" --output table
Delete stackaws cloudformation delete-stack --stack-name cmtr-29vd4qvr-basic-infra --region eu-west-1
Test instancecurl http://PUBLIC_IP

This documentation covers everything needed to understand and complete the CloudFormation task. Save this as a reference!