INTRODUCTION
I’m excited to discuss a fascinating project I had the chance to work on this year in this piece. I began a journey that not just expanded my comprehension but also gave me the ability to use cutting-edge cloud computing and DevOps techniques after being inspired by the in-depth curricula of Udacity Cloud DevOps course. This project specifically explored the world of Infrastructure as Code using AWS CloudFormation, giving me great practical experience in constructing scalable cloud infrastructure.
You can simply locate the project’s source code on my GitHub repository if you’re interested in looking through it.
Project Scenario
Your company is creating an Instagram clone called Udagram, and the requirement is to deploy this new application to the AWS infrastructure using Infrastructure as Code.
You have been tasked with provisioning the required infrastructure and deploying a dummy application, along with the necessary supporting software.
Since the underlying network infrastructure will be maintained by a separate team, you must create independent stacks for the network infrastructure and the application itself.
Infrastructure spin up and tear down needs to be automated so that each team can create and discard testing environments on demand.
Server specifications
Application servers have a launch configuration that allows you to deploy four servers—two in each of your private subnets. An auto-scaling group used the launch configuration. There were two virtual CPUs and 4GB of RAM. Ubuntu 18 serves as the operating system. The best instance size and machine image (AMI) for this specification was selected.
My Architecture Diagram
Here is the information in the parameter files and configuration files for the servers (EC2 instances), S3 buckets, and network infrastructure.
network-parameters.json
[
{
"ParameterKey": "EnvironmentName",
"ParameterValue": "udagram-cf-network"
}
]
Configuration of a network
A network infrastructure with public and private subnets, routing, and internet access is created via the network.yaml file below. It provides subnet CIDR blocks, VPC CIDR blocks, and environment customization settings. A VPC, an internet gateway, public and private subnets, NAT gateways, and route tables are all created by the code. Important data like VPC IDs, route table IDs, and subnet IDs are defined as outputs. By using this CloudFormation template, a network configuration that can route internet traffic to both public and private subnets can be built.
Description: >
Charles Asirifi
This template deploys a VPC with public and private subnets in two Availabilty Zones (AZs). The subnets are in pairs.
Also deployed in an Internet Gateway, with a default route on the public subnets and a pair of NAT Gateways, one in each Availability Zone,
and default routes for them in the private subnets.
Parameters:
EnvironmentName:
Description: Environment name prefixed to resource names
Type: String
VpcCIDR:
Description: IP range in CIDR notation for this VPC
Type: String
Default: 10.0.0.0/16
PublicSubnet1CIDR:
Description: IP range in CIDR notation for the public subnet in the first AZ
Type: String
Default: 10.0.0.0/24
PublicSubnet2CIDR:
Description: IP range in CIDR notation for the public subnet in the second AS
Type: String
Default: 10.0.1.0/24
PrivateSubnet1CIDR:
Description: IP range in CIDR notation for the private subnet in the first AZ
Type: String
Default: 10.0.2.0/24
PrivateSubnet2CIDR:
Description: IP range in CIDR notation for the private subnet in the second AZ
Type: String
Default: 10.0.3.0/24
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref EnvironmentName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Ref EnvironmentName
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref PublicSubnet1CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Public Subnet (AZ1)
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: !Ref PublicSubnet2CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Public Subnet (AZ2)
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref PrivateSubnet1CIDR
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Subnet (AZ1)
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: !Ref PrivateSubnet2CIDR
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Subnet (AZ2)
NatGateway1EIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway2EIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGateway1EIP.AllocationId
SubnetId: !Ref PublicSubnet1
NatGateway2:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGateway2EIP.AllocationId
SubnetId: !Ref PublicSubnet2
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Public Routes
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Routes (AZ1)
DefaultPrivateRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1
SubnetId: !Ref PrivateSubnet1
PrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Routes (AZ2)
DefaultPrivateRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway2
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable2
SubnetId: !Ref PrivateSubnet2
Outputs:
VPC:
Description: A reference to the created VPC
Value: !Ref VPC
Export:
Name: !Sub ${EnvironmentName}-VPCID
VPCPublicRouteTable:
Description: Public Routing
Value: !Ref PublicRouteTable
Export:
Name: !Sub ${EnvironmentName}-PUB-RT
VPCPrivateRouteTable1:
Description: Private Routing AZ1
Value: !Ref PrivateRouteTable1
Export:
Name: !Sub ${EnvironmentName}-PRI1-RT
VPCPrivateRouteTable2:
Description: Private Routing AZ2
Value: !Ref PrivateRouteTable2
Export:
Name: !Sub ${EnvironmentName}-PRI2-RT
PublicSubnets:
Description: A list of the public subnets
Value: !Join [ ",", [ !Ref PublicSubnet1, !Ref PublicSubnet2 ]]
Export:
Name: !Sub ${EnvironmentName}-PUB-NETS
PrivateSubnets:
Description: A list of the private subnets
Value: !Join [ ",", [ !Ref PrivateSubnet1, !Ref PrivateSubnet2 ]]
Export:
Name: !Sub ${EnvironmentName}-PRIV-NETS
PublicSubnet1:
Description: A reference to the public subnet in the 1st Availability Zone
Value: !Ref PublicSubnet1
Export:
Name: !Sub ${EnvironmentName}-PUB1-SN
PublicSubnet2:
Description: A reference to the public subnet in the 2nd Availability Zone
Value: !Ref PublicSubnet2
Export:
Name: !Sub ${EnvironmentName}-PUB2-SN
PrivateSubnet1:
Description: A reference to the private subnet in the 1st Availability Zone
Value: !Ref PrivateSubnet1
Export:
Name: !Sub ${EnvironmentName}-PRI1-SN
PrivateSubnet2:
Description: A reference to the private subnet in the 2nd Availability Zone
Value: !Ref PrivateSubnet2
Export:
Name: !Sub ${EnvironmentName}-PRI2-SN
udagram-parameters.json
[
{
"ParameterKey": "EnvironmentName",
"ParameterValue": "cloudformation-server"
},
{
"ParameterKey": "NetworkEnvironmentName",
"ParameterValue": "cloudformation-network"
}
]
udagram.yml
This template deploys a High-Availability Web Application in the cloud
using Load Balancers, AutoScaling Groups and related infrastructure.
Description:
Charles Asirifi
This template deploys a High-Availability Web Application in the cloud
using Load Balancers, AutoScaling Groups and related infrastructure.
Parameters:
EnvironmentName:
Description: Environment name prefixed to server resource names
Type: String
NetworkEnvironmentName:
Description: Environment name prefixed to network resource names
Type: String
AMI:
Description: 'The AWS Machine Image used.'
Type: String
Default: ami-053b0d53c279acc90
InstanceType:
Description: EC2 instance type
Type: String
Default: t3.medium
Resources:
UdacityS3ReadOnlyEC2:
Type: AWS::IAM::Role
Properties:
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "ec2.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
ProfileWithRolesForOurApp:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref UdacityS3ReadOnlyEC2
LBSecGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow http to our load balancer
VpcId:
Fn::ImportValue:
!Sub "${NetworkEnvironmentName}-VPCID"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
WebServerSecGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow http to our hosts and SSH from local only
VpcId:
Fn::ImportValue:
!Sub "${NetworkEnvironmentName}-VPCID"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
WebAppLaunchConfig:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
UserData:
Fn::Base64: !Sub |
#!/bin/bash
apt-get update -y
apt-get install apache2 -y
systemctl start apache2.service
cd /var/www/html
echo "it works! Udagram, Udacity" > index.html
ImageId: !Ref AMI
IamInstanceProfile: !Ref ProfileWithRolesForOurApp
SecurityGroups:
- Ref: WebServerSecGroup
InstanceType: !Ref InstanceType
BlockDeviceMappings:
- DeviceName: "/dev/sdk"
Ebs:
VolumeSize: '10'
WebAppGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
- Fn::ImportValue:
!Sub "${NetworkEnvironmentName}-PRIV-NETS"
LaunchConfigurationName:
Ref: WebAppLaunchConfig
MinSize: '4'
MaxSize: '6'
TargetGroupARNs:
- Ref: WebAppTargetGroup
WebAppLB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Subnets:
- Fn::ImportValue: !Sub "${NetworkEnvironmentName}-PUB1-SN"
- Fn::ImportValue: !Sub "${NetworkEnvironmentName}-PUB2-SN"
SecurityGroups:
- Ref: LBSecGroup
Listener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn:
Ref: WebAppTargetGroup
LoadBalancerArn:
Ref: WebAppLB
Port: '80'
Protocol: HTTP
ALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref 'WebAppTargetGroup'
Conditions:
- Field: path-pattern
Values: [/]
ListenerArn: !Ref 'Listener'
Priority: 1
WebAppTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 10
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 8
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
VpcId:
Fn::ImportValue:
Fn::Sub: "${NetworkEnvironmentName}-VPCID"
Outputs:
LoadBalancerUrl:
Description: The URL to the Load Balancer
Value: !Join [ '', [ http://, !GetAtt WebAppLB.DNSName ] ]
Export:
Name: !Sub ${EnvironmentName}-LBID
Script Usage
This Repository contains some scripts that will be used to create, update and delete the CloudFormation stack.
Create.sh
./create.sh (stackName) (script.yml) (parameters.json) (profile)
Below is the content of the create.sh script
aws cloudformation create-stack \
--stack-name $1 \
--template-body file://$2 \
--parameters file://$3 \
--region=us-east-1 \
--capabilities CAPABILITY_NAMED_IAM
Example:
./create.sh Udagram infrastructure/network.yaml parameters/network.json charles_user
Update:
./update.sh (stackName) (script.yml) (parameters.json) (profile)
Example:
./update.sh Udagram infrastructure/network.yaml parameters/network.json charles_user
Below is the content of the update.sh script
aws cloudformation update-stack \
--stack-name $1 \
--template-body file://$2 \
--parameters file://$3 \
--region=us-east-1 \
--capabilities CAPABILITY_NAMED_IAM
Delete.sh
aws cloudformation delete-stack \
--stack-name $1
aws cloudformation delete-stack \
--stack-name $1
These are the screenshots for the project