AWSTemplateFormatVersion: '2010-09-09'
Description: >
  aws-enforza-boilerplate-single-az-landing-zone:
  Creates a VPC, public/private subnets, security groups, EC2 instance, and associated route tables with custom CIDR and session inputs.

Parameters:
  enforzaCfnVersion:
    Type: String
    Default: 2024.09.24/release/1.2
    Description: aws-enforza-boilerplate-single-az-landing-zone version

  VPCCidr:
    Type: String
    Default: 10.1.0.0/16
    Description: Enter the VPC CIDR (Must be a valid /16 CIDR block)
    AllowedPattern: ^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/16)$
    ConstraintDescription: Must be a valid /16 IPv4 CIDR block.

  TrustedManagementIPs:
    Type: CommaDelimitedList
    Default: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
    Description: Enter trusted management IPs or ranges (e.g., "192.168.1.0/24, 203.0.113.0/32"). These will be allowed SSH, ICMP, and SNMP access.

  LatestAmiId:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/canonical/ubuntu/server/jammy/stable/current/amd64/hvm/ebs-gp2/ami-id'
    Description: SSM AMI image for the Enforza Gateway (if in doubt, leave it alone)

Resources:
  # VPC Creation
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidr
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: enforza-vpc

  # Internet Gateway
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: enforza-igw

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # Subnets calculation using Fn::Cidr
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [0, !Cidr [!Ref VPCCidr, 4, 8]]  # First /24 from the VPC CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: enforza-public-subnet

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [1, !Cidr [!Ref VPCCidr, 4, 8]]  # Second /24 from the VPC CIDR
      Tags:
        - Key: Name
          Value: enforza-private-subnet-1

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [2, !Cidr [!Ref VPCCidr, 4, 8]]  # Third /24 from the VPC CIDR
      Tags:
        - Key: Name
          Value: enforza-private-subnet-2

  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [3, !Cidr [!Ref VPCCidr, 4, 8]]  # Fourth /24 from the VPC CIDR
      Tags:
        - Key: Name
          Value: enforza-private-subnet-3

  # Public Route Table
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: enforza-public-route-table

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

  # Private Route Table
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: enforza-private-route-table

  PrivateSubnetRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetRouteTableAssociation3:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet3
      RouteTableId: !Ref PrivateRouteTable
  # Private Route Table
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: enforza-private-route-table

  # Routes for the Private Route Table
  PrivateRouteToInternet:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0  # Route all traffic to the EC2 ENI
      NetworkInterfaceId: !Ref EC2NetworkInterface

  # Route table to direct traffic from private subnets to EC2 ENI
  PrivateRouteForPrivateSubnet1:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: !GetAtt PrivateSubnet1.CidrBlock  # Reusing the calculated CIDR block
      NetworkInterfaceId: !Ref EC2NetworkInterface

  PrivateRouteForPrivateSubnet2:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: !GetAtt PrivateSubnet2.CidrBlock  # Reusing the calculated CIDR block
      NetworkInterfaceId: !Ref EC2NetworkInterface

  PrivateRouteForPrivateSubnet3:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: !GetAtt PrivateSubnet3.CidrBlock  # Reusing the calculated CIDR block
      NetworkInterfaceId: !Ref EC2NetworkInterface

  # Security Group with VPC CIDR Permit and Trusted IPs for SSH, ICMP, SNMP
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable traffic from trusted IPs and VPC
      VpcId: !Ref VPC
      SecurityGroupIngress:
        # Allow All Traffic from VPC CIDR Range
        - IpProtocol: -1
          CidrIp: !Ref VPCCidr
        # Allow SSH, ICMP, SNMP from Trusted Management IPs
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: !Select [0, !Ref TrustedManagementIPs]
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: !Select [0, !Ref TrustedManagementIPs]
        - IpProtocol: udp
          FromPort: '161'
          ToPort: '161'
          CidrIp: !Select [0, !Ref TrustedManagementIPs]
      Tags:
        - Key: Name
          Value: enforza-sg

  # Create ENI for EC2 Instance
  EC2NetworkInterface:
    Type: AWS::EC2::NetworkInterface
    Properties:
      SubnetId: !Ref PublicSubnet
      GroupSet:
        - !Ref InstanceSecurityGroup
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: enforza-eni

  EC2InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: AllowTagEC2Instance
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - ec2:CreateTags
                Resource:
                  - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/*"

  # Attach the role to an EC2 Instance Profile
  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2InstanceRole

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.micro
      ImageId: !Ref LatestAmiId
      IamInstanceProfile: !Ref EC2InstanceProfile
      NetworkInterfaces:
        - NetworkInterfaceId: !Ref EC2NetworkInterface
          DeviceIndex: '0'
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          set -x  # Enable shell debugging

          exec > >(tee /var/log/enforza-cfn-install.log|logger -t user-data -s 2>/dev/console) 2>&1

          # Install AWS CLI and jq
          sudo apt-get update
          sudo apt-get install -y unzip curl jq || echo "Error installing jq and curl" >> /var/log/enforza-cfn-install.log

          # Download and install AWS CLI
          curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" || echo "Error downloading AWS CLI" >> /var/log/enforza-cfn-install.log
          unzip awscliv2.zip
          sudo ./aws/install || echo "Error installing AWS CLI" >> /var/log/enforza-cfn-install.log
          aws --version || echo "AWS CLI version check failed" >> /var/log/enforza-cfn-install.log
          jq --version || echo "jq version check failed" >> /var/log/enforza-cfn-install.log

          # Fetch instance metadata token and instance ID
          TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
          INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)

          # Log the instance ID
          echo "Instance ID: $INSTANCE_ID" >> /var/log/enforza-cfn-install.log

          # Add efzadmin user
          sudo useradd -m -s /bin/bash efzadmin
          echo "efzadmin:$INSTANCE_ID" | sudo chpasswd
          sudo usermod -aG sudo efzadmin

          # Modify the sudoers file to allow passwordless sudo for efzadmin
          echo 'efzadmin ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/efzadmin
          sudo chmod 440 /etc/sudoers.d/efzadmin
          echo "Passwordless sudo enabled for efzadmin" >> /var/log/enforza-cfn-install.log

          # Run the Enforza install script
          curl -s -L https://efz.io/install | sudo bash || echo "Error running Enforza install script" >> /var/log/enforza-cfn-install.log

          # Wait for the Enforza installation
          while [ ! -f /opt/enforza/enforza.info ]; do
              echo "Waiting for /opt/enforza/enforza.info" >> /var/log/enforza-cfn-install.log
              sleep 10
          done

          CLAIM_KEY=$(jq -r '.claimKey0' /opt/enforza/enforza.info)

          echo "Claim Key: $CLAIM_KEY" >> /var/log/enforza-cfn-install.log

          # Tag the EC2 instance with the Claim Key
          aws ec2 create-tags \
            --resources $INSTANCE_ID \
            --tags Key=EnforzaClaimKey,Value=$CLAIM_KEY \
                  Key=EnforzaSerialConsoleUsername,Value=efzadmin \
                  Key=EnforzaSerialConsolePassword,Value=$INSTANCE_ID || echo "Error tagging instance with additional keys" >> /var/log/enforza-cfn-install.log
      Tags:
        - Key: Name
          Value: enforza-firewall-instance

Outputs:
  VPCId:
    Description: The ID of the VPC
    Value: !Ref VPC
  PublicSubnetId:
    Description: The ID of the public subnet
    Value: !Ref PublicSubnet
  PrivateSubnet1Id:
    Description: The ID of the first private subnet
    Value: !Ref PrivateSubnet1
  PrivateSubnet2Id:
    Description: The ID of the second private subnet
    Value: !Ref PrivateSubnet2
  PrivateSubnet3Id:
    Description: The ID of the third private subnet
    Value: !Ref PrivateSubnet3
  EC2InstanceId:
    Description: The ID of the EC2 instance
    Value: !Ref EC2Instance