AWSTemplateFormatVersion: '2010-09-09' Transform: 'AWS::Serverless-2016-10-31' 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 Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" RdsMasterUsername: Type: String #Default: "admin" NoEcho: true RdsMasterUserPassword: Type: String #Default: "welcome1" NoEcho: true MyPcIpAddress: Type: String Default: 111.111.111.111/32 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 < /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 < /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 - Key: deregistration_delay.timeout_seconds Value: 10 Targets: - Id: !Ref RdsRouterVmAz1 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 ####### 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" ######## Bastion RDS Client EC2 ########### ## Bastion Host EC2 config. Ec2AdminRole: Type: 'AWS::IAM::Role' Properties: RoleName: Ec2-admin-role-345 AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM Ec2AdminRoleInstanceProfile: Type: 'AWS::IAM::InstanceProfile' Properties: Roles: - !Ref Ec2AdminRole 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,RdsRouterVmAz1,RdsRouterVmAz2] Properties: ImageId: !Ref LatestAmazonLinux2AMIId InstanceType: m5.xlarge SubnetId: !Ref PublicSubnetAz1 KeyName: !Ref SSHKeyName IamInstanceProfile: !Ref Ec2AdminRoleInstanceProfile SecurityGroupIds: - !Ref BastionSecurityGroup Tags: - Key: Name Value: "BastionClientAz1" SourceDestCheck: false BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 50 VolumeType: gp2 Encrypted: true 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 amazon-linux-extras install epel -y yum install mysql -y yum install nc -y yum install jq -y export AWS_DEFAULT_REGION=${AWS::Region} echo "Create route table rules of RDS subnet of AZ1" aws ec2 create-route --route-table-id ${PrivateRouteTableAz1.RouteTableId} --destination-cidr-block ${PublicSubnet1CIDR} --instance-id ${RdsRouterVmAz1} aws ec2 create-route --route-table-id ${PrivateRouteTableAz1.RouteTableId} --destination-cidr-block ${PublicSubnet2CIDR} --instance-id ${RdsRouterVmAz1} echo "Create route table rules of RDS subnet of AZ2" aws ec2 create-route --route-table-id ${PrivateRouteTableAz2.RouteTableId} --destination-cidr-block ${PublicSubnet1CIDR} --instance-id ${RdsRouterVmAz1} aws ec2 create-route --route-table-id ${PrivateRouteTableAz2.RouteTableId} --destination-cidr-block ${PublicSubnet2CIDR} --instance-id ${RdsRouterVmAz1} ## python echo "alias python='python3'" >> /home/ec2-user/.bash_profile echo "alias pip='pip3'" >> /home/ec2-user/.bash_profile echo "set -o vi" >> /home/ec2-user/.bash_profile echo "export PATH=$PATH:/usr/local/bin" >> /home/ec2-user/.bash_profile cd ~ rm -f /usr/bin/aws rm -rf /usr/local/aws-cli/v2 curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip ./aws/install pip3 install boto3 BastionClientAz2: Type: AWS::EC2::Instance DependsOn: InternetGateway Properties: ImageId: !Ref LatestAmazonLinux2AMIId InstanceType: m5.xlarge SubnetId: !Ref PublicSubnetAz2 KeyName: !Ref SSHKeyName IamInstanceProfile: !Ref Ec2AdminRoleInstanceProfile SecurityGroupIds: - !Ref BastionSecurityGroup Tags: - Key: Name Value: "BastionClientAz2" SourceDestCheck: false BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 50 VolumeType: gp2 Encrypted: true 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 amazon-linux-extras install epel -y yum install mysql -y yum install nc -y yum install jq -y export AWS_DEFAULT_REGION=${AWS::Region} ## python echo "alias python='python3'" >> /home/ec2-user/.bash_profile echo "alias pip='pip3'" >> /home/ec2-user/.bash_profile echo "set -o vi" >> /home/ec2-user/.bash_profile echo "export PATH=$PATH:/usr/local/bin" >> /home/ec2-user/.bash_profile cd ~ rm -f /usr/bin/aws rm -rf /usr/local/aws-cli/v2 curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip ./aws/install pip3 install boto3 ######## Lambda ########### ## Lambda Security Group LambdaSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: "LambdaSecurityGroup" GroupDescription: "LambdaSecurityGroup" VpcId: !Ref MyVpc SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref VpcCIDR - IpProtocol: tcp FromPort: 8080 ToPort: 8080 CidrIp: !Ref VpcCIDR - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Ref VpcCIDR SecurityGroupEgress: - IpProtocol: -1 CidrIp: 0.0.0.0/0 ## LambdaRole LambdaExecRole: Type: AWS::IAM::Role Properties: RoleName: 'LambdaExecRole' AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - 'sts:AssumeRole' Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess RdsRouterHealthCheckLambda: Type: AWS::Serverless::Function DependsOn: [TgForNlbForRdsRouterVm, RdsRouterVmAz1, RdsRouterVmAz2] Properties: FunctionName: 'RdsRouterHealthCheck' Timeout: 30 VpcConfig: SecurityGroupIds: - !Ref LambdaSecurityGroup SubnetIds: - !Ref PrivateSubnetAz1 - !Ref PrivateSubnetAz2 Role: !GetAtt LambdaExecRole.Arn Runtime: 'python3.9' Handler: index.lambda_handler Events: StartScheduledEvent: Type: Schedule Properties: Schedule: 'cron(* * * * ? *)' InlineCode: !Sub | import json import boto3 import socket import time from boto3.session import Session # Variables AWS_REGION="${AWS::Region}" ip_address_of_host_of_az1 = "${RdsRouterVmAz1.PrivateIp}" ip_address_of_host_of_az2 = "${RdsRouterVmAz2.PrivateIp}" port_of_healthcheck = 15000 port_of_service = 3306 route_table_id_of_az1 = "${PrivateRouteTableAz1}" route_table_id_of_az2 = "${PrivateRouteTableAz2}" client_destination_cidr_az1 = "${PublicSubnet1CIDR}" client_destination_cidr_az2 = "${PublicSubnet2CIDR}" InstanceId_Of_RdsRouter_az1 = "${RdsRouterVmAz1}" InstanceId_Of_RdsRouter_az2 = "${RdsRouterVmAz2}" TargetGroupArn_of_Nlb = "${TgForNlbForRdsRouterVm}" session = Session(region_name=AWS_REGION) def lambda_handler(event, context): #def main(): print("-Debug-def lambda_handler(event, context) or main()") currentServiceAz = current_service_az() if currentServiceAz != "undecidable" and isHealchCheckSucceeded(currentServiceAz) == False: updateRouteTable() registerInstanceAsNlbTarget(currentServiceAz) def getServiceTarget(): print("-Debug-def getHealthyTarget()") serviceTarget = "undecidable" client = session.client('elbv2') response = client.describe_target_health( TargetGroupArn= TargetGroupArn_of_Nlb ) #print(response) print("-Debug-def getHealthyTarget(): length is " + str(len(response['TargetHealthDescriptions'])) ) print("-Debug-def getHealthyTarget(): target[0] is " + response['TargetHealthDescriptions'][0]['TargetHealth']['State']) # Even if the target becomes unhealthy due to unavoidable circumstances, the corresponding az is in service state. if len(response['TargetHealthDescriptions']) == 1 and response['TargetHealthDescriptions'][0]['TargetHealth']['State'] in ["healthy","unhealthy"]: serviceTarget = response['TargetHealthDescriptions'][0]['Target']['Id'] return serviceTarget def isHealchCheckSucceeded(currentServiceAz): print("-Debug-def isHealchCheckSucceededInServiceAz():") isSucceeded = True if currentServiceAz == "az1": isSucceeded=healchCheck(ip_address_of_host_of_az1, port_of_healthcheck) elif currentServiceAz == "az2": isSucceeded=healchCheck(ip_address_of_host_of_az2, port_of_healthcheck) return isSucceeded def current_service_az(): print("-Debug-def current_service_az():") currentServiceAz = "undecidable" serviceTarget=getServiceTarget() if serviceTarget == InstanceId_Of_RdsRouter_az1 : currentServiceAz = "az1" elif serviceTarget == InstanceId_Of_RdsRouter_az2 : currentServiceAz = "az2" print("-Debug-def current_service_az(): current_service_az() : " + currentServiceAz) return currentServiceAz def healchCheck(host,port): print("-Debug-def healchCheck(host,port):") unHealthyCount = 0 numberOfHealthCheckCount = 3 for i in range(0, numberOfHealthCheckCount): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(1) # 1 Second Timeout connectionResponse = sock.connect_ex((host, port)) if connectionResponse != 0 : unHealthyCount = unHealthyCount + 1 sock.close() time.sleep(1) print("-Debug-def healchCheck(host,port): unHealthyCount : " + str(unHealthyCount) ) if numberOfHealthCheckCount == unHealthyCount: return False else: return True def updateRouteTable(): print("-Debug-def updateRouteTable():") updateRouteTablePerAz("az1") updateRouteTablePerAz("az2") def updateRouteTablePerAz(availabilityZone): print("-Debug-def updateRouteTablePerAz(availabilityZone):") if availabilityZone == "az1": routeTableId = route_table_id_of_az1 elif availabilityZone == "az2": routeTableId = route_table_id_of_az2 if current_service_az() == "az1": updated_InstanceId_Of_route = InstanceId_Of_RdsRouter_az2 if current_service_az() == "az2": updated_InstanceId_Of_route = InstanceId_Of_RdsRouter_az1 ec2 = session.resource('ec2') print ("Update Route table : " + routeTableId + " for CIDR(" + client_destination_cidr_az1 + ") with " + updated_InstanceId_Of_route) route = ec2.Route(routeTableId,client_destination_cidr_az1) response = route.replace(InstanceId=updated_InstanceId_Of_route) print ("Update Route table : " + routeTableId + " for CIDR(" + client_destination_cidr_az2 + ") with " + updated_InstanceId_Of_route) route = ec2.Route(routeTableId,client_destination_cidr_az2) response = route.replace(InstanceId=updated_InstanceId_Of_route) def registerInstanceAsNlbTarget(currentServiceAz): print("-Debug-def registerInstanceAsNlbTarget():") if currentServiceAz == "az1": deregistered_InstanceId= InstanceId_Of_RdsRouter_az1 deregisteredAz = "az1" registered_InstanceId= InstanceId_Of_RdsRouter_az2 registeredAz = "az2" if currentServiceAz == "az2": deregistered_InstanceId= InstanceId_Of_RdsRouter_az2 deregisteredAz = "az2" registered_InstanceId= InstanceId_Of_RdsRouter_az1 registeredAz = "az1" client = session.client('elbv2') print("-Debug-def registerInstanceAsNlbTarget(): deregister old target : " + deregistered_InstanceId + " in az (" + deregisteredAz + ")" ) response = client.deregister_targets( TargetGroupArn= TargetGroupArn_of_Nlb, Targets=[ { 'Id': deregistered_InstanceId, 'Port': port_of_service }, ] ) print("-Debug-def registerInstanceAsNlbTarget(): register new target : " + registered_InstanceId + " in az (" + registeredAz + ")" ) response = client.register_targets( TargetGroupArn= TargetGroupArn_of_Nlb, Targets=[ { 'Id': registered_InstanceId, 'Port': port_of_service }, ] ) ################# Output Sections ############### Outputs: BastionClientAz1: Value: !Sub "ssh -A ec2-user@${BastionClientAz1.PublicIp} ## BastionClientAz1 ${BastionClientAz1.PrivateIp}" BastionClientAz2: Value: !Sub "ssh -A ec2-user@${BastionClientAz2.PublicIp} ## BastionClientAz2 ${BastionClientAz2.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}"