AWS Multi-tier VPC Architecture Design

In this post, i am going to show you, how to design a 3-Tier architecture, to host your application by following Best Practices. This option offers better security, HA (High Availability), Elasticity and Scalability, it’s fault tolerant and easier to manage/perform updates.

Some of the advantages are:

  • Decoupling – Breaking down your application
  • Development teams can work on different tiers and also is easier to recover from an issue as you know which tier is responsible for it. Your team can update servers in one layer without impacting the operation of the others. For example, a change to business logic, won’t impact the presentation layer.
  • High Availability as you are going to host your application across different Availability zones. (We are going to use 3 AZs but in the design diagram below i have used 2, for simplicity)
  • Each tier can scale when needed or when there are traffic spikes
  • The presentation (Web) tier of the infrastructure will be in a private subnets with no public IP assigned to the instances. Users can only reach the Web servers through the application load balancer.
  • All assets/images can be stored in a S3 bucket and retrieved by the App.
  • Databases can be Multi AZ with Read replicas and also use caching if required.
  • Traffic will be controlled by using Security Groups and ACLs and only the required ports will be open.

In a 3 Tier architecture we break down the application into 3 logical tiers or layers as they called.

  • Presentation Tier
    • This the tier where the Web Servers are going to be (Private Subnets)
  • Application Tier
    • This can also be referenced as Logic Tier. It’s where the application servers live, and it contains the business logic for the application.
  • Data Tier
    • This is where we place the Databases required for the application. The only way to access those databases is by connecting to them from the application layer.

To deploy a three-tier Virtual Private Cloud (VPC) using AWS CloudFormation, you will need to create a CloudFormation template in JSON or YAML. The template should define all of the resources needed for your VPC, including the VPC itself, subnets, route tables, internet gateway, and any other resources required by your application.

Here are the basic steps for deploying a three-tier VPC using CloudFormation:

  1. Create a new CloudFormation stack or modify an existing stack.
  2. Define the VPC, subnets, and internet gateway resources in your CloudFormation template.
  3. Define the route tables and routes for each subnet.
  4. Optionally, add resources for your application, such as EC2 instances, load balancers, and security groups.
  5. Validate your template to ensure it is correct.
  6. Review the changes that will be made by the CloudFormation stack and acknowledge them.
  7. Wait for the stack to reach the CREATE_COMPLETE or UPDATE_COMPLETE state.

You can use the AWS Management Console, AWS CLI, or SDKs to create and deploy your CloudFormation stack (see below)

AWSTemplateFormatVersion: '2010-09-09'
Description: '3 Tier VPC'

Parameters:


  ApplicationName:
    Description: 'Enter your app name:'
    Type: String
    MaxLength: '50'
    MinLength: '3'
    ConstraintDescription: Please input your app name.

Resources:


    myVPC:
      Type: AWS::EC2::VPC
      Properties:
        CidrBlock: 10.0.0.0/16
        InstanceTenancy: default
        EnableDnsSupport: true
        EnableDnsHostnames: true
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [myVPC, !Ref 'ApplicationName']]

    PublicSubnetA:
      Type: AWS::EC2::Subnet
      Properties:
        CidrBlock: 10.0.0.0/24
        AvailabilityZone:
          Fn::Select:
            - 0
            - Fn::GetAZs: !Ref AWS::Region
        MapPublicIpOnLaunch: true
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PublicSubnetA, !Ref 'ApplicationName']]

    PublicSubnetB:
      Type: AWS::EC2::Subnet
      Properties:
        CidrBlock: 10.0.1.0/24
        AvailabilityZone:
          Fn::Select:
            - 1
            - Fn::GetAZs: !Ref AWS::Region
        MapPublicIpOnLaunch: true
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PublicSubnetB, !Ref 'ApplicationName']]

    PublicSubnetC:
      Type: AWS::EC2::Subnet
      Properties:
        CidrBlock: 10.0.2.0/24
        AvailabilityZone:
          Fn::Select:
            - 2
            - Fn::GetAZs: !Ref AWS::Region
        MapPublicIpOnLaunch: true
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PublicSubnetC, !Ref 'ApplicationName']]

    PrivateSubnetA:
      Type: AWS::EC2::Subnet
      Properties:
        CidrBlock: 10.0.3.0/24
        AvailabilityZone:
          Fn::Select:
            - 0
            - Fn::GetAZs: !Ref AWS::Region
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PrivateSubnetA, !Ref 'ApplicationName']]

    PrivateSubnetB:
      Type: AWS::EC2::Subnet
      Properties:
        CidrBlock: 10.0.4.0/24
        AvailabilityZone:
          Fn::Select:
            - 1
            - Fn::GetAZs: !Ref AWS::Region
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PrivateSubnetB, !Ref 'ApplicationName']]

    PrivateSubnetC:
      Type: AWS::EC2::Subnet
      Properties:
        CidrBlock: 10.0.5.0/24
        AvailabilityZone:
          Fn::Select:
            - 2
            - Fn::GetAZs: !Ref AWS::Region
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PrivateSubnetC, !Ref 'ApplicationName']]

    myIGW:
      Type: AWS::EC2::InternetGateway
      Properties:
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [IGW, !Ref 'ApplicationName']]

    myNetworkACL:
      Type: AWS::EC2::NetworkAcl
      Properties:
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [NACL, !Ref 'ApplicationName']]

    myRoutePublic:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PublicRoute, !Ref 'ApplicationName']]

    myRoutePrivate:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [PrivateRoute, !Ref 'ApplicationName']]

    myLT:
      Type: AWS::EC2::LaunchTemplate
      Properties:
        LaunchTemplateName: myLT
        LaunchTemplateData:
          DimybleApiTermination: true
          ImageId: ami-012345678790
          UserData:
          InstanceType: t2.micro
          SecurityGroupIds: 
            - Ref: mySGapp

    myASG:
      Type: AWS::AutoScaling::AutoScalingGroup
      Properties:
        VPCZoneIdentifier:
           - !Ref PrivateSubnetA
           - !Ref PrivateSubnetB
           - !Ref PrivateSubnetC
        LaunchTemplate:
          LaunchTemplateId: !Ref myLT
          Version: !GetAtt myLT.LatestVersionNumber
        MaxSize: '3'
        MinSize: '2'
        TargetGroupARNs:
          - !Ref myALBTG

    myelb:
      Type: AWS::ElasticLoadBalancingV2::LoadBalancer
      Properties:
        Name: myelb
        Scheme: internet-facing
        Subnets: 
          - !Ref PublicSubnetA
          - !Ref PublicSubnetB
          - !Ref PublicSubnetC
        SecurityGroups: [!Ref 'mySGELB']
        Type: application
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: !Join ['-', [ELB, !Ref 'ApplicationName']]

    ALBHTTPListener:
      Type: AWS::ElasticLoadBalancingV2::Listener
      Properties:
        DefaultActions:
          - Type: "redirect"
            RedirectConfig:
              Protocol: "HTTPS"
              Port: 443
              Host: "#{host}"
              Path: "/#{path}"
              Query: "#{query}"
              StatusCode: "HTTP_301"
        LoadBalancerArn: !Ref myelb
        Port: 80
        Protocol: HTTP

    ALBHTTPSListener:
      Type: AWS::ElasticLoadBalancingV2::Listener
      Properties:
        DefaultActions:
          - Type: forward
            TargetGroupArn: !Ref myALBTG
        LoadBalancerArn: !Ref myelb
        Port: 443
        Protocol: HTTPS
        Certificates:
        - CertificateArn: arn:aws:acm:eu-west-1:123456789012:certificate/12345678-1234-1234-1234-123456789012 #Replace this with your certficate ARN
        SslPolicy: ELBSecurityPolicy-TLS-1-2-2017-01
    
    myALBTG:
      Type: AWS::ElasticLoadBalancingV2::TargetGroup
      Properties:
        HealthCheckIntervalSeconds: 30
        HealthCheckPath: /index.html
        HealthCheckProtocol: HTTP
        HealthCheckPort: traffic-port
        HealthCheckTimeoutSeconds: 5
        HealthyThresholdCount: 2
        Matcher:
          HttpCode: '200'
        Name: myInstances
        Port: 80
        Protocol: HTTP
        VpcId: !Ref 'myVPC'
        Tags:
          - Key: Name 
            Value: myALB-TG

    mySGELB:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: myapp - ELB security group
        VpcId: !Ref 'myVPC'
        SecurityGroupIngress:
          - IpProtocol: tcp
            ToPort: 80
            FromPort: 80
            CidrIp: 0.0.0.0/0
          - IpProtocol: tcp
            ToPort: 80
            FromPort: 443
            CidrIp: 0.0.0.0/0
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: ELBSecurityGroup

    mySGapp:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: myapp - App server security group
        VpcId: !Ref 'myVPC'
        SecurityGroupIngress:
          - IpProtocol: tcp
            ToPort: 80
            FromPort: 80
            SourceSecurityGroupId: !Ref 'mySGELB'
        Tags:
          - Key: environment
            Value: my3tier-vpc
          - Key: Name
            Value: AppServerSecurityGroup

    myNACLEntry1:
      Type: AWS::EC2::NetworkAclEntry
      Properties:
        CidrBlock: 0.0.0.0/0
        Egress: true
        Protocol: -1
        RuleAction: allow
        RuleNumber: 100
        NetworkAclId: !Ref 'myNetworkACL'

    myNACLEntry2:
      Type: AWS::EC2::NetworkAclEntry
      Properties:
        CidrBlock: 0.0.0.0/0
        Protocol: -1
        RuleAction: allow
        RuleNumber: 100
        NetworkAclId: !Ref 'myNetworkACL'

    subnetacl1:
      Type: AWS::EC2::SubnetNetworkAclAssociation
      Properties:
        NetworkAclId: !Ref 'myNetworkACL'
        SubnetId: !Ref 'PublicSubnetA'

    subnetacl2:
      Type: AWS::EC2::SubnetNetworkAclAssociation
      Properties:
        NetworkAclId: !Ref 'myNetworkACL'
        SubnetId: !Ref 'PublicSubnetB'

    subnetacl3:
      Type: AWS::EC2::SubnetNetworkAclAssociation
      Properties:
        NetworkAclId: !Ref 'myNetworkACL'
        SubnetId: !Ref 'PublicSubnetC'

    subnetacl4:
      Type: AWS::EC2::SubnetNetworkAclAssociation
      Properties:
        NetworkAclId: !Ref 'myNetworkACL'
        SubnetId: !Ref 'PrivateSubnetA'

    subnetacl5:
      Type: AWS::EC2::SubnetNetworkAclAssociation
      Properties:
        NetworkAclId: !Ref 'myNetworkACL'
        SubnetId: !Ref 'PrivateSubnetB'

    subnetacl6:
      Type: AWS::EC2::SubnetNetworkAclAssociation
      Properties:
        NetworkAclId: !Ref 'myNetworkACL'
        SubnetId: !Ref 'PrivateSubnetC'

    myIGWAttachment:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
        VpcId: !Ref 'myVPC'
        InternetGatewayId: !Ref 'myIGW'

    subnetRoutePublicA:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref 'myRoutePublic'
        SubnetId: !Ref 'PublicSubnetA'

    subnetRoutePublicB:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref 'myRoutePublic'
        SubnetId: !Ref 'PublicSubnetB'

    subnetRoutePublicC:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref 'myRoutePublic'
        SubnetId: !Ref 'PublicSubnetC'

    subnetRoutePrivateA:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref 'myRoutePrivate'
        SubnetId: !Ref 'PrivateSubnetA'

    subnetRoutePrivateB:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref 'myRoutePrivate'
        SubnetId: !Ref 'PrivateSubnetB'

    subnetRoutePrivateC:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref 'myRoutePrivate'
        SubnetId: !Ref 'PrivateSubnetC'

    publicroute:
      Type: AWS::EC2::Route
      Properties:
        DestinationCidrBlock: 0.0.0.0/0
        RouteTableId: !Ref 'myRoutePublic'
        GatewayId: !Ref 'myIGW'

    privateroute:
      Type: AWS::EC2::Route
      Properties:
        DestinationCidrBlock: 0.0.0.0/0
        RouteTableId: !Ref 'myRoutePrivate'
        NatGatewayId: !Ref 'NATGW'

    NATEIP:
      Type: AWS::EC2::EIP
      Properties:
        Domain: !Ref 'myVPC'

    NATGW:
      Type: AWS::EC2::NatGateway
      Properties: 
        AllocationId: !GetAtt NATEIP.AllocationId
        SubnetId: !Ref 'PublicSubnetA'


Outputs:


  LoadBalancerDNSName:
    Description: The DNS Name of the load balancer
    Value: !GetAtt myelb.DNSName

Once done, you can also enable/configure a number of AWS services to increase security, like Security Hub, GuardDuty, CloudTrail, AWS config, AWS Inspector.

AWS Well-Architected framework helps us, cloud architects, build secure, high-performing, resilient, and efficient infrastructure for a variety of applications and workloads.

For your reference these are the Pillars and we are going to discuss them in more details, in another post

  • Operational Excellence
  • Security
  • Reliability
  • Performance Efficiency
  • Cost Optimization
  • Sustainability

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

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