A Cloud Architect Company
Multi Tenant SAAS application in AWS
Amazon Web Services

Building Multi Tenant SAAS application in AWS (Fully automated): Part3

This is part 3 and final section on “Building Multi Tenant SAAS application in AWS” if you have not read part 1 and part2. Please refer Building Multi Tenant SAAS application in AWS (Fully automated): Part1 and Building Multi 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 .

 

Leave a Reply

Your email address will not be published. Required fields are marked *

back to top