---
AWSTemplateFormatVersion: '2010-09-09'
Description: Basic VPC with Public/Private Subnets.
  This template creates a simple VPC architecture with one public and one private subnet in a single Availability Zone.
  The template offers flexibility by allowing you to either create a new VPC or use an existing one.
  When creating a new VPC, it provisions an Internet Gateway for public subnet connectivity.
  A NAT Gateway is deployed in the public subnet to enable outbound internet access for resources in the private subnet.
  Appropriate route tables are configured to direct traffic between the subnets and the internet.
  The template supports customizable CIDR ranges for both the VPC and subnet configurations.
  Use this template as a starting point for creating basic two-tier architectures with public-facing and backend resources.
  Refer to the Outputs tab of the deployed stack for important resource identifiers including VPC ID, subnet IDs, and internet gateway ID.

### Stack metadata
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: VPC
        Parameters:
          - AvailabilityZone
          - VpcCIDR
          - VpcId
          - InternetGatewayId
      - Label:
          default: Subnets
        Parameters:
          - PublicCIDR
          - PrivateCIDR

Parameters:
  AvailabilityZone:
    Description: "(Optional) Availability Zone in which you want to create your subnet(s). Required if you opt to create a VPC."
    Type: String
  InternetGatewayId:
    Description: "(Optional) The id of the Internet Gateway. Required if using an existing VPC."
    Type: String
    Default: ''
  PrivateCIDR:
    AllowedPattern: "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/(1[6-9]|2[0-9]|3[0-2])$"
    Description: CIDR block for the private subnet
    Default: 10.0.16.0/20
    Type: String
  PublicCIDR:
    AllowedPattern: "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/(1[6-9]|2[0-9]|3[0-2])$"
    Description: CIDR block for the public subnet
    Default: 10.0.0.0/24
    Type: String
  VpcId:
    Description: "(Optional) The VPC to create subnets in. Leave blank to create a new VPC."
    Default: ''
    Type: String
  VpcCIDR:
    Description: "CIDR block for the VPC if it will be created. Required if you opt to create a VPC."
    Default: 10.0.0.0/16
    Type: String

Conditions:
  CreateInternetGateway:
    Fn::Equals:
    - Ref: InternetGatewayId
    - ''
  CreateVpc:
    Fn::Equals:
    - Ref: VpcId
    - ''
  ExistingInternetGateway:
    Fn::Not:
    - Fn::Equals:
      - Ref: InternetGatewayId
      - ''

Resources:
  Vpc:
    Condition: CreateVpc
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock:
        Ref: VpcCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: "Name"
          Value: !Sub '${AWS::StackName}:Basic-HPC'

  DefaultRouteDependsOnPublic:
    Condition: CreateInternetGateway
    DependsOn: VPCGatewayAttachment
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Fn::If:
        - CreateInternetGateway
        - Ref: InternetGateway
        - Ref: InternetGatewayId
      RouteTableId:
        Ref: RouteTablePublic
    Type: AWS::EC2::Route

  DefaultRouteNoDependsOnPublic:
    Condition: ExistingInternetGateway
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Fn::If:
        - CreateInternetGateway
        - Ref: InternetGateway
        - Ref: InternetGatewayId
      RouteTableId:
        Ref: RouteTablePublic
    Type: AWS::EC2::Route

  InternetGateway:
    Condition: CreateInternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}:InternetGateway'
      - Key: Stack
        Value:
          Ref: AWS::StackId
    Type: AWS::EC2::InternetGateway

  NatEIPPublic:
    Properties:
      Domain: vpc
    Type: AWS::EC2::EIP

  NatGatewayPublic:
    Properties:
      AllocationId:
        Fn::GetAtt:
        - NatEIPPublic
        - AllocationId
      SubnetId:
        Ref: Public
    Type: AWS::EC2::NatGateway

  NatRoutePrivate:
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId:
        Ref: NatGatewayPublic
      RouteTableId:
        Ref: RouteTablePrivate
    Type: AWS::EC2::Route

  Private:
    Properties:
      AvailabilityZone:
        Ref: AvailabilityZone
      CidrBlock:
        Ref: PrivateCIDR
      MapPublicIpOnLaunch: false
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}:PrivateSubnetA-${AvailabilityZone}'
      - Key: Stack
        Value:
          Ref: AWS::StackId
      VpcId:
        Fn::If:
        - CreateVpc
        - Ref: Vpc
        - Ref: VpcId
    Type: AWS::EC2::Subnet

  Public:
    Properties:
      AvailabilityZone:
        Ref: AvailabilityZone
      CidrBlock:
        Ref: PublicCIDR
      MapPublicIpOnLaunch: true
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}:PublicSubnetA-${AvailabilityZone}'
      - Key: Stack
        Value:
          Ref: AWS::StackId
      VpcId:
        Fn::If:
        - CreateVpc
        - Ref: Vpc
        - Ref: VpcId
    Type: AWS::EC2::Subnet

  RouteAssociationPrivate:
    Properties:
      RouteTableId:
        Ref: RouteTablePrivate
      SubnetId:
        Ref: Private
    Type: AWS::EC2::SubnetRouteTableAssociation

  RouteAssociationPublic:
    Properties:
      RouteTableId:
        Ref: RouteTablePublic
      SubnetId:
        Ref: Public
    Type: AWS::EC2::SubnetRouteTableAssociation

  RouteTablePrivate:
    Properties:
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}:PrivateRoute'
      - Key: Stack
        Value:
          Ref: AWS::StackId
      VpcId:
        Fn::If:
        - CreateVpc
        - Ref: Vpc
        - Ref: VpcId
    Type: AWS::EC2::RouteTable

  RouteTablePublic:
    Properties:
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}:PublicRoute'
      - Key: Stack
        Value:
          Ref: AWS::StackId
      VpcId:
        Fn::If:
        - CreateVpc
        - Ref: Vpc
        - Ref: VpcId
    Type: AWS::EC2::RouteTable

  VPCGatewayAttachment:
    Condition: CreateInternetGateway
    Properties:
      InternetGatewayId:
        Ref: InternetGateway
      VpcId:
        Fn::If:
        - CreateVpc
        - Ref: Vpc
        - Ref: VpcId
    Type: AWS::EC2::VPCGatewayAttachment

Outputs:
  VPC:
    Value:
      Fn::If:
        - CreateVpc
        - Ref: Vpc
        - Ref: VpcId
    Description: ID of the VPC
    Export:
      Name: !Sub ${AWS::StackName}-VPC
  DefaultPrivateSubnet:
    Description: The ID of a default private subnet
    Value: !Ref Private
    Export:
      Name: !Sub "${AWS::StackName}-DefaultPrivateSubnet"
  DefaultPublicSubnet:
    Description: The ID of a default public subnet
    Value: !Ref Public
    Export:
      Name: !Sub "${AWS::StackName}-DefaultPublicSubnet"
  InternetGatewayId:
    Description: The ID of the Internet Gateway
    Value: 
      Fn::If:
        - CreateInternetGateway
        - Ref: InternetGateway
        - Ref: InternetGatewayId
    Export:
      Name: !Sub "${AWS::StackName}-InternetGateway"