Skip to content

Building a Secure and Scalable Three-Tier Architecture on AWS using CloudFormation

11 minute read
Content level: Intermediate
2

Guide for building secure, scalable three-tier architecture on AWS using CloudFormation, detailing components, benefits, and implementation steps for enterprise applications.

Building a Secure and Scalable Three-Tier Architecture on AWS

In today's cloud-first world, designing resilient, secure, and scalable architectures is essential for businesses of all sizes. In this article, we'll explore a production-ready three-tier high availability architecture implemented with AWS CloudFormation, breaking down its components, benefits, and implementation details.

What is a Three-Tier Architecture?

A three-tier architecture separates an application into three logical and physical computing tiers:

  1. Presentation Tier (Web): Handles user interface and client requests
  2. Application Tier (App): Contains the business logic and processing
  3. Data Tier (Database): Manages data storage and access

This separation of concerns improves security, scalability, and maintainability by isolating each component.

Architecture Overview:

Our implementation uses AWS CloudFormation to provision a complete three-tier architecture with high availability features:

Architecture Diagram

The architecture includes:

  • Network Layer: VPC with public, private, and isolated subnets across two availability zones
  • Security Layer: WAF, security groups, and network ACLs
  • Web Tier: Application Load Balancer in public subnets
  • Application Tier: Auto Scaling EC2 instances in private subnets
  • Data Tier: Aurora MySQL cluster in isolated subnets
  • Management: Bastion host for secure administrative access

**Key Components

Network Infrastructure**

The foundation of our architecture is a well-designed VPC with multiple subnet types:

VPC:
  Type: AWS::EC2::VPC
  Properties:
    CidrBlock: !Ref VpcCIDR
    EnableDnsHostnames: true
    EnableDnsSupport: true

We create three types of subnets across two availability zones:

  • Public Subnets (10.0.1.0/24, 10.0.2.0/24): Host internet-facing resources
  • Private Subnets (10.0.10.0/24, 10.0.11.0/24): Host application servers
  • Isolated Subnets (10.0.20.0/24, 10.0.21.0/24): Host database resources

This design implements the principle of least privilege at the network level, with each tier having only the connectivity it requires.

Security Controls

Multiple security layers protect our infrastructure:

  1. AWS WAF: Protects against common web exploits and attacks
WebACL:
  Type: AWS::WAFv2::WebACL
  Properties:
    Name: !Sub ${EnvironmentName}-WebACL
    Scope: REGIONAL
    Rules:
      - Name: AWSManagedRulesCommonRuleSet
        Priority: 1
  1. Security Groups: Implement fine-grained access control
EC2SecurityGroup:
  Type: AWS::EC2::SecurityGroup
  Properties:
    SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        SourceSecurityGroupId: !Ref ALBSecurityGroup
  1. Network Isolation: Each tier is isolated in its own subnet type

High Availability Features

The architecture implements multiple high availability features:

1.*** Multi-AZ Deployment:*** All components are deployed across two availability zones 2. Auto Scaling: Application tier automatically adjusts capacity

AutoScalingGroup:
  Type: AWS::AutoScaling::AutoScalingGroup
  Properties:
    VPCZoneIdentifier:
      - !Ref PrivateSubnet1
      - !Ref PrivateSubnet2
    MinSize: '2'
    MaxSize: '6'
    DesiredCapacity: '2'
  1. Load Balancing: Distributes traffic and provides health checks
  2. Database Redundancy: Aurora MySQL with primary and replica instances

Application Tier

The application tier uses EC2 instances in an Auto Scaling group:

LaunchTemplate:
  Type: AWS::EC2::LaunchTemplate
  Properties:
    LaunchTemplateData:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      SecurityGroupIds:
        - !Ref EC2SecurityGroup

The instances are bootstrapped with a simple web server for demonstration purposes, but in a real-world scenario, you would deploy your application code here.

Data Tier

The data tier uses Amazon Aurora MySQL, a fully managed database service:

DBCluster:
  Type: AWS::RDS::DBCluster
  Properties:
    Engine: aurora-mysql
    DatabaseName: !Ref DBName
    DBSubnetGroupName: !Ref DBSubnetGroup
    VpcSecurityGroupIds:
      - !Ref DBSecurityGroup

Aurora provides built-in high availability with automatic failover capabilities.

Setting Up the Architecture

Prerequisites

Before deploying this architecture, you'll need:

  1. An AWS account with appropriate permissions
  2. AWS CLI installed and configured or console access
  3. An EC2 key pair for SSH access
  4. A secure password for the database

Deployment Steps

  1. Save the CloudFormation template to a file named three-tier-ha.yaml
  2. Deploy the stack using AWS CLI:

aws cloudformation create-stack \ --stack-name three-tier-ha \ --template-body file://three-tier-ha.yaml \ --parameters \ ParameterKey=KeyPairName,ParameterValue=your-key-pair \ ParameterKey=DBPassword,ParameterValue=your-secure-password \ --capabilities CAPABILITY_IAM

  1. Monitor the deployment in the AWS CloudFormation console
  2. Access the outputs once deployment is complete:

aws cloudformation describe-stacks \ --stack-name three-tier-ha \ --query "Stacks[0].Outputs"

  1. Connect to the bastion host to access resources in private subnets:

ssh -i your-key.pem ec2-user@<BastionPublicIP>

Benefits of This Architecture

Security

  • **Defense in Depth: **Multiple security layers protect your application
  • Network Isolation: Each tier has its own security controls
  • Least Privilege: Components only have the access they need
  • ** WAF Protection:** Guards against common web vulnerabilities

Scalability

  • Horizontal Scaling: Auto Scaling groups adjust to demand
  • Vertical Scaling: Instance types can be changed as needed
  • **Database Scaling: **Aurora scales compute and storage independently

Reliability

  • **Multi-AZ Deployment: **Resilient to availability zone failures
  • **Auto Recovery: **Failed instances are automatically replaced
  • Load Distribution: Traffic is balanced across healthy instances
  • **Database Redundancy: **Automatic failover for database instances

Maintainability

  • Infrastructure as Code: Entire architecture defined in CloudFormation
  • Parameterized Deployment: Easily customize for different environments
  • Separation of Concerns: Each tier can be updated independently
  • Bastion Host: Secure administrative access

Cost Optimization

While this architecture provides enterprise-grade features, you can optimize costs by:

  1. Right-sizing instances: Start with smaller instance types and scale as needed
  2. Auto Scaling policies: Scale down during low-traffic periods
  3. Reserved Instances: Purchase reserved instances for predictable workloads
  4. Aurora Serverless: Consider Aurora Serverless for variable workloads

Enhancements and Next Steps This architecture provides a solid foundation, but consider these enhancements for production use:

  • **HTTPS Support: **Add SSL/TLS certificates and configure HTTPS listeners
  • Monitoring: Implement CloudWatch dashboards and alarms
  • Backup Strategy: Configure automated backups and test restoration
  • CI/CD Pipeline: Automate application deployment *** Session Management:** Add ElastiCache for session storage
  • Content Delivery: Integrate CloudFront for static content

Conclusion

The three-tier high availability architecture presented in this article provides a robust foundation for deploying applications on AWS. By leveraging CloudFormation, you can consistently deploy this architecture across multiple environments while maintaining security, scalability, and reliability. This architecture follows AWS best practices and can be adapted to a wide range of applications, from simple websites to complex enterprise systems. The separation of tiers, combined with high availability features, ensures your application can handle failures gracefully while providing a consistent experience to your users. Whether you're building a new application or migrating an existing one to AWS, this architecture provides a proven pattern for success in the cloud.

Full file: three-tier-ha.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 3-Tier HA Architecture with WAF, ALB, EC2 Auto Scaling, and Aurora MySQL

Parameters:
  LatestAmiId:
    Type: String
    Default: ami-05ffe3c48a9991133
    Description: AMI ID for the EC2 instances

  EnvironmentName:
    Type: String
    Default: ThreeTierHA

  VpcCIDR:
    Type: String
    Default: 10.0.0.0/16

  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Name of an existing EC2 KeyPair

  InstanceType:
    Type: String
    Default: t3.xlarge
    AllowedValues:
      - t3.large
      - t3.xlarge
      - t3.2xlarge

  DBInstanceType:
    Type: String
    Default: db.r5.large

  DBName:
    Type: String
    Default: appdb

  DBUsername:
    Type: String
    Default: admin
    NoEcho: true

  DBPassword:
    Type: String
    NoEcho: true
    MinLength: '8'

Mappings:
  SubnetConfig:
    VPC:
      CIDR: 10.0.0.0/16
    Public1:
      CIDR: 10.0.1.0/24
    Public2:
      CIDR: 10.0.2.0/24
    Private1:
      CIDR: 10.0.10.0/24
    Private2:
      CIDR: 10.0.11.0/24
    Isolated1:
      CIDR: 10.0.20.0/24
    Isolated2:
      CIDR: 10.0.21.0/24

Resources:
  # VPC and Network Configuration
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-VPC

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-IGW

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # Public Subnets
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 0
        - !GetAZs ''
      CidrBlock: !FindInMap
        - SubnetConfig
        - Public1
        - CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PublicSubnet1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 1
        - !GetAZs ''
      CidrBlock: !FindInMap
        - SubnetConfig
        - Public2
        - CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PublicSubnet2

  # Private Subnets
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 0
        - !GetAZs ''
      CidrBlock: !FindInMap
        - SubnetConfig
        - Private1
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PrivateSubnet1

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 1
        - !GetAZs ''
      CidrBlock: !FindInMap
        - SubnetConfig
        - Private2
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PrivateSubnet2

  # Isolated Subnets for Database
  IsolatedSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 0
        - !GetAZs ''
      CidrBlock: !FindInMap
        - SubnetConfig
        - Isolated1
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-IsolatedSubnet1

  IsolatedSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 1
        - !GetAZs ''
      CidrBlock: !FindInMap
        - SubnetConfig
        - Isolated2
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-IsolatedSubnet2

  # NAT Gateways
  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

  NATGateway1EIP:
    Type: AWS::EC2::EIP
    DependsOn: InternetGatewayAttachment
    Properties:
      Domain: vpc

  NATGateway2EIP:
    Type: AWS::EC2::EIP
    DependsOn: InternetGatewayAttachment
    Properties:
      Domain: vpc

  # Route Tables and Associations
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PublicRoutes

  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}-PrivateRoutes1

  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}-PrivateRoutes2

  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

  IsolatedRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-IsolatedRoutes

  IsolatedSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref IsolatedRouteTable
      SubnetId: !Ref IsolatedSubnet1

  IsolatedSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref IsolatedRouteTable
      SubnetId: !Ref IsolatedSubnet2

  # Security Groups
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ALB Security Group
      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

  BastionSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Bastion Host Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref ALBSecurityGroup
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref BastionSecurityGroup

  DBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Aurora Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref EC2SecurityGroup

  # Bastion Host
  BastionHost:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.micro
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyPairName
      SubnetId: !Ref PublicSubnet1
      SecurityGroupIds: 
        - !Ref BastionSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-Bastion

  # WAF Web ACL
  WebACL:
    Type: AWS::WAFv2::WebACL
    Properties:
      Name: !Sub ${EnvironmentName}-WebACL
      Scope: REGIONAL
      DefaultAction:
        Allow: {}
      Rules:
        - Name: AWSManagedRulesCommonRuleSet
          Priority: 1
          OverrideAction:
            None: {}
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesCommonRuleSet
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWSManagedRulesCommonRuleSetMetric
      VisibilityConfig:
        SampledRequestsEnabled: true
        CloudWatchMetricsEnabled: true
        MetricName: !Sub ${EnvironmentName}-WebACLMetric

  # Application Load Balancer
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      Type: application

  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ALBTargetGroup

  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckPath: /
      HealthCheckIntervalSeconds: 30
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VPC

  # Launch Template
  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: !Ref LatestAmiId
        InstanceType: !Ref InstanceType
        KeyName: !Ref KeyPairName
        SecurityGroupIds:
          - !Ref EC2SecurityGroup
        TagSpecifications:
          - ResourceType: instance
            Tags:
            - Key: Name
              Value: !Sub ${EnvironmentName}-Int
        UserData: !Base64
          Fn::Sub: |
            #!/bin/bash
            yum update -y
            yum install -y httpd
            systemctl start httpd
            systemctl enable httpd
            
            # Get IMDSv2 token
            TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

            # Use token to get metadata
            INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/instance-id)
            AVAILABILITY_ZONE=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/placement/availability-zone)
            PRIVATE_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/local-ipv4)

            cat <<EOF > /var/www/html/index.html
            <!DOCTYPE html>
            <html>
            <head>
                <title>Welcome to My Web Server</title>
                <style>
                    body {
                        font-family: Arial, sans-serif;
                        margin: 40px;
                        background-color: #f0f0f0;
                    }
                    .container {
                        background-color: white;
                        padding: 20px;
                        border-radius: 8px;
                        box-shadow: 0 0 10px rgba(0,0,0,0.1);
                    }
                    h1 {
                        color: #333;
                    }
                </style>
            </head>
            <body>
                <div class="container">
                    <h1>Welcome to ${EnvironmentName}</h1>
                    <p>This server is running on Amazon Linux 2</p>
                    <p>Instance ID: $INSTANCE_ID</p>
                    <p>Availability Zone: $AVAILABILITY_ZONE</p>
                    <p>Private IP: $PRIVATE_IP</p>
                    <p>Timestamp: $(date)</p>
                </div>
            </body>
            </html>
            EOF

            # Set proper permissions
            chmod 644 /var/www/html/index.html

  # Auto Scaling Group
  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      LaunchTemplate:
        LaunchTemplateId: !Ref LaunchTemplate
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      MinSize: '2'
      MaxSize: '6'
      DesiredCapacity: '2'
      CapacityRebalance: true
      NewInstancesProtectedFromScaleIn: false
      TargetGroupARNs:
        - !Ref ALBTargetGroup
      HealthCheckType: ELB
      HealthCheckGracePeriod: 300

  # Aurora DB Cluster
  DBCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      Engine: aurora-mysql
      DatabaseName: !Ref DBName
      MasterUsername: !Ref DBUsername
      MasterUserPassword: !Ref DBPassword
      DBSubnetGroupName: !Ref DBSubnetGroup
      VpcSecurityGroupIds:
        - !Ref DBSecurityGroup
      AvailabilityZones:
        - !Select
          - 0
          - !GetAZs ''
        - !Select
          - 1
          - !GetAZs ''

  DBInstance1:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: aurora-mysql
      DBClusterIdentifier: !Ref DBCluster
      DBInstanceClass: !Ref DBInstanceType
      DBSubnetGroupName: !Ref DBSubnetGroup

  DBInstance2:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: aurora-mysql
      DBClusterIdentifier: !Ref DBCluster
      DBInstanceClass: !Ref DBInstanceType
      DBSubnetGroupName: !Ref DBSubnetGroup

  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: Subnet group for Aurora DB
      SubnetIds:
        - !Ref IsolatedSubnet1
        - !Ref IsolatedSubnet2

Outputs:
  VPC:
    Description: VPC ID
    Value: !Ref VPC

  ALBDNSName:
    Description: ALB DNS Name
    Value: !GetAtt ApplicationLoadBalancer.DNSName

  DBEndpoint:
    Description: Aurora Cluster Endpoint
    Value: !GetAtt DBCluster.Endpoint.Address

  WebACLArn:
    Description: WAF Web ACL ARN
    Value: !GetAtt WebACL.Arn

  BastionPublicIP:
    Description: Bastion Host Public IP
    Value: !GetAtt BastionHost.PublicIp