AWSTemplateFormatVersion: "2010-09-09"
Description: "VPC template for simulated on-prem environment. "
Parameters:
  OnPremVpcCidr:
    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]))$"
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 172.16.0.0/16
    Description: CIDR block for the VPC
    Type: String
  OnPremPublicSubnetCidr:
    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]))$"
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 172.16.0.0/24
    Description: CIDR block for Public subnet
    Type: String
  OnPremPrivateSubnetCidr:
    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]))$"
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 172.16.1.0/24
    Description: CIDR block for Private subnet
    Type: String
  InstanceType:
    AllowedValues:
    - t2.nano
    - t2.micro
    - t2.small
    - t2.medium
    - t2.large
    - t2.xlarge
    - t2.2xlarge
    - t3.nano
    - t3.micro
    - t3.small
    - t3.medium
    - t3.large
    - t3.xlarge
    - t3.2xlarge
    Default: t2.micro
    Description: Customer Gateway EC2 instance type.
    Type: String
  LatestAmiId:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64'
  LatestAmiIdCustomerGateway:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
  ParticipantIPAddress:
    Type: String
    Description: 'What is your external IP address in the format x.x.x.x/32? This entry will be added to certain Security Groups. Find your IP at https://checkip.amazonaws.com/'
    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])(\\/(32))$"
    ConstraintDescription: must be a valid IP address of the form x.x.x.x/32.

Resources:
  ## Data center resources
  OnPremVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref OnPremVpcCidr
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: "On Premises"

  OnPremPrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref 'AWS::Region'
      CidrBlock: !Ref OnPremPrivateSubnetCidr
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: "On-Premises Private Subnet"
        - Key: SubnetType
          Value: Private
      VpcId: !Ref OnPremVpc

  OnPremPrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref OnPremPrivateRouteTable
      SubnetId: !Ref OnPremPrivateSubnet

  OnPremPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref 'AWS::Region'
      CidrBlock: !Ref OnPremPublicSubnetCidr
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value:  "On-Premises Public Subnet"
        - Key: SubnetType
          Value: Public
      VpcId: !Ref OnPremVpc

  OnPremPublicRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref OnPremPublicSubnet
      RouteTableId: !Ref OnPremPublicRouteTable

  OnPremInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: "On-Premises IGW"

  OnPremAttachInternetGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    DependsOn:
      - OnPremInternetGateway
      - OnPremVpc
    Properties:
      InternetGatewayId: !Ref OnPremInternetGateway
      VpcId: !Ref OnPremVpc

  OnPremPublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags:
        - Key: Name
          Value: "On-Premises Public Route Table"
        - Key: Network
          Value: Public
      VpcId: !Ref OnPremVpc

  OnPremPublicRoute:
    Type: AWS::EC2::Route
    DependsOn:
      - OnPremAttachInternetGateway
    Properties:
      RouteTableId: !Ref OnPremPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref OnPremInternetGateway

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

  OnPremNatGateway:
    Type: AWS::EC2::NatGateway
    DependsOn: OnPremAttachInternetGateway
    Properties:
      AllocationId: !GetAtt OnPremNatEIP.AllocationId
      SubnetId: !Ref OnPremPublicSubnet
      Tags:
        - Key: Name
          Value: "On-Premises NATGW"

  OnPremPrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags:
        - Key: Name
          Value: "On-Premises Private Route Table"
        - Key: Network
          Value: Private
      VpcId: !Ref OnPremVpc

  OnPremPrivateRouteNat:
    Type: AWS::EC2::Route
    DependsOn: 
      - OnPremNatGateway
      - OnPremPrivateRouteTable
    Properties:
      RouteTableId: !Ref OnPremPrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref OnPremNatGateway

  OnPremPrivateRouteAws:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref OnPremPrivateRouteTable
      DestinationCidrBlock: 10.0.0.0/8
      InstanceId: !Ref OnPremCustomerGatewayServer

  OnPremAppServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for app server
      GroupName: "On-Premises App Server Security Group"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "10.0.0.0/8"
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "172.16.0.0/12"
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "192.168.0.0/16"
        - IpProtocol: icmp
          CidrIp: 10.0.0.0/8
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: 172.16.0.0/12
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: 192.168.0.0/16
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: !Ref ParticipantIPAddress
          FromPort: "-1"
          ToPort: "-1"          
      VpcId: !Ref OnPremVpc

  OnPremDnsServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for DNS server
      GroupName: "On-Premises DNS Server Security Group"
      SecurityGroupIngress:
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: "10.0.0.0/8"
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: "10.0.0.0/8"
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: "172.16.0.0/12"
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: "172.16.0.0/12"
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: "192.168.0.0/16"
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: "192.168.0.0/16"
        - IpProtocol: icmp
          CidrIp: 10.0.0.0/8
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: 172.16.0.0/12
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: 192.168.0.0/16
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: !Ref ParticipantIPAddress
          FromPort: "-1"
          ToPort: "-1"          
      VpcId: !Ref OnPremVpc

  OnPremCustomerGatewayServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Customer Gateway Server
      GroupName: "On-Premises Customer Gateway Security Group"
      SecurityGroupIngress:
        - IpProtocol: icmp
          CidrIp: 10.0.0.0/8
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: 172.16.0.0/12
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: 192.168.0.0/16
          FromPort: "-1"
          ToPort: "-1"
        - IpProtocol: icmp
          CidrIp: !Ref ParticipantIPAddress
          FromPort: "-1"
          ToPort: "-1"          
      VpcId: !Ref OnPremVpc

  OnPremDhcpOptions:
    Type: AWS::EC2::DHCPOptions
    DependsOn: OnPremDnsServer
    Properties:
      DomainName: "example.corp"
      DomainNameServers:
        - !GetAtt OnPremDnsServer.PrivateIp
      Tags:
        - Key: Name
          Value: "On-Premises DHCP Options"

  OnPremAttachDhcpOptions:
    Type: AWS::EC2::VPCDHCPOptionsAssociation
    DependsOn: OnPremDhcpOptions
    Properties:
      DhcpOptionsId: !Ref OnPremDhcpOptions
      VpcId: !Ref OnPremVpc

  OnPremAppServer:
    Type: AWS::EC2::Instance
    DependsOn: 
      - OnPremPrivateRouteNat
      - OnPremAppServerSecurityGroup
    Properties:
      IamInstanceProfile: NetworkingWorkshopInstanceProfile
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      PrivateIpAddress: 172.16.1.100
      SecurityGroupIds:
        - !Ref OnPremAppServerSecurityGroup
      SubnetId: !Ref OnPremPrivateSubnet
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          # set up web server
          dnf install -y httpd
          echo "Hello, world." > /var/www/html/index.html
          systemctl enable httpd.service
          systemctl start httpd.service
      Tags:
        - Key: Name
          Value: "On-Premises App Server"

  OnPremDnsServer:
    Type: AWS::EC2::Instance
    DependsOn:
      - OnPremPrivateRouteNat
      - OnPremDnsServerSecurityGroup
      - OnPremAppServer
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: "PT5M"
    Properties:
      IamInstanceProfile: NetworkingWorkshopInstanceProfile
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      PrivateIpAddress: 172.16.1.200
      SecurityGroupIds:
        - !Ref OnPremDnsServerSecurityGroup
      SubnetId: !Ref OnPremPrivateSubnet
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          # set up DNS server
          dnf install -y bind

          # replace named.conf
          cat > /etc/named.conf<< EOF
          options {
            directory       "/var/named";
            dump-file       "/var/named/data/cache_dump.db";
            statistics-file "/var/named/data/named_stats.txt";
            memstatistics-file "/var/named/data/named_mem_stats.txt";
            recursing-file  "/var/named/data/named.recursing";
            secroots-file   "/var/named/data/named.secroots";

            recursion yes;

            allow-query { any; };

            dnssec-enable no;
            dnssec-validation no;

            bindkeys-file "/etc/named.root.key";

            managed-keys-directory "/var/named/dynamic";

            pid-file "/run/named/named.pid";
            session-keyfile "/run/named/session.key";

            forwarders {
                    169.254.169.253;
            };
            forward first;
          };

          logging {
            channel default_debug {
                  file "data/named.run";
                  severity dynamic;
            };
          };


          zone "." IN {
                  type hint;
                  file "named.ca";
          };

          zone "example.corp" IN {
                  type master;
                  file "/etc/named/example.corp";
                  allow-update { none; };
          };

          include "/etc/named.rfc1912.zones";
          include "/etc/named.root.key";

          EOF

          # build zone file with my IP address and AppServer IP.
          ORIGIN='$ORIGIN'
          APPIP='${OnPremAppServer.PrivateIp}'
          MYIP='172.16.1.200'

          cat > /etc/named/example.corp<< EOF
          $ORIGIN example.corp.
          @                      3600 SOA   ns.example.corp. (
                                        zone-admin.example.com.     ; address of responsible party
                                        2020050701                 ; serial number
                                        3600                       ; refresh period
                                        600                        ; retry period
                                        604800                     ; expire time
                                        1800                     ) ; minimum ttl
                                86400 NS    ns1.example.corp.
          myapp                    60 IN A  $APPIP
          ns1                      60 IN A  $MYIP
          EOF

          # activate DNS server
          systemctl enable named.service
          systemctl start named.service

          # set up as local DNS resolver
          cat > /etc/resolv.conf<< EOF
          search example.corp
          nameserver $MYIP
          EOF

          # signal CFN that we are ready. We want our DNS server to be up before launching the next instance.
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource OnPremDnsServer --region ${AWS::Region}

      Tags:
        - Key: Name
          Value: "On-Premises DNS Server"

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

  OnPremCustomerGatewayServer:
    Type: AWS::EC2::Instance
    DependsOn:
      - OnPremPublicRoute
      - OnPremAttachDhcpOptions
    Properties:
      IamInstanceProfile: NetworkingWorkshopInstanceProfile
      ImageId: !Ref LatestAmiIdCustomerGateway
      InstanceType: !Ref InstanceType
      PrivateIpAddress: 172.16.0.100
      SecurityGroupIds:
        - !Ref OnPremCustomerGatewayServerSecurityGroup
      SourceDestCheck: false
      SubnetId: !Ref OnPremPublicSubnet
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash

          # install OpenSWAN
          yum install -y openswan
          systemctl enable ipsec.service

          # Enable IP forwarding
          cat >> /etc/sysctl.conf<< EOF
          net.ipv4.ip_forward = 1
          net.ipv4.conf.default.rp_filter = 0
          net.ipv4.conf.default.accept_source_route = 0
          EOF

          sysctl -p
      Tags:
        - Key: Name
          Value: "On-Premises Customer Gateway"

  OnPremCustomerGatewayServerEIPAssociation:
    Type: AWS::EC2::EIPAssociation
    DependsOn:
      - OnPremCustomerGatewayServerEIP
      - OnPremCustomerGatewayServer
    Properties:
      AllocationId: !GetAtt OnPremCustomerGatewayServerEIP.AllocationId
      InstanceId: !Ref OnPremCustomerGatewayServer

Outputs:
  OnPremVpc:
    Value: !Ref OnPremVpc
  OnPremVpcCidr:
    Value: !Ref OnPremVpcCidr
  OnPremPublicSubnet:
    Value: !Ref OnPremPublicSubnet
  OnPremPrivateSubnet:
    Value: !Ref OnPremPrivateSubnet
  OnPremCustomerGatewayServerSecurityGroup:
    Value: !Ref OnPremCustomerGatewayServerSecurityGroup
  PublicIp:
    Value: !GetAtt OnPremCustomerGatewayServer.PublicIp
  OnPremAppServerPrivateIp:
    Value: !GetAtt OnPremAppServer.PrivateIp
  OnPremDnsServerPrivateIp:
    Value: !GetAtt OnPremDnsServer.PrivateIp