
Parameters:
  EnvironmentName:
    Description: An environment name that is prefixed to resource names
    Type: String
    Default: rdsStack

  VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String
    Default: 10.1.0.0/16

  PublicSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
    Type: String
    Default: 10.1.1.0/24

  PublicSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone
    Type: String
    Default: 10.1.2.0/24

  PrivateSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
    Type: String
    Default: 10.1.3.0/24

  PrivateSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
    Type: String
    Default: 10.1.4.0/24

  SSHKeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Choose a SSH key for the instances
    Default: bigdata-hol

  LatestAmazonLinux2AMIId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"

  MyPcIpAddress:
    Type: String
    Default: 111.111.111.111/32

  RdsMasterUsername:
    Type: String
    #Default: "admin"
    NoEcho: true

  RdsMasterUserPassword:
    Type: String
    #Default: "welcome1"
    NoEcho: true

Resources:
  MyVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Ref EnvironmentName

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref EnvironmentName

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

  PublicSubnetAz1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVpc
      AvailabilityZone: !Select [ 0, !GetAZs '' ]
      CidrBlock: !Ref PublicSubnet1CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public-AZ1

  PublicSubnetAz2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVpc
      AvailabilityZone: !Select [ 1, !GetAZs  '' ]
      CidrBlock: !Ref PublicSubnet2CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public-AZ2

  PrivateSubnetAz1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVpc
      AvailabilityZone: !Select [ 0, !GetAZs  '' ]
      CidrBlock: !Ref PrivateSubnet1CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Private-AZ1

  PrivateSubnetAz2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVpc
      AvailabilityZone: !Select [ 1, !GetAZs  '' ]
      CidrBlock: !Ref PrivateSubnet2CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Private-AZ2

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

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

  NatGatewayAz1:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayEipAz1.AllocationId
      SubnetId: !Ref PublicSubnetAz1
      Tags:
        - Key: Name
          Value : NatGatewayAz1

  NatGatewayAz2:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayEipAz2.AllocationId
      SubnetId: !Ref PublicSubnetAz2
      Tags:
        - Key: Name
          Value : NatGatewayAz2

  PublicRouteTableAz1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public-AZ1

  PublicRouteTableAz2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public-AZ2

  ## internet route rule
  DefaultPublicRouteAz1:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTableAz1
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  DefaultPublicRouteAz2:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTableAz2
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetAz1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTableAz1
      SubnetId: !Ref PublicSubnetAz1

  PublicSubnetAz2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTableAz2
      SubnetId: !Ref PublicSubnetAz2

####### private subnet section ########
  PrivateRouteTableAz1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Private-AZ1

  DefaultPrivateRouteAz1:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableAz1
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayAz1

  PrivateSubnetAz1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableAz1
      SubnetId: !Ref PrivateSubnetAz1

  PrivateRouteTableAz2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Private-AZ2

  DefaultPrivateRouteAz2:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableAz2
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayAz2

  PrivateSubnetAz2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableAz2
      SubnetId: !Ref PrivateSubnetAz2

################# RDS Router EC2 Config. Section. #################
  ## RDS Router EC2 Security Group
  RdsRouterVmSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: "RdsRouterVmSecurityGroup"
      GroupDescription: "RdsRouterVmSecurityGroup"
      VpcId: !Ref MyVpc
      SecurityGroupIngress:
        - IpProtocol: tcp  ## ssh
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyPcIpAddress
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: !Ref VpcCIDR
        - IpProtocol: tcp
          FromPort: 15000
          ToPort: 15000
          CidrIp: !Ref VpcCIDR
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

  ## RdsRouter EC2 config.
  RdsRouterVmAz1:
    Type: AWS::EC2::Instance
    DependsOn: [InternetGateway, MySqlDb1]
    Properties:
      ImageId: !Ref LatestAmazonLinux2AMIId
      InstanceType: m5.2xlarge
      SubnetId: !Ref PublicSubnetAz1
      KeyName: !Ref SSHKeyName
      SecurityGroupIds:
      - !Ref RdsRouterVmSecurityGroup
      SourceDestCheck: false
      BlockDeviceMappings:
      - DeviceName: /dev/sda1
        Ebs:
          VolumeSize: 50
          VolumeType: gp2
          Encrypted: true
      Tags:
      - Key: Name
        Value: "RdsRouterVmAz1"
      UserData:
        Fn::Base64: !Sub |
            #!/bin/bash -xe
            set -x
            exec > >(tee /var/log/user-data.log|logger -t user-data ) 2>&1
            yum update -y
            yum install nc -y
            yum install go -y
            yum install iptables-services -y
            systemctl enable iptables
            # install go and setup go env.
            cd ~
            cat <<EOF > /root/dummyport.go
            package main
            
            import (
                "fmt"
                "net/http"
            )
            
            func main() {
                http.HandleFunc("/", HelloServer)
                http.ListenAndServe(":15000", nil)
            }
            
            func HelloServer(w http.ResponseWriter, r *http.Request) {
                fmt.Fprintf(w, "Hello Server $(hostname), %s!", r.URL.Path[1:])
            }
            EOF
            mkdir ~/go
            export GOPATH=~/go
            export GOCACHE=/root/.cache/go-build
            echo "export GOPATH=~/go" >> ~/.bash_profile
            echo "export GOCACHE=~/.cache/go-build" >> ~/.bash_profile
            nohup go run /root/dummyport.go &
            cat << 'EOF' > /root/dummygo-check.sh
            #!/bin/bash
            date
            ps -ef | grep dummyport | grep -v grep
            DUMMYPORT_PROCESS_COUNT=`ps -ef | grep dummyport | grep -v grep | wc -l`
            echo "The number of dummyport process: " $DUMMYPORT_PROCESS_COUNT
            
            if [ `expr $DUMMYPORT_PROCESS_COUNT + 0` -gt 0 ]
            then
              echo "Exiting."
              exit 0
            else
              echo "Run dummyport.go process."
              nohup go run /root/dummyport.go &
            fi
            EOF
            sysctl -w net.ipv4.ip_forward=1
            echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/nat.conf
            sysctl net.ipv4.ip_forward
            RDS_CONNECTION_IP=`nslookup ${MySqlDb1.Endpoint.Address} | egrep "^Address" | grep -v "#53" | awk '{print $2}'`
            iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 3306 -j DNAT --to $RDS_CONNECTION_IP:3306
            iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
            service iptables save
            # crontab
            cat << 'EOF' > /root/rds_iptables_update.sh
            #!/bin/bash
            date
            for i in {1..5}
            do
              RDS_CONNECTION_IP=`nslookup ${MySqlDb1.Endpoint.Address} | egrep "^Address" | grep -v "#53" | awk '{print $2}'`
              #echo "## Current RDS_CONNECTION_IP : " $RDS_CONNECTION_IP
              set -x
              /sbin/iptables -t nat -R PREROUTING 1 -i eth0 -p tcp --dport 3306 -j DNAT --to $RDS_CONNECTION_IP:3306
              set +x
              sleep 10
            done
            EOF
            cat << 'EOF' > crontab_joblists.txt
            * * * * * sh /root/dummygo-check.sh 2>&1 | tee -a /root/dummygo-check.sh.log
            * * * * * sh /root/rds_iptables_update.sh 2>&1 | tee -a /root/rds_iptables_update.sh.log
            EOF
            crontab crontab_joblists.txt

  RdsRouterVmAz2:
    Type: AWS::EC2::Instance
    DependsOn: [InternetGateway, MySqlDb1, NatGatewayAz2]
    Properties:
      ImageId: !Ref LatestAmazonLinux2AMIId
      InstanceType: m5.2xlarge
      SubnetId: !Ref PublicSubnetAz2
      KeyName: !Ref SSHKeyName
      SecurityGroupIds:
      - !Ref RdsRouterVmSecurityGroup
      SourceDestCheck: false
      BlockDeviceMappings:
      - DeviceName: /dev/sda1
        Ebs:
          VolumeSize: 50
          VolumeType: gp2
          Encrypted: true
      Tags:
      - Key: Name
        Value: "RdsRouterVmAz2"
      UserData:
        Fn::Base64: !Sub |
            #!/bin/bash -xe
            set -x
            exec > >(tee /var/log/user-data.log|logger -t user-data ) 2>&1
            yum update -y
            yum install nc -y
            yum install go -y
            yum install iptables-services -y
            systemctl enable iptables
            # install go and setup go env.
            cd ~
            cat <<EOF > /root/dummyport.go
            package main
            
            import (
                "fmt"
                "net/http"
            )
            
            func main() {
                http.HandleFunc("/", HelloServer)
                http.ListenAndServe(":15000", nil)
            }
            
            func HelloServer(w http.ResponseWriter, r *http.Request) {
                fmt.Fprintf(w, "Hello Server $(hostname), %s!", r.URL.Path[1:])
            }
            EOF
            mkdir ~/go
            export GOPATH=~/go
            export GOCACHE=/root/.cache/go-build
            echo "export GOPATH=~/go" >> ~/.bash_profile
            echo "export GOCACHE=~/.cache/go-build" >> ~/.bash_profile
            nohup go run /root/dummyport.go &
            cat << 'EOF' > /root/dummygo-check.sh
            #!/bin/bash
            date
            ps -ef | grep dummyport | grep -v grep
            DUMMYPORT_PROCESS_COUNT=`ps -ef | grep dummyport | grep -v grep | wc -l`
            echo "The number of dummyport process: " $DUMMYPORT_PROCESS_COUNT
            
            if [ `expr $DUMMYPORT_PROCESS_COUNT + 0` -gt 0 ]
            then
              echo "Exiting."
              exit 0
            else
              echo "Run dummyport.go process."
              nohup go run /root/dummyport.go &
            fi
            EOF
            sysctl -w net.ipv4.ip_forward=1
            echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/nat.conf
            sysctl net.ipv4.ip_forward
            RDS_CONNECTION_IP=`nslookup ${MySqlDb1.Endpoint.Address} | egrep "^Address" | grep -v "#53" | awk '{print $2}'`
            iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 3306 -j DNAT --to $RDS_CONNECTION_IP:3306
            iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
            service iptables save
            # crontab
            cat << 'EOF' > /root/rds_iptables_update.sh
            #!/bin/bash
            date
            for i in {1..5}
            do
              RDS_CONNECTION_IP=`nslookup ${MySqlDb1.Endpoint.Address} | egrep "^Address" | grep -v "#53" | awk '{print $2}'`
              #echo "## Current RDS_CONNECTION_IP : " $RDS_CONNECTION_IP
              set -x
              /sbin/iptables -t nat -R PREROUTING 1 -i eth0 -p tcp --dport 3306 -j DNAT --to $RDS_CONNECTION_IP:3306
              set +x
              sleep 10
            done
            EOF
            cat << 'EOF' > crontab_joblists.txt
            * * * * * sh /root/dummygo-check.sh 2>&1 | tee -a /root/dummygo-check.sh.log
            * * * * * sh /root/rds_iptables_update.sh 2>&1 | tee -a /root/rds_iptables_update.sh.log
            EOF
            crontab crontab_joblists.txt

  ## NLB
  NlbForRdsRouterVm:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties: 
      IpAddressType: ipv4
      LoadBalancerAttributes: 
        - Key: load_balancing.cross_zone.enabled
          Value: false
      Name: "NlbForRdsRouterVm"
      Scheme: internal
      SubnetMappings: 
        - SubnetId: !Ref PublicSubnetAz1
        - SubnetId: !Ref PublicSubnetAz2
      Tags: 
        - Key: Name
          Value: "NlbForRdsRouterVm"
      Type: network

  ## NLB TG
  TgForNlbForRdsRouterVm:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties: 
      HealthCheckIntervalSeconds: 10
      HealthCheckPort: 15000
      HealthCheckProtocol: TCP
      HealthyThresholdCount: 2
      Name: "TgForNlbForRdsRouterVm"
      Port: 3306
      Protocol: TCP
      Tags: 
        - Key: Name
          Value: "TgForNlbForRdsRouterVm"
      TargetGroupAttributes: 
        - Key: preserve_client_ip.enabled
          Value: true
      Targets: 
        - Id: !Ref RdsRouterVmAz1
          Port: 3306
        - Id: !Ref RdsRouterVmAz2
          Port: 3306
      TargetType: instance
      UnhealthyThresholdCount: 2
      VpcId: !Ref MyVpc

  ## NLB Listener
  ListenerForRdsRouterVm:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties: 
      DefaultActions: 
        - TargetGroupArn: !Ref TgForNlbForRdsRouterVm
          Type: forward
      LoadBalancerArn: !Ref NlbForRdsRouterVm
      Port: 3306
      Protocol: TCP

######## Bastion RDS Client EC2 ###########
  BastionSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: "BastionSecurityGroup"
      GroupDescription: "BastionSecurityGroup"
      VpcId: !Ref MyVpc
      SecurityGroupIngress:
        - IpProtocol: tcp  ## ssh
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyPcIpAddress
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

  BastionClientAz1:
    Type: AWS::EC2::Instance
    DependsOn: InternetGateway
    Properties:
      ImageId: !Ref LatestAmazonLinux2AMIId
      InstanceType: m5.xlarge
      SubnetId: !Ref PublicSubnetAz1
      KeyName: !Ref SSHKeyName
      SecurityGroupIds:
      - !Ref BastionSecurityGroup
      BlockDeviceMappings:
      - DeviceName: /dev/sda1
        Ebs:
          VolumeSize: 50
          VolumeType: gp2
          Encrypted: true
      Tags:
      - Key: Name
        Value: "BastionClientAz1"
      SourceDestCheck: false
      UserData:
        Fn::Base64: !Sub |
            #!/bin/bash -xe
            set -x
            exec > >(tee /var/log/user-data.log|logger -t user-data ) 2>&1
            yum update -y
            yum install mysql -y
            yum install nc -y
            echo "export AWS_DEFAULT_REGION=${AWS::Region}" >> /home/ec2-user/.bash_profile

####### for RDS ######
  RdsDbSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: "RdsDbSubnetGroup"
      DBSubnetGroupName: "RdsDbSubnetGroup"
      SubnetIds: 
        - !Ref PrivateSubnetAz1
        - !Ref PrivateSubnetAz2
      Tags: 
      - Key: Name
        Value: "RdsDbSubnetGroup"

  ## RDS DB Security Group
  RdsDbSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: "RdsDbSecurityGroup"
      GroupDescription: "RdsDbSecurityGroup"
      VpcId: !Ref MyVpc
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: !Ref VpcCIDR
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

  MySqlDb1:
    Type: AWS::RDS::DBInstance
    Properties: 
      DBInstanceIdentifier: "MySqlDb1"
      DBName: "maindb"
      AllocatedStorage: 100
      DBInstanceClass: "db.m5.2xlarge"
      MasterUsername: !Ref RdsMasterUsername
      MasterUserPassword: !Ref RdsMasterUserPassword
      StorageType: "io1"
      Iops: 5000
      Engine: "MySQL"
      MultiAZ: true
      DBSubnetGroupName: !Ref RdsDbSubnetGroup
      PubliclyAccessible: false
      VPCSecurityGroups:
        - !Ref RdsDbSecurityGroup
      StorageEncrypted: true
      DeletionProtection: true
      Tags:
      - Key: Name
        Value: "MySqlDb1"

################# Output Sections ###############
Outputs:
  BastionClientAz1:
    Value: !Sub "ssh -A ec2-user@${BastionClientAz1.PublicIp} ## BastionClientAz1 ${BastionClientAz1.PrivateIp}"

  RdsRouterVmAz1:
    Value: !Sub "ssh -A ec2-user@${RdsRouterVmAz1.PublicIp} ## RdsRouterVmAz1 ${RdsRouterVmAz1.PrivateIp}"

  RdsRouterVmAz2:
    Value: !Sub "ssh -A ec2-user@${RdsRouterVmAz2.PublicIp} ## RdsRouterVmAz2 ${RdsRouterVmAz2.PrivateIp}"

  NlbForRdsRouterVm:
    Value: !Sub "export NLB_DNS=${NlbForRdsRouterVm.DNSName}"
