AWSTemplateFormatVersion: '2010-09-09'
Description: >
  3-Tier Architecture with DB Connection Health Check Script
  ALB + ASG + RDS MySQL Multi-AZ + Bastion + Dual NAT HA
  Web Server checks RDS connection status every 5 seconds via PHP.

Parameters:
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: EC2 접속을 위한 기존 키 페어 이름을 입력하세요.

  DBMasterUsername:
    Type: String
    Default: admin

  DBMasterUserPassword:
    Type: String
    NoEcho: true
    MinLength: 8
    Description: DB 마스터 비밀번호 (최소 8자)

  LatestAmiId:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64'

Resources:

# ---------------- VPC & Networking ----------------
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true

  IGW:
    Type: AWS::EC2::InternetGateway

  AttachIGW:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref IGW

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select [0, !GetAZs '']

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select [1, !GetAZs '']

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.11.0/24
      AvailabilityZone: !Select [0, !GetAZs '']

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.12.0/24
      AvailabilityZone: !Select [1, !GetAZs '']

  DBSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.21.0/24
      AvailabilityZone: !Select [0, !GetAZs '']

  DBSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.22.0/24
      AvailabilityZone: !Select [1, !GetAZs '']

# ---------------- NAT Gateways (High Availability) ----------------
  EIP1:
    Type: AWS::EC2::EIP
    DependsOn: AttachIGW
    Properties:
      Domain: vpc

  EIP2:
    Type: AWS::EC2::EIP
    DependsOn: AttachIGW
    Properties:
      Domain: vpc

  NAT1:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIP1.AllocationId
      SubnetId: !Ref PublicSubnet1

  NAT2:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIP2.AllocationId
      SubnetId: !Ref PublicSubnet2

# ---------------- Routing ----------------
  PublicRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRT
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW

  PubAssoc1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRT

  PubAssoc2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRT

  PrivateRT1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  PrivateRoute1:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRT1
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NAT1

  PriAssoc1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRT1

  PrivateRT2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  PrivateRoute2:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRT2
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NAT2

  PriAssoc2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRT2

# ---------------- Security Groups ----------------
  ALBSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Allow HTTP from everywhere
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

  WebSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Allow HTTP from ALB
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref ALBSG

  BastionSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Allow SSH from everywhere
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0

  DBSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Allow MySQL from Web and Bastion
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref WebSG
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref BastionSG

# ---------------- Load Balancer ----------------
  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Subnets: [!Ref PublicSubnet1, !Ref PublicSubnet2]
      SecurityGroups: [!Ref ALBSG]

  TG:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VPC
      TargetType: instance
      HealthCheckPath: /index.php
      HealthCheckIntervalSeconds: 30

  Listener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref TG

# ---------------- Launch Template & Auto Scaling ----------------
  LT:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: !Ref LatestAmiId
        InstanceType: t3.micro
        SecurityGroupIds: [!Ref WebSG]
        # 실시간 DB 체크 로직이 포함된 UserData
        UserData:
          Fn::Base64: !Sub |
            #!/bin/bash
            dnf update -y
            dnf install -y httpd php php-mysqli mariadb105
            systemctl enable httpd
            systemctl start httpd

            cat <<EOF > /var/www/html/index.php
            <!DOCTYPE html>
            <html>
            <head>
                <title>RDS Connectivity Monitor</title>
                <meta http-equiv="refresh" content="5">
                <style>
                    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; text-align: center; padding: 50px; background-color: #f4f7f6; }
                    .container { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); display: inline-block; min-width: 400px; }
                    .status { font-weight: bold; padding: 15px; border-radius: 5px; margin: 20px 0; font-size: 1.2em; }
                    .ok { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
                    .fail { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
                    .info { color: #666; font-size: 0.9em; text-align: left; background: #eee; padding: 10px; border-radius: 5px; }
                </style>
            </head>
            <body>
                <div class="container">
                    <h1>Cloud Infrastructure Status</h1>
                    <p><strong>Web Server:</strong> <?php echo gethostname(); ?></p>
                    <p><strong>Last Checked:</strong> <?php echo date('H:i:s'); ?> (Every 5s)</p>
                    
                    <?php
                    \$host = '${RDSInstance.Endpoint.Address}';
                    \$user = '${DBMasterUsername}';
                    \$pass = '${DBMasterUserPassword}';
                    
                    mysqli_report(MYSQLI_REPORT_OFF);
                    \$conn = mysqli_init();
                    mysqli_options(\$conn, MYSQLI_OPT_CONNECT_TIMEOUT, 3);
                    
                    if (@mysqli_real_connect(\$conn, \$host, \$user, \$pass)) {
                        echo "<div class='status ok'>● DATABASE CONNECTED</div>";
                        mysqli_close(\$conn);
                    } else {
                        echo "<div class='status fail'>○ DATABASE DISCONNECTED</div>";
                        echo "<p style='color:red'>Reason: " . mysqli_connect_error() . "</p>";
                    }
                    ?>

                    <div class="info">
                        <strong>Target Endpoint:</strong><br>
                        <?php echo \$host; ?>
                    </div>
                </div>
            </body>
            </html>
            EOF

  ASG:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier: [!Ref PrivateSubnet1, !Ref PrivateSubnet2]
      LaunchTemplate:
        LaunchTemplateId: !Ref LT
        Version: !GetAtt LT.LatestVersionNumber
      MinSize: 2
      MaxSize: 4
      DesiredCapacity: 2
      TargetGroupARNs: [!Ref TG]

# ---------------- Bastion & RDS ----------------
  Bastion:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.micro
      KeyName: !Ref KeyName
      ImageId: !Ref LatestAmiId
      SubnetId: !Ref PublicSubnet1
      SecurityGroupIds: [!Ref BastionSG]
      Tags:
        - Key: Name
          Value: Bastion-Host

  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: RDS Multi-AZ Subnet Group
      SubnetIds: [!Ref DBSubnet1, !Ref DBSubnet2]

  RDSInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Snapshot
    Properties:
      DBInstanceClass: db.t3.micro
      Engine: mysql
      EngineVersion: "8.0"
      MasterUsername: !Ref DBMasterUsername
      MasterUserPassword: !Ref DBMasterUserPassword
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups: [!Ref DBSG]
      AllocatedStorage: "20"
      StorageType: gp2
      MultiAZ: true
      PubliclyAccessible: false
      BackupRetentionPeriod: 0
      DeletionProtection: false

# ---------------- Outputs ----------------
Outputs:
  WebUrl:
    Description: 접속하여 DB 상태를 확인하세요.
    Value: !Sub "http://${ALB.DNSName}"

  BastionPublicIP:
    Description: Bastion 호스트 IP
    Value: !GetAtt Bastion.PublicIp

  RDSAddress:
    Description: RDS 엔드포인트 주소
    Value: !GetAtt RDSInstance.Endpoint.Address