Cloud formation
AWS CloudFormation offers a unified language for describing and provisioning all the infrastructure resources within your environment in a secure and reproducible manner.
AWS CloudFormation enables you to define the resources required for an application or solution in a text file, formatted in JSON or YAML. This template, which can be manually uploaded or retrieved from an S3 bucket, articulates the set of resources as a cohesive unit referred to as a stack. CloudFormation subsequently interprets the template’s parameters into API calls and establishes the stack, provisioning its resources to form a functional environment. Stacks can be created, updated, and deleted through the AWS CloudFormation console, AWS API, or AWS command-line interface (CLI).
Benefits of Cloudformation
- Simplify infrastructure management: A CloudFormation template provides a comprehensive description of all your resources and their attributes. By deploying this template as a CloudFormation stack, the service takes care of provisioning the Auto Scaling group, load balancer, database, etc on your behalf. Once the stack is successfully generated, your AWS resources are operational. Deleting the stack is just as straightforward, resulting in the removal of all resources associated with it.
- Quickly replicate your infrastructure: If your application requires additional availability, you might replicate it in multiple regions so that if one region becomes unavailable, your users can still use your application in other regions.
- Easily control and track changes to your infrastructure: Because templates are text files, you simply track differences in your templates to track changes to your infrastructure, similar to the way developers control revisions to source code.
How does cloud formation Work?
When creating a stack, AWS CloudFormation makes underlying service calls to AWS to provision and configure your resources. CloudFormation can only perform actions that you have permission to do.
The calls that CloudFormation makes are all declared by your template. For example, suppose you have a template that describes an EC2 instance with a t2.micro
instance type. When you use that template to create a stack, CloudFormation calls the Amazon EC2 create instance API and specifies the instance type as t2.micro
.
Example YAML
AWSTemplateFormatVersion: 2010-09-09
Description: A simple EC2 instance
Resources:
MyEC2Instance:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: ami-0ff8a91507f77f867
InstanceType: t2.micro
CloudFormation provisions and configures resources by making calls to the AWS services that are described in your template. After all the resources have been created, CloudFormation reports that your stack has been created. You can then start using the resources in your stack. If stack creation fails, CloudFormation rolls back your changes by deleting the resources that it created.
Template Basics
- Resources:
The Resources object contains a list of resource objects. A resource declaration contains the resource’s attributes, which are themselves declared as child objects. A resource must have a Type
attribute, which defines the kind of AWS resource you want to create.
Resources:
HelloBucket:
Type: 'AWS::S3::Bucket'
- Resource properties
Usually, a property for a resource is simply a string value. Some resources can have multiple properties, and some properties can have one or more subproperties. For example, the AWS::S3::Bucket resource has two properties: AccessControl and WebsiteConfiguration. The WebsiteConfiguration property has two subproperties: IndexDocument and ErrorDocument
Resources:
HelloBucket:
Type: 'AWS::S3::Bucket'
Properties:
AccessControl: PublicRead
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
- Input parameters
You declare parameters in a template’s Parameters object. A parameter contains a list of attributes that define its value and constraints against its value. The only required attribute is Type, which can be String, Number, or an AWS-specific type. You can also add a Description attribute that tells a user more about what kind of value they should specify. The Fn::Ref function can be leveraged to reference parameters
Parameters:
KeyName:
Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
Type: String
Mappings:
RegionMap:
us-east-1:
AMI: ami-76f0061f
us-west-1:
AMI: ami-655a0a20
Resources:
Ec2Instance:
Type: 'AWS::EC2::Instance'
Properties:
KeyName: !Ref KeyName
ImageId: !FindInMap
- RegionMap
- !Ref 'AWS::Region'
- AMI
UserData: !Base64 '80'
- conditional values
We can specify value based on a conditional input. Conditions are used to control the creation of resources or outputs
based on a condition.
The intrinsic function (logical) can be any of the following:
Fn::Equals, Fn::If, Fn::Not, Fn::Or
- Constructed values and output values
The Outputs section declares optional outputs values that we can import into other stacks(if you export them first). They’re very useful for example if you define a network CloudFormation, and output the variables such as VPC ID and your Subnet IDs
Fn::ImportValue for cross stack references.
- Intrensic Functions :
Ref : The Fn::Ref function can be leveraged to reference
Fn::GetAtt : To get the attributes of an resource
Fn::FindInMap
Fn::ImportValue
Fn::Join
Fn::Sub
Condition Functions
Resource Documentation
- We can’t go through all of the 224 resources, but we can learn how to use them.
- All the resources can be found here:
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aw
s-template-resource-type-ref.html - Then, we just read the docs
Example
We are going to create the cloud formation stack for our EKS cluster using the cloudformation template
Step1: Create VPC
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS Sample VPC - Private and Public subnets'
Parameters:
VpcBlock:
Type: String
Default: 192.168.0.0/16
Description: The CIDR range for the VPC. This should be a valid private (RFC 1918) CIDR range.
PublicSubnet01Block:
Type: String
Default: 192.168.0.0/18
Description: CidrBlock for public subnet 01 within the VPC
PublicSubnet02Block:
Type: String
Default: 192.168.64.0/18
Description: CidrBlock for public subnet 02 within the VPC
PrivateSubnet01Block:
Type: String
Default: 192.168.128.0/18
Description: CidrBlock for private subnet 01 within the VPC
PrivateSubnet02Block:
Type: String
Default: 192.168.192.0/18
Description: CidrBlock for private subnet 02 within the VPC
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: "Worker Network Configuration"
Parameters:
- VpcBlock
- PublicSubnet01Block
- PublicSubnet02Block
- PrivateSubnet01Block
- PrivateSubnet02Block
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcBlock
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC'
InternetGateway:
Type: "AWS::EC2::InternetGateway"
VPCGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Public Subnets
- Key: Network
Value: Public
PrivateRouteTable01:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Private Subnet AZ1
- Key: Network
Value: Private01
PrivateRouteTable02:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Private Subnet AZ2
- Key: Network
Value: Private02
PublicRoute:
DependsOn: VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PrivateRoute01:
DependsOn:
- VPCGatewayAttachment
- NatGateway01
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable01
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway01
PrivateRoute02:
DependsOn:
- VPCGatewayAttachment
- NatGateway02
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable02
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway02
NatGateway01:
DependsOn:
- NatGatewayEIP1
- PublicSubnet01
- VPCGatewayAttachment
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt 'NatGatewayEIP1.AllocationId'
SubnetId: !Ref PublicSubnet01
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-NatGatewayAZ1'
NatGateway02:
DependsOn:
- NatGatewayEIP2
- PublicSubnet02
- VPCGatewayAttachment
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt 'NatGatewayEIP2.AllocationId'
SubnetId: !Ref PublicSubnet02
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-NatGatewayAZ2'
NatGatewayEIP1:
DependsOn:
- VPCGatewayAttachment
Type: 'AWS::EC2::EIP'
Properties:
Domain: vpc
NatGatewayEIP2:
DependsOn:
- VPCGatewayAttachment
Type: 'AWS::EC2::EIP'
Properties:
Domain: vpc
PublicSubnet01:
Type: AWS::EC2::Subnet
Metadata:
Comment: Subnet 01
Properties:
MapPublicIpOnLaunch: true
AvailabilityZone:
Fn::Select:
- '0'
- Fn::GetAZs:
Ref: AWS::Region
CidrBlock:
Ref: PublicSubnet01Block
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet01"
- Key: kubernetes.io/role/elb
Value: 1
PublicSubnet02:
Type: AWS::EC2::Subnet
Metadata:
Comment: Subnet 02
Properties:
MapPublicIpOnLaunch: true
AvailabilityZone:
Fn::Select:
- '1'
- Fn::GetAZs:
Ref: AWS::Region
CidrBlock:
Ref: PublicSubnet02Block
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet02"
- Key: kubernetes.io/role/elb
Value: 1
PrivateSubnet01:
Type: AWS::EC2::Subnet
Metadata:
Comment: Subnet 03
Properties:
AvailabilityZone:
Fn::Select:
- '0'
- Fn::GetAZs:
Ref: AWS::Region
CidrBlock:
Ref: PrivateSubnet01Block
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet01"
- Key: kubernetes.io/role/internal-elb
Value: 1
PrivateSubnet02:
Type: AWS::EC2::Subnet
Metadata:
Comment: Private Subnet 02
Properties:
AvailabilityZone:
Fn::Select:
- '1'
- Fn::GetAZs:
Ref: AWS::Region
CidrBlock:
Ref: PrivateSubnet02Block
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet02"
- Key: kubernetes.io/role/internal-elb
Value: 1
PublicSubnet01RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet01
RouteTableId: !Ref PublicRouteTable
PublicSubnet02RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet02
RouteTableId: !Ref PublicRouteTable
PrivateSubnet01RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet01
RouteTableId: !Ref PrivateRouteTable01
PrivateSubnet02RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet02
RouteTableId: !Ref PrivateRouteTable02
ControlPlaneSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Cluster communication with worker nodes
VpcId: !Ref VPC
Outputs:
PublicSubnetIds:
Description: Public Subnets IDs in the VPC
Value: !Join [ ",", [ !Ref PublicSubnet01, !Ref PublicSubnet02 ] ]
Export:
Name: PublicSubnetIds
PrivateSubnetIds:
Description: Private Subnets IDs in the VPC
Value: !Join [ ",", [ !Ref PrivateSubnet01, !Ref PrivateSubnet02 ] ]
Export:
Name: PrivateSubnetIds
ControlPlaneSecurityGroupId:
Description: Security group for the cluster control plane communication with worker nodes
Value: !Ref ControlPlaneSecurityGroup
Export:
Name: ControlPlaneSecurityGroupId
VpcId:
Description: The VPC Id
Value: !Ref VPC
Export:
Name: VpcId
aws cloudformation create-stack \
–region us-east-2 \
–stack-name tradesman-vpc \
–template-body file://vpc.yaml
Step3: Create EKS
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS Cluster'
Parameters:
ClusterName:
Type: String
Default: tradesman
NumberOfWorkerNodes:
Type: Number
Default: 1
WorkerNodesInstanceType:
Type: String
Default: t2.micro
KubernetesVersion:
Type: String
Default: 1.22
Resources:
###########################################
## Roles
###########################################
EksRole:
Type: AWS::IAM::Role
Properties:
RoleName: tradesman.cluster.role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- eks.amazonaws.com
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
EksNodeRole:
Type: AWS::IAM::Role
Properties:
RoleName: tradesman.node.role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
- "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
- "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
- "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
- "arn:aws:iam::aws:policy/AmazonRDSFullAccess"
###########################################
## Eks Cluster
###########################################
EksCluster:
Type: AWS::EKS::Cluster
Properties:
Name: !Ref ClusterName
Version: !Ref KubernetesVersion
RoleArn: !GetAtt EksRole.Arn
ResourcesVpcConfig:
SecurityGroupIds:
- !ImportValue ControlPlaneSecurityGroupId
SubnetIds: !Split [ ',', !ImportValue PrivateSubnetIds ]
EksNodegroup:
Type: AWS::EKS::Nodegroup
DependsOn: EksCluster
Properties:
ClusterName: !Ref ClusterName
NodeRole: !GetAtt EksNodeRole.Arn
ScalingConfig:
MinSize:
Ref: NumberOfWorkerNodes
DesiredSize:
Ref: NumberOfWorkerNodes
MaxSize:
Ref: NumberOfWorkerNodes
Subnets: !Split [ ',', !ImportValue PrivateSubnetIds ]
aws cloudformation create-stack \
--region us-east-2 \
--stack-name tradesman-cluster \
--capabilities CAPABILITY_NAMED_IAM \
--template-body file://eks.yaml