A template has a few sections: Parameters, Mappings, Conditions, Resources (required), and Outputs.
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
Env: { Type: String, AllowedValues: [dev, prod], Default: dev }
VpcCidr: { Type: String, Default: 10.20.0.0/16 }
Conditions:
IsProd: !Equals [!Ref Env, prod]
Resources:
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
Tags: [{ Key: Name, Value: !Sub "${Env}-vpc" }]
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 4, 8]]
AvailabilityZone: !Select [0, !GetAZs ""]
Outputs:
VpcId:
Value: !Ref Vpc
Export: { Name: !Sub "${Env}-vpc-id" }
pip install cfn-lint && cfn-lint template.yaml
aws cloudformation deploy --template-file template.yaml --stack-name dev-network \
--parameter-overrides Env=dev --region ap-south-1
Ref, Sub, GetAtt, Select and If cover most needs. Exported outputs let other stacks import values with Fn::ImportValue.