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 .