# Following example shows how to implement AWS Gateway Load Balancer (GWLB)
# integrated with AWS Transit Gateway for centralized architecture using AWS
# CloudFormation. It uses nested stacks and deploys entire architecture as
# 1-click deployment.

# For architecture details refer to blog:
# https://aws.amazon.com/blogs/networking-and-content-delivery/centralized-inspection-architecture-with-aws-gateway-load-balancer-and-aws-transit-gateway/

# This is the primary template. Primar template launches:
#   - Appliance VPC template to configure Application VPC
#   - Spoke VPC template to configure to Spoke VPCs: Spoke 1 and 2.
#   - Transit Gateway template to configure Transti Gateway.

AWSTemplateFormatVersion: "2010-09-09"

Description: >-
  AWS CloudFormation Sample Template For deploying AWS Gateway Load Balancer
  (GWLB) in centralized architecture. This primary template, using nested
  stacks, creates appropriate resources arcoss 2 AZs.
  
  **WARNING** This template creates one or more Amazon EC2 instances,
  GWLB, GWLB endpints, NAT gateways and Transit Gateway. You will be billed for the AWS 
  resources used if you create a stack from this template.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Availability Zone Configuration
        Parameters:
          - AvailabilityZone1
          - AvailabilityZone2
      - Label:
          default: Generice EC2 - Configuration
        Parameters:
          - EC2InstanceType
          - EC2InstanceAmiId
          - EC2InstanceDiskSize
          - KeyPairName
          - AccessLocation          
      - Label:
          default: Appliance VPC - Network Configuration
        Parameters:
          - ApplianceVpcCidr
          - ApplianceVpcTgwAttachSubnet1Cidr
          - ApplianceVpcApplianceSubnet1Cidr
          - ApplianceVpcNatgwSubnet1Cidr
          - ApplianceVpcTgwAttachSubnet2Cidr
          - ApplianceVpcApplianceSubnet2Cidr
          - ApplianceVpcNatgwSubnet2Cidr
      - Label:
          default: Appliance VPC - Gateway Load Balancer Configuration
        Parameters:
          - ApplianceVpcGwlbName
          - ApplianceVpcTargetGroupName
          - ApplianceVpcHealthPort
          - ApplianceVpcHealthProtocol
      - Label:
          default: Spoke 1 VPC - Network Configuration
        Parameters:
          - Spoke1VpcCidr
          - Spoke1VpcApplicationSubnet1Cidr
          - Spoke1VpcTgwAttachSubnet1Cidr
          - Spoke1VpcBastionSubnet1Cidr
          - Spoke1VpcApplicationSubnet2Cidr
          - Spoke1VpcTgwAttachSubnet2Cidr
          - Spoke1VpcBastionSubnet2Cidr          
      - Label:
          default: Spoke 2 VPC - Network Configuration
        Parameters:
          - Spoke2VpcCidr
          - Spoke2VpcApplicationSubnet1Cidr
          - Spoke2VpcTgwAttachSubnet1Cidr
          - Spoke2VpcBastionSubnet1Cidr
          - Spoke2VpcApplicationSubnet2Cidr
          - Spoke2VpcTgwAttachSubnet2Cidr
          - Spoke2VpcBastionSubnet2Cidr

    ParameterLabels:
      AvailabilityZone1:
        default: Availability Zone 1
      AvailabilityZone2:
        default: Availability Zone 2
      EC2InstanceType:
        default: Ec2 Instance Type
      EC2InstanceAmiId:
        default: Latest AMI ID for EC2 intance        
      EC2InstanceDiskSize:
        default: Appliance Instance Size in GB
      KeyPairName:
        default: KeyPair required for accessing Appliance instance
      AccessLocation:
        default: Network CIDR to access Appliance instance        
      ApplianceVpcCidr:
        default: Appliance VPC - Network CIDR for VPC
      ApplianceVpcTgwAttachSubnet1Cidr:
        default: Appliance VPC - TGW Attachment Subnet 1 CIDR in AZ1
      ApplianceVpcApplianceSubnet1Cidr:
        default: Appliance VPC - Appliance Subnet 1 CIDR in AZ1
      ApplianceVpcNatgwSubnet1Cidr:
        default: Appliance VPC - NAT GW Subnet 1 CIDR in AZ1     
      ApplianceVpcTgwAttachSubnet2Cidr:
        default: Appliance VPC - TGW Attachment Subnet 2 CIDR in AZ2
      ApplianceVpcApplianceSubnet2Cidr:
        default: Appliance VPC - Appliance Subnet 2 CIDR in AZ2
      ApplianceVpcNatgwSubnet2Cidr:
        default: Appliance VPC - NAT GW Subnet 2 CIDR in AZ2     
      ApplianceVpcGwlbName:
        default: Appliance VPC - Gateway Load Balancer Name
      ApplianceVpcTargetGroupName:
        default: Appliance VPC - Target Group Name
      ApplianceVpcHealthPort:
        default: Appliance VPC - Health Check Port
      ApplianceVpcHealthProtocol:
        default: Appliance VPC - Health Check Protocol
      Spoke1VpcCidr:
        default: Spoke1 VPC - VPC CIDR       
      Spoke1VpcApplicationSubnet1Cidr:
        default: Spoke1 VPC - Application Subnet 1 CIDR
      Spoke1VpcTgwAttachSubnet1Cidr:
        default: Spoke1 VPC - TGW Attachment Subnet 1 CIDR
      Spoke1VpcBastionSubnet1Cidr:
        default: Spoke1 VPC - Bastion Subnet 1 CIDR    
      Spoke1VpcApplicationSubnet2Cidr:
        default: Spoke1 VPC - Application Subnet 2 CIDR
      Spoke1VpcTgwAttachSubnet2Cidr:
        default: Spoke1 VPC - TGW Attachment Subnet 2 CIDR
      Spoke1VpcBastionSubnet2Cidr:
        default: Spoke1 VPC - Bastion Subnet 2 CIDR        
      Spoke2VpcCidr:
        default: Spoke2 VPC - VPC CIDR    
      Spoke2VpcApplicationSubnet1Cidr:
        default: Spoke2 VPC - Application Subnet 1 CIDR
      Spoke2VpcTgwAttachSubnet1Cidr:
        default: Spoke2 VPC - TGW Attachment Subnet 1 CIDR
      Spoke2VpcBastionSubnet1Cidr:
        default: Spoke2 VPC - Bastion Subnet 1 CIDR
      Spoke2VpcApplicationSubnet2Cidr:
        default: Spoke2 VPC - Application Subnet 2 CIDR
      Spoke2VpcTgwAttachSubnet2Cidr:
        default: Spoke2 VPC - TGW Attachment Subnet 2 CIDR
      Spoke2VpcBastionSubnet2Cidr:
        default: Spoke2 VPC - Bastion Subnet 2 CIDR        

Parameters:
  # Generic Parameters:
  AvailabilityZone1:
    Description: Availability Zone 1
    Type: AWS::EC2::AvailabilityZone::Name
    ConstraintDescription: Valid Availability Zone Id
  AvailabilityZone2:
    Description: Availability Zone 2
    Type: AWS::EC2::AvailabilityZone::Name
    ConstraintDescription: Valid Availability Zone Id
  EC2InstanceType:
    Description: Select EC2 instance type. Default is set to t2.micro
    Default: t2.micro
    AllowedValues:
      - t2.micro
    Type: String
  EC2InstanceAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'    
  EC2InstanceDiskSize:
    Description: EC2 instance disk size in GB. Default is set to 8GB
    Default: 8
    AllowedValues: [8]
    Type: Number
    ConstraintDescription: Should be a valid instance size in GB
  KeyPairName:
    Description: EC2 KeyPair required for accessing EC2 instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: Must be the name of an existing EC2 KeyPair
  AccessLocation:
    Description: >-
      Enter desired Network CIDR to allow traffic to EC2 instance. Default is
      set to access from anywhere and it is not recommended
    AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
    MinLength: "9"
    MaxLength: "18"
    Default: 0.0.0.0/0
    Type: String
    ConstraintDescription: Must be a valid Network CIDR of the form x.x.x.x/y
  # Appliance VPC specific parameters:
  ApplianceVpcCidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 192.168.1.0/24
    Description: Appliance VPC - CIDR block for the VPC
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/y
  # AZ1:    
  ApplianceVpcTgwAttachSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 192.168.1.0/28
    Description: Appliance VPC - TGW Attachment Subnet 1 CIDR in AZ1
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  ApplianceVpcApplianceSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 192.168.1.16/28
    Description: Appliance VPC - Appliance Subnet 1 CIDR in AZ1
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  ApplianceVpcNatgwSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 192.168.1.32/28
    Description: Appliance VPC - NAT GW Subnet 1 CIDR in AZ1
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  # AZ2:    
  ApplianceVpcTgwAttachSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 192.168.1.48/28
    Description: Appliance VPC - TGW Attachment Subnet 2 CIDR in AZ2
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  ApplianceVpcApplianceSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 192.168.1.64/28
    Description: Appliance VPC - Appliance Subnet 2 CIDR in AZ2
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  ApplianceVpcNatgwSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 192.168.1.80/28
    Description: Appliance VPC - NAT GW Subnet 2 CIDR in AZ2
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  ApplianceVpcGwlbName:
    Description: >-
      Gateway Load Balancer name. This name must be unique within your AWS 
      account and can have a maximum of 32 alphanumeric characters and 
      hyphens. A name cannot begin or end with a hyphen.
    Type: String
    Default: gwlb1
    ConstraintDescription: Must be a valid GWLB Name
  ApplianceVpcTargetGroupName:
    Description: Target Group Name
    Type: String
    Default: gwlb1-tg1
    ConstraintDescription: Must be a valid target group name
  ApplianceVpcHealthProtocol:
    Description: >-
      The protocol GWLB uses when performing health checks on targets.
      Default is HTTP.
    Type: String
    Default: HTTP
    AllowedValues: ['TCP', 'HTTP', 'HTTPS']
    ConstraintDescription: Must be a valid health check protocol
  ApplianceVpcHealthPort:
    Description: >- 
      The port the load balancer uses when performing health checks
      on targets. Default is 80.
    Type: String
    Default: '80'
    ConstraintDescription: Must be a valid health check port
  # Spoke1 VPC specific parameters:
  Spoke1VpcCidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.0.0/24
    Description: Spoke1 VPC - CIDR block for the VPC
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  # AZ1:    
  Spoke1VpcApplicationSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.0.0/28
    Description: Spoke1 VPC - Application Subnet 1 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  Spoke1VpcTgwAttachSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.0.16/28
    Description: Spoke1 VPC - TGW Attachment Subnet 1 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: Subnet CIDR parameter must be in the form x.x.x.x/16-28
  Spoke1VpcBastionSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.0.64/28
    Description: Spoke1 VPC - Bastion Subnet 1 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28    
  # AZ2:
  Spoke1VpcApplicationSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.0.32/28
    Description: Spoke1 VPC - Application Subnet 2 CIDR in Availability Zone 2
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  Spoke1VpcTgwAttachSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.0.48/28
    Description: Spoke1 VPC - TGW Attachment Subnet 2 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: Subnet CIDR parameter must be in the form x.x.x.x/16-28
  Spoke1VpcBastionSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.0.80/28
    Description: Spoke1 VPC - Bastion Subnet 2 CIDR in Availability Zone 2
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28    
  # Spoke2 VPC specific parameters:
  Spoke2VpcCidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.1.0/24
    Description: Spoke2 VPC - CIDR block for the VPC
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  # AZ1:    
  Spoke2VpcApplicationSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.1.0/28
    Description: Spoke2 VPC - Application Subnet 1 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28    
  Spoke2VpcTgwAttachSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.1.16/28
    Description: Spoke2 VPC - TGW Attachment Subnet 1 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: Subnet CIDR parameter must be in the form x.x.x.x/16-28 
  Spoke2VpcBastionSubnet1Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.1.64/28
    Description: Spoke2 VPC - Bastion Subnet 1 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  # AZ2:      
  Spoke2VpcApplicationSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.1.32/28
    Description: Spoke2 VPC - Application Subnet 2 CIDR in Availability Zone 2
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
  Spoke2VpcTgwAttachSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.1.48/28
    Description: Spoke2 VPC - TGW Attachment Subnet 2 CIDR in Availability Zone 1
    Type: String
    ConstraintDescription: Subnet CIDR parameter must be in the form x.x.x.x/16-28
  Spoke2VpcBastionSubnet2Cidr:
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
    Default: 10.0.1.80/28
    Description: Spoke2 VPC - Bastion Subnet 2 CIDR in Availability Zone 2
    Type: String
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28    

Resources:
  ApplianceVpcStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: 'https://s3-us-west-2.amazonaws.com/tech-content-us-west-2/aws-gwlb-cloudformation-samples/centralized-architecture/nested-stack/ApplianceVpc2Az.yaml'
      Parameters:
        VpcCidr: !Ref ApplianceVpcCidr
        AvailabilityZone1: !Ref AvailabilityZone1
        TgwAttachSubnet1Cidr: !Ref ApplianceVpcTgwAttachSubnet1Cidr
        ApplianceSubnet1Cidr: !Ref ApplianceVpcApplianceSubnet1Cidr
        NatgwSubnet1Cidr: !Ref ApplianceVpcNatgwSubnet1Cidr
        AvailabilityZone2: !Ref AvailabilityZone2
        TgwAttachSubnet2Cidr: !Ref ApplianceVpcTgwAttachSubnet2Cidr
        ApplianceSubnet2Cidr: !Ref ApplianceVpcApplianceSubnet2Cidr
        NatgwSubnet2Cidr: !Ref ApplianceVpcNatgwSubnet2Cidr
        ApplianceInstanceType: !Ref EC2InstanceType
        ApplianceInstanceAmiId: !Ref EC2InstanceAmiId
        ApplianceInstanceDiskSize: !Ref EC2InstanceDiskSize
        KeyPairName: !Ref KeyPairName
        AccessLocation: !Ref AccessLocation
        Spoke1VpcCidr: !Ref Spoke1VpcCidr
        Spoke2VpcCidr: !Ref Spoke2VpcCidr
        GwlbName: !Ref ApplianceVpcGwlbName
        TargetGroupName: !Ref ApplianceVpcTargetGroupName
        HealthPort: !Ref ApplianceVpcHealthPort
        HealthProtocol: !Ref ApplianceVpcHealthProtocol

  Spoke1VpcStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: 'https://s3-us-west-2.amazonaws.com/tech-content-us-west-2/aws-gwlb-cloudformation-samples/centralized-architecture/nested-stack/SpokeVpc2Az.yaml'
      Parameters:
        VpcCidr: !Ref Spoke1VpcCidr
        AvailabilityZone1: !Ref AvailabilityZone1
        BastionSubnet1Cidr: !Ref Spoke1VpcBastionSubnet1Cidr
        ApplicationSubnet1Cidr: !Ref Spoke1VpcApplicationSubnet1Cidr
        TgwAttachSubnet1Cidr: !Ref Spoke1VpcTgwAttachSubnet1Cidr
        AvailabilityZone2: !Ref AvailabilityZone2
        BastionSubnet2Cidr: !Ref Spoke1VpcBastionSubnet2Cidr
        ApplicationSubnet2Cidr: !Ref Spoke1VpcApplicationSubnet2Cidr
        TgwAttachSubnet2Cidr: !Ref Spoke1VpcTgwAttachSubnet2Cidr
        ApplicationInstanceType: !Ref EC2InstanceType
        ApplicationInstanceAmiId: !Ref EC2InstanceAmiId
        ApplicationInstanceDiskSize: !Ref EC2InstanceDiskSize
        KeyPairName: !Ref KeyPairName
        AccessLocation: !Ref AccessLocation

  Spoke2VpcStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: 'https://s3-us-west-2.amazonaws.com/tech-content-us-west-2/aws-gwlb-cloudformation-samples/centralized-architecture/nested-stack/SpokeVpc2Az.yaml'
      Parameters:
        VpcCidr: !Ref Spoke2VpcCidr
        AvailabilityZone1: !Ref AvailabilityZone1
        BastionSubnet1Cidr: !Ref Spoke2VpcBastionSubnet1Cidr
        ApplicationSubnet1Cidr: !Ref Spoke2VpcApplicationSubnet1Cidr
        TgwAttachSubnet1Cidr: !Ref Spoke2VpcTgwAttachSubnet1Cidr
        AvailabilityZone2: !Ref AvailabilityZone2
        BastionSubnet2Cidr: !Ref Spoke2VpcBastionSubnet2Cidr
        ApplicationSubnet2Cidr: !Ref Spoke2VpcApplicationSubnet2Cidr
        TgwAttachSubnet2Cidr: !Ref Spoke2VpcTgwAttachSubnet2Cidr
        ApplicationInstanceType: !Ref EC2InstanceType
        ApplicationInstanceAmiId: !Ref EC2InstanceAmiId
        ApplicationInstanceDiskSize: !Ref EC2InstanceDiskSize
        KeyPairName: !Ref KeyPairName
        AccessLocation: !Ref AccessLocation

  TgwStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: ['ApplianceVpcStack', 'Spoke1VpcStack', 'Spoke2VpcStack' ]
    Properties:
      TemplateURL: 'https://s3-us-west-2.amazonaws.com/tech-content-us-west-2/aws-gwlb-cloudformation-samples/centralized-architecture/nested-stack/Tgw.yaml'
      Parameters:
        ApplianceVpcId: !GetAtt
          - ApplianceVpcStack
          - Outputs.ApplianceVpcId
        ApplianceVpcTgwAttachSubnet1Id: !GetAtt ['ApplianceVpcStack', 'Outputs.ApplianceTgwAttachSubnet1Id']
        ApplianceVpcTgwAttachSubnet2Id: !GetAtt ['ApplianceVpcStack', 'Outputs.ApplianceTgwAttachSubnet2Id']
        ApplianceVpcApplianceRtb1Id: !GetAtt ['ApplianceVpcStack', 'Outputs.ApplianceRtb1Id']
        ApplianceVpcApplianceRtb2Id: !GetAtt ['ApplianceVpcStack', 'Outputs.ApplianceRtb2Id']
        Spoke1VpcId: !GetAtt ['Spoke1VpcStack', 'Outputs.SpokeVpcId']
        Spoke1VpcCidr: !GetAtt ['Spoke1VpcStack', 'Outputs.SpokeVpcCidr']
        Spoke1VpcTgwAttachSubnet1Id: !GetAtt ['Spoke1VpcStack', 'Outputs.SpokeTgwAttachSubnet1Id']
        Spoke1VpcTgwAttachSubnet2Id: !GetAtt ['Spoke1VpcStack', 'Outputs.SpokeTgwAttachSubnet2Id']
        Spoke1VpcRtb1Id: !GetAtt ['Spoke1VpcStack', 'Outputs.SpokeApplicationRouteTableId']
        Spoke2VpcId: !GetAtt ['Spoke2VpcStack', 'Outputs.SpokeVpcId']
        Spoke2VpcCidr: !GetAtt ['Spoke2VpcStack', 'Outputs.SpokeVpcCidr']
        Spoke2VpcTgwAttachSubnet1Id: !GetAtt ['Spoke2VpcStack', 'Outputs.SpokeTgwAttachSubnet1Id']
        Spoke2VpcTgwAttachSubnet2Id: !GetAtt ['Spoke2VpcStack', 'Outputs.SpokeTgwAttachSubnet2Id']
        Spoke2VpcRtb1Id: !GetAtt ['Spoke2VpcStack', 'Outputs.SpokeApplicationRouteTableId']
        AccessLocation: !Ref AccessLocation