AWSTemplateFormatVersion: '2010-09-09'
Description: >-
  Pelotech AL2023 NAT Instance (ARM64/Graviton) - CIS Level 2 hardened, FIPS 140-3 compliant.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Instance Configuration
        Parameters:
          - InstanceType
          - AmiId
          - KeyPairName
      - Label:
          default: Network Configuration
        Parameters:
          - VpcId
          - VpcCidr
          - SubnetId
          - AssociateElasticIP
          - RouteTableId1
          - RouteTableId2
          - RouteTableId3
          - RouteTableId4
          - RouteTableId5
          - RouteTableId6
      - Label:
          default: Monitoring
        Parameters:
          - EnableCloudWatch
          - CloudWatchConfigParam
      - Label:
          default: Tuning
        Parameters:
          - ENIId
          - ConntrackMax
          - LocalPortRange
    ParameterLabels:
      AmiId:
        default: AMI ID
      InstanceType:
        default: Instance Type
      KeyPairName:
        default: EC2 Key Pair (optional)
      VpcId:
        default: VPC
      VpcCidr:
        default: VPC CIDR Block
      SubnetId:
        default: Public Subnet
      AssociateElasticIP:
        default: Associate Elastic IP
      RouteTableId1:
        default: Route Table ID 1 (optional)
      RouteTableId2:
        default: Route Table ID 2 (optional)
      RouteTableId3:
        default: Route Table ID 3 (optional)
      RouteTableId4:
        default: Route Table ID 4 (optional)
      RouteTableId5:
        default: Route Table ID 5 (optional)
      RouteTableId6:
        default: Route Table ID 6 (optional)
      EnableCloudWatch:
        default: Enable CloudWatch Agent
      CloudWatchConfigParam:
        default: CloudWatch Config SSM Parameter
      ENIId:
        default: Secondary ENI ID (optional)
      ConntrackMax:
        default: nf_conntrack_max (0 = kernel default)
      LocalPortRange:
        default: ip_local_port_range (optional)

Parameters:
  InstanceType:
    Type: String
    Default: t4g.micro
    Description: EC2 instance type (ARM64/Graviton).
    AllowedValues:
      - t4g.nano
      - t4g.micro
      - t4g.small
      - t4g.medium
      - c6gn.medium
      - c6gn.large
      - c6gn.xlarge
      - c7gn.medium
      - c7gn.large
      - c7gn.xlarge
      - c8gn.medium
      - c8gn.large
      - c8gn.xlarge
      - c8gn.2xlarge
      - c8gn.4xlarge
      - c8gn.8xlarge
      - m7g.medium
      - m7g.large
      - m7g.xlarge
  VpcId:
    Type: AWS::EC2::VPC::Id
    Description: VPC to deploy the NAT instance into.
  SubnetId:
    Type: AWS::EC2::Subnet::Id
    Description: Public subnet for the NAT instance.
  AssociateElasticIP:
    Type: String
    Default: 'true'
    AllowedValues:
      - 'true'
      - 'false'
    Description: Allocate and associate an Elastic IP address.
  VpcCidr:
    Type: String
    Description: VPC CIDR block (e.g. 10.0.0.0/16). Used for security group ingress.
  RouteTableId1:
    Type: String
    Default: ''
    Description: Route table ID to add a default route through the NAT instance. Leave empty to skip.
  RouteTableId2:
    Type: String
    Default: ''
    Description: Additional route table ID. Leave empty to skip.
  RouteTableId3:
    Type: String
    Default: ''
    Description: Additional route table ID. Leave empty to skip.
  RouteTableId4:
    Type: String
    Default: ''
    Description: Additional route table ID. Leave empty to skip.
  RouteTableId5:
    Type: String
    Default: ''
    Description: Additional route table ID. Leave empty to skip.
  RouteTableId6:
    Type: String
    Default: ''
    Description: Additional route table ID. Leave empty to skip.
  EnableCloudWatch:
    Type: String
    Default: 'false'
    AllowedValues:
      - 'true'
      - 'false'
    Description: Enable the CloudWatch agent for monitoring.
  CloudWatchConfigParam:
    Type: String
    Default: ''
    Description: SSM Parameter name containing CloudWatch agent config. Required if CloudWatch is enabled.
  KeyPairName:
    Type: String
    Default: ''
    Description: EC2 key pair for SSH access. Leave empty to disable SSH key-based access.
  ENIId:
    Type: String
    Default: ''
    Description: Secondary ENI ID for dual-ENI setups. Leave empty to disable.
  ConntrackMax:
    Type: Number
    Default: 0
    Description: nf_conntrack_max sysctl value. 0 uses the kernel default.
  LocalPortRange:
    Type: String
    Default: ''
    Description: ip_local_port_range sysctl value (e.g. "1024 65535"). Leave empty for kernel default.
  AmiId:
    Type: AWS::EC2::Image::Id
    Description: >-
      AMI ID for the NAT instance. AWS Marketplace populates this
      automatically per region during distribution.

Conditions:
  CreateEIP: !Equals [!Ref AssociateElasticIP, 'true']
  EnableCW: !And
    - !Equals [!Ref EnableCloudWatch, 'true']
    - !Not [!Equals [!Ref CloudWatchConfigParam, '']]
  HasKeyPair: !Not [!Equals [!Ref KeyPairName, '']]
  HasENI: !Not [!Equals [!Ref ENIId, '']]
  HasRouteTable1: !Not [!Equals [!Ref RouteTableId1, '']]
  HasRouteTable2: !Not [!Equals [!Ref RouteTableId2, '']]
  HasRouteTable3: !Not [!Equals [!Ref RouteTableId3, '']]
  HasRouteTable4: !Not [!Equals [!Ref RouteTableId4, '']]
  HasRouteTable5: !Not [!Equals [!Ref RouteTableId5, '']]
  HasRouteTable6: !Not [!Equals [!Ref RouteTableId6, '']]
  HasAnyRouteTableAndNoENI: !And
    - !Or
      - !Not [!Equals [!Ref RouteTableId1, '']]
      - !Not [!Equals [!Ref RouteTableId2, '']]
      - !Not [!Equals [!Ref RouteTableId3, '']]
      - !Not [!Equals [!Ref RouteTableId4, '']]
      - !Not [!Equals [!Ref RouteTableId5, '']]
      - !Not [!Equals [!Ref RouteTableId6, '']]
    - !Equals [!Ref ENIId, '']

Resources:
  # --- Security Group ---
  NATSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Pelotech NAT Instance - allows all outbound traffic
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: '-1'
          CidrIp: !Ref VpcCidr
          Description: Allow all traffic from VPC
      SecurityGroupEgress:
        - IpProtocol: '-1'
          CidrIp: 0.0.0.0/0
          Description: Allow all outbound IPv4
        - IpProtocol: '-1'
          CidrIpv6: ::/0
          Description: Allow all outbound IPv6
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-nat-sg'

  # --- IAM Role for NAT Instance ---
  NATRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore'
      Policies:
        - PolicyName: NATInstancePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Sid: ModifyNetworking
                Effect: Allow
                Action:
                  - ec2:ModifyNetworkInterfaceAttribute
                  - ec2:AssociateAddress
                  - ec2:AttachNetworkInterface
                Resource: '*'
              - Sid: SSMGetParameter
                Effect: Allow
                Action: ssm:GetParameter
                Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*'
        - !If
          - EnableCW
          - PolicyName: CloudWatchAgentPolicy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action:
                    - cloudwatch:PutMetricData
                    - logs:CreateLogGroup
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                    - logs:DescribeLogStreams
                  Resource: '*'
          - !Ref 'AWS::NoValue'

  NATInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref NATRole

  # --- Elastic IP (conditional) ---
  NATEIP:
    Type: AWS::EC2::EIP
    Condition: CreateEIP
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-nat-eip'

  # --- Managed ENI (conditional) ---
  NATNetworkInterface:
    Type: AWS::EC2::NetworkInterface
    Condition: HasAnyRouteTableAndNoENI
    Properties:
      SubnetId: !Ref SubnetId
      GroupSet:
        - !Ref NATSecurityGroup
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-nat-eni'

  # --- Routes (conditional, up to 6) ---
  NATRoute1:
    Type: AWS::EC2::Route
    Condition: HasRouteTable1
    Properties:
      RouteTableId: !Ref RouteTableId1
      DestinationCidrBlock: 0.0.0.0/0
      NetworkInterfaceId: !If [HasENI, !Ref ENIId, !Ref NATNetworkInterface]
  NATRoute2:
    Type: AWS::EC2::Route
    Condition: HasRouteTable2
    Properties:
      RouteTableId: !Ref RouteTableId2
      DestinationCidrBlock: 0.0.0.0/0
      NetworkInterfaceId: !If [HasENI, !Ref ENIId, !Ref NATNetworkInterface]
  NATRoute3:
    Type: AWS::EC2::Route
    Condition: HasRouteTable3
    Properties:
      RouteTableId: !Ref RouteTableId3
      DestinationCidrBlock: 0.0.0.0/0
      NetworkInterfaceId: !If [HasENI, !Ref ENIId, !Ref NATNetworkInterface]
  NATRoute4:
    Type: AWS::EC2::Route
    Condition: HasRouteTable4
    Properties:
      RouteTableId: !Ref RouteTableId4
      DestinationCidrBlock: 0.0.0.0/0
      NetworkInterfaceId: !If [HasENI, !Ref ENIId, !Ref NATNetworkInterface]
  NATRoute5:
    Type: AWS::EC2::Route
    Condition: HasRouteTable5
    Properties:
      RouteTableId: !Ref RouteTableId5
      DestinationCidrBlock: 0.0.0.0/0
      NetworkInterfaceId: !If [HasENI, !Ref ENIId, !Ref NATNetworkInterface]
  NATRoute6:
    Type: AWS::EC2::Route
    Condition: HasRouteTable6
    Properties:
      RouteTableId: !Ref RouteTableId6
      DestinationCidrBlock: 0.0.0.0/0
      NetworkInterfaceId: !If [HasENI, !Ref ENIId, !Ref NATNetworkInterface]

  # --- Launch Template ---
  NATLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: !Ref AmiId
        InstanceType: !Ref InstanceType
        KeyName: !If [HasKeyPair, !Ref KeyPairName, !Ref 'AWS::NoValue']
        MetadataOptions:
          HttpTokens: required
          HttpPutResponseHopLimit: 1
          HttpEndpoint: enabled
        IamInstanceProfile:
          Arn: !GetAtt NATInstanceProfile.Arn
        NetworkInterfaces:
          - DeviceIndex: 0
            AssociatePublicIpAddress: true
            Groups:
              - !Ref NATSecurityGroup
        BlockDeviceMappings:
          - DeviceName: /dev/xvda
            Ebs:
              VolumeSize: 5
              VolumeType: gp3
              Encrypted: true
        UserData:
          Fn::Base64: !Sub
            - |
              #!/bin/bash
              cat > /etc/pelotech-nat.conf <<'CONF'
              eip_id=${EipAllocationId}
              eni_id=${EniId}
              cwagent_enabled=${CWEnabled}
              cwagent_cfg_param_name=${CWParam}
              nf_conntrack_max=${ConntrackMaxVal}
              ip_local_port_range=${PortRange}
              CONF
            - EipAllocationId: !If [CreateEIP, !GetAtt NATEIP.AllocationId, '']
              EniId: !If
                - HasENI
                - !Ref ENIId
                - !If
                  - HasAnyRouteTableAndNoENI
                  - !Ref NATNetworkInterface
                  - ''
              CWEnabled: !If [EnableCW, 'true', '']
              CWParam: !If [EnableCW, !Ref CloudWatchConfigParam, '']
              ConntrackMaxVal: !Ref ConntrackMax
              PortRange: !Ref LocalPortRange

  # --- Auto Scaling Group ---
  NATASG:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      MinSize: 1
      MaxSize: 1
      DesiredCapacity: 1
      VPCZoneIdentifier:
        - !Ref SubnetId
      LaunchTemplate:
        LaunchTemplateId: !Ref NATLaunchTemplate
        Version: !GetAtt NATLaunchTemplate.LatestVersionNumber
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-nat'
          PropagateAtLaunch: true
        - Key: Product
          Value: pelotech-nat
          PropagateAtLaunch: true

Outputs:
  ElasticIP:
    Condition: CreateEIP
    Description: Elastic IP address
    Value: !Ref NATEIP
  SecurityGroupId:
    Description: Security group ID for the NAT instance
    Value: !Ref NATSecurityGroup
  Architecture:
    Description: CPU architecture for this template
    Value: arm64
  AutoScalingGroupName:
    Description: Name of the Auto Scaling Group managing the NAT instance
    Value: !Ref NATASG
