This is part 3 and final section on “Building Single Tenant SAAS application in AWS” if you have not read part 1 and part2. Please refer Building Single Single SAAS application in AWS (Fully automated): Part1 and Building Single Tenant SAAS application in AWS (Fully automated): Part2 before proceeding further.
Cloudformation Script for resource creation:
Cloudformation template:
So instead of having to write script with a bunch of AWS API calls, wait loops, and retry logic, you just tell describe what you want and tell CloudFormation to do it for you and our
Client requirement based we write the cloudformation template in yaml format
Let’s go through a launching a CloudFormation stack. We are going to spin up a EC2,Route53, Elastic Load Balancer,RDS,VPC and a Security Groups.
AWSTemplateFormatVersion: 2010-09-09
Description: The CloudFormation template for the EC2,ALB,S3.
Parameters:
company:
Type: String
KeyName:
Description: EC2 Key Pair for SSH Access, you must have created these prior to running this.
Type: String
Default: sshkey-cloudformation-demo
InstanceType:
Description: WebServer EC2 instance type
Type: String
Default: t2.micro
AllowedValues:
- t1.micro
- t2.nano
- t2.micro
- t2.small
- t2.medium
CodedeployRole :
Description : Role description
Type : String
Default : EC2-Codedeploy
Tags:
Description: EC2 tag name of launch instance
Type: String
Default: codedeploy
SSLCertificateId:
Description: ssl certificateid name
Type: String
Default: arn:aws:acm:ap-south-1:465517521296:certificate/9c8ac069-2b37-479c-ab05-b25abdb98988
SSlpolicy:
Description: ssl policy
Type: String
Default: ELBSecurityPolicy-2016-08
RecordSetDomainName:
Description: create a sub domain name
Type: String
Default: aimcrest.com.
VPC:
Type: AWS::EC2::VPC::Id
Default: vpc-0259580badc65cb70
PublicSubnetA:
Type: String
Default: subnet-04f3e3ec5b678c217
PublicSubnetB:
Type: String
Default: subnet-08b26124041951e4c
PublicSubnetC:
Type: String
Default: subnet-00875dca857741667
PrivatesubnetA:
Type: String
Default: subnet-0fa2012ad2698bf34
PrivatesubnetB:
Type: String
Default: subnet-08478c64d665c7068
PrivatesubnetC:
Type: String
Default: subnet-023cd8927c87f3885
Resources:
# Create a Amazon Linux Instance and attach a security group
EC2Instance:
Type: AWS::EC2::Instance
Properties:
KeyName: !Ref KeyName
InstanceType: !Ref InstanceType
ImageId: ami-01d9a41cf34ae4c84
IamInstanceProfile: !Ref ListS3BucketsInstanceProfile
NetworkInterfaces:
- GroupSet:
- !Ref InstanceSg
AssociatePublicIpAddress: 'true'
DeviceIndex: '0'
DeleteOnTermination: 'true'
SubnetId: !Ref PrivatesubnetA
Tags:
- Key: Name
Value: !Ref Tags
ListS3BucketsInstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
Roles:
- !Ref CodedeployRole
# Create an S3 Bucket to store build artifacts
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join ['-', [!Ref company, !Ref 'AWS::AccountId', 's3bucket']]
# Creat a security group for Ec2 instance and open port 80 in bound from internet
InstanceSg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable SSH access via port 22
VpcId: !Ref VPC
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
# Creat a security group for load balancer and open port 80 in bound from internet
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Join ['-', [!Ref company, !Ref 'AWS::AccountId', 'LoadBalancerSecurityGroup']]
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
# Create a LoadBalancer and attach the Security group and Subnets
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: !Join ['-', [!Ref company, !Ref 'AWS::AccountId', 'LoadBalancer']]
Scheme: internet-facing
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
Subnets:
- !Ref PublicSubnetA
- !Ref PublicSubnetB
- !Ref PublicSubnetC
Type: application
# Create a Route53 sub domain alias with ELB
Route53DNS:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: aimcrest.com.
Comment: Zone apex alias targeted to myELB LoadBalancer.
RecordSets:
- Name: !Join ['', [!Ref 'company', ., !Ref 'RecordSetDomainName']]
Type: A
AliasTarget:
HostedZoneId: !GetAtt 'LoadBalancer.CanonicalHostedZoneID'
DNSName: !GetAtt 'LoadBalancer.DNSName'
# Create a TargetGroup for HTTP port 80
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Join ['-', [!Ref company, !Ref 'AWS::AccountId', 'TargetGroup']]
HealthCheckIntervalSeconds: 30
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 15
HealthyThresholdCount: 5
Matcher:
HttpCode: '200'
Port: 80
Protocol: HTTP
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '20'
Targets:
- Id: !Ref EC2Instance
Port: 80
UnhealthyThresholdCount: 3
VpcId: !Ref VPC
# Create a LoadBalancerListener and attach the TargetGroup and LoadBalancer
LoadBalancerHttpsListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 443
Protocol: HTTPS
Certificates:
- CertificateArn: !Ref SSLCertificateId
SslPolicy: 'ELBSecurityPolicy-2016-08'
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
LoadBalancerHttpListener:
Type : AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- RedirectConfig:
Host: "#{host}"
Path: "/#{path}"
Port: 443
Protocol: "HTTPS"
Query: "#{query}"
StatusCode: HTTP_301
Type: redirect
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
# create a database security group
DBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "DB instances security group"
GroupName: "test-db-instance-SG"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '3306'
ToPort: '3306'
CidrIp: "0.0.0.0/0"
# SourceSecurityGroupName: !Ref 'EC2SecurityGroup'
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: "test db subnet group"
DBSubnetGroupName: "test-dbtier-subnet-group"
SubnetIds:
- !Ref PrivatesubnetA
- !Ref PrivatesubnetB
Tags:
- Key: Name
Value: us-east-1-test-dbtier-subnet-group
- Key: createdBy
Value: Niyaz
- Key: Project
Value: test-db
- Key: Environment
Value: test
# Create a RDS serverless DB
RDSCluster:
Type: AWS::RDS::DBCluster
Properties:
BackupRetentionPeriod : 1
DBClusterIdentifier : test-db-cluster
MasterUsername:
Ref: DBUsername
MasterUserPassword:
Ref: DBPassword
DatabaseName: !Ref DBName
Engine: aurora
EngineMode: serverless
EngineVersion: !Ref EngineVersion
ScalingConfiguration:
AutoPause: true
MaxCapacity: 2
MinCapacity: 1
SecondsUntilAutoPause: 300
DBSubnetGroupName:
Ref: DBSubnetGroup
VpcSecurityGroupIds:
- !Ref DBSecurityGroup
You have to run the command in your terminal below given:
Please make sure which directory in a cloudforamtion stack you saved mention in the file path. in my case we saved (/home/ec2-user/public_html/user/EC2-instance.yml)
aws cloudformation create-stack --template-body file:///home/ec2-user/public_html/user/EC2-instance.yml--stack-name "" --capabilities CAPABILITY_NAMED_IAM --parameters ParameterKey=KeyName,ParameterValue=sshkey-cloudformation-demo ParameterKey=InstanceType,ParameterValue=t2.micro ParameterKey=company,ParameterValue="" ParameterKey=RecordSetDomainName,ParameterValue="" ParameterKey=Tags,ParameterValue=codedeploy
The above command you need to use it in your application codes to run the automation process. You can either use a web form where the client inputs all the information that is needed by the ParameterValue. The above command will create the stack that composes of EC2 instance, Elastic load balancer, S3 bucket, RDS serverless instance, security groups.
Once the stack creation is complete you can have the application ready at company.maindomain.com.
We have cloudformation stack that also runs using ECS Fargate, Lambda, and many other AWS services that combines to support a fully automated SAAS application deployment .








