AWSTemplateFormatVersion: 2010-09-09
Description: Dual AZ NAT Instances
Parameters:
  VPCCode:
    Type: String
    Description: Code for VPC
  VPC:
    Type: AWS::EC2::VPC::Id
    Description: VPC for deployment
  PublicSubnetA:
    Type: AWS::EC2::Subnet::Id
    Description: Public subnet in Availability Zone 1
  PublicSubnetB:
    Type: AWS::EC2::Subnet::Id
    Description: Public subnet in Availability Zone 2

  LatestAmiId:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-arm64-gp2'
    Description: AMI to use for EC2 instances

  Ec2InstanceType:
    Type: String
    Default: c7g.medium
    Description: Instance type to use for EC2 Instances

  NotifySns:
    Type: String
    Description: SNS Topic to send notifications
  ElasticIpMap:
    Type: String
    Description: Elastic IP Mapping

Resources:
  Ec2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Join ['-', [nat, !Ref VPCCode]]
      GroupDescription: !Join ['-', [nat, !Ref VPCCode]]
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

  Ec2InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Join ['-', [nat, !Ref VPCCode, ec2]]
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/AmazonSSMManagedEC2InstanceDefaultPolicy

  Ec2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Join ['-', [nat, !Ref VPCCode]]
      Roles:
        - !Ref Ec2InstanceRole

  Ec2Launch:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Join ['-', [nat, !Ref VPCCode]]
      LaunchTemplateData:
        ImageId: !Ref LatestAmiId
        InstanceType: !Ref Ec2InstanceType
        IamInstanceProfile:
          Arn: !GetAtt Ec2InstanceProfile.Arn
        NetworkInterfaces:
          - DeviceIndex: 0
            AssociatePublicIpAddress: true
            Groups:
              - !GetAtt Ec2SecurityGroup.GroupId
        PrivateDnsNameOptions:
          EnableResourceNameDnsAAAARecord: false
          EnableResourceNameDnsARecord: false
        UserData:
          Fn::Base64: !Sub |
            #!/bin/bash -xe
            iptables -A INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
            iptables -A INPUT -i eth0 -j DROP
            iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
            sysctl net.ipv4.ip_forward=1

  Ec2ScalingGroupA:
    Type: AWS::AutoScaling::AutoScalingGroup
    DependsOn:
      - Lambda
      - EventBridgeRule
    Properties:
      AutoScalingGroupName: !Join ['-', [nat, !Ref VPCCode, a]]
      HealthCheckGracePeriod: 60
      LaunchTemplate:
        LaunchTemplateId: !Ref Ec2Launch
        Version: !GetAtt Ec2Launch.LatestVersionNumber
      VPCZoneIdentifier:
        - !Ref PublicSubnetA
      MaxSize: 1
      MinSize: 1
      DesiredCapacity: 1
      Tags:
        - Key: Name
          Value: !Join ['-', [nat, !Ref VPCCode, a]]
          PropagateAtLaunch: "true"
        - Key: teaglu:app
          Value: nat
          PropagateAtLaunch: "true"

  Ec2ScalingGroupB:
    Type: AWS::AutoScaling::AutoScalingGroup
    DependsOn:
      - Lambda
      - EventBridgeRule
    Properties:
      AutoScalingGroupName: !Join ['-', [nat, !Ref VPCCode, b]]
      HealthCheckGracePeriod: 60
      LaunchTemplate:
        LaunchTemplateId: !Ref Ec2Launch
        Version: !GetAtt Ec2Launch.LatestVersionNumber
      VPCZoneIdentifier:
        - !Ref PublicSubnetB
      MaxSize: 1
      MinSize: 1
      DesiredCapacity: 1
      Tags:
        - Key: Name
          Value: !Join ['-', [nat, !Ref VPCCode, b]]
          PropagateAtLaunch: "true"
        - Key: teaglu:app
          Value: nat
          PropagateAtLaunch: "true"

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Join ['-', [nat, !Ref VPCCode, lambda]]
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
            - sts:AssumeRole
      Path: "/"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess
      Policies:
        - PolicyName: LoggingPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: "*"
        - PolicyName: UpdatePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - ec2:CreateRoute
                  - ec2:DeleteRoute
                  - ec2:ModifyNetworkInterfaceAttribute
                  - ec2:AssociateAddress
                Resource: "*"
        - PolicyName: NotifyPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - sns:Publish
                Resource: "*"

  Lambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Join ['-', [nat, !Ref VPCCode, updater]]
      Description: !Sub "Update Route Tables for ${VPCCode} NAT Instances"
      Handler: index.handler
      Architectures: 
        - arm64
      Runtime: nodejs18.x
      Role: !GetAtt LambdaRole.Arn
      Environment:
        Variables:
          EIPMAP: !Ref ElasticIpMap
          NOTIFY_TOPIC: !Ref NotifySns
          REGION: !Ref "AWS::Region"
          VPC: !Ref VPC
      MemorySize: 128
      ReservedConcurrentExecutions: 1
      Timeout: 20
      Code:
        S3Bucket: teaglu-lambda
        S3Key: nat-updater.zip

  EventBridgeRule:
    Type: AWS::Events::Rule
    Properties:
      Name: !Join ['-', [nat, !Ref VPCCode, instance, change ]]
      Description: !Sub "Update Route Tables for ${VPCCode} NAT Instances"
      EventBusName: default
      EventPattern:
        source:
          - aws.ec2
        detail-type:
          - EC2 Instance State-change Notification
      State: ENABLED
      Targets:
        - Arn: !GetAtt Lambda.Arn
          Id: 'execute-route-lambda'

  EventBridgeLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt Lambda.Arn
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt EventBridgeRule.Arn

