## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
## SPDX-License-Identifier: MIT-0

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Network Load Balancer, VPC Endpoint and auxiliary functions


###############################################################################
# Parameters 
###############################################################################  

Parameters:

  pVpcId:
    Description: VPC where the resources will be created.
    Type: AWS::EC2::VPC::Id
  pPrivateSubnetIds:
    Description: Subnets where the NLB will be launched.
    Type: List<AWS::EC2::Subnet::Id>
  pCIDRRange:
    Description: IP address range that is allowed to connect to the API.
    Type: String
    MinLength: '9'
    MaxLength: '18'
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x


###############################################################################
# Resources 
###############################################################################  

Resources:

################ NETWORK LOAD BALANCER (Necessary for VPC Link for REST API) ##########################

  rNetworkLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internal 
      Type: network
      Subnets: !Ref pPrivateSubnetIds
      Tags: 
        - Key: Name
          Value: !Sub '${AWS::StackName} - NLB for API Gateway VPC Link'

################ VPC ENDPOINT FOR API GATEWAY ##########################

  rVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties: 
      PrivateDnsEnabled: true
      SecurityGroupIds: 
        - !GetAtt rVPCEndpointSecurityGroup.GroupId
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.execute-api'
      SubnetIds: !Ref pPrivateSubnetIds
      VpcEndpointType: "Interface"
      VpcId: !Ref pVpcId

################ SECURITY GROUP FOR VPC ENDPOINT ##########################

  rVPCEndpointSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Allow HTTPS inbound traffic to API Gateway Endpoint.
      VpcId: !Ref pVpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
          CidrIp: !Ref pCIDRRange # ["10.0.0.0/8"] # This should include not only the VPC CIDR but also the on-prem network
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName} - Security group allowing HTTPS inbound traffic to API Gateway Endpoint'

################################################################################################

# Auxiliary Lambda function to get the NLB IP addresses

  rLambdaFunctionNLBIPAddresses:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: AuxLambdaFunctionNLB/
      Description: Look up info from NLB
      Handler: index.handler
      MemorySize: 128
      Runtime: python3.6
      Timeout: 30
      Tags:
        Name: !Sub '${AWS::StackName} - Function to get additional data from NLB'  
      Policies:
        - AWSLambdaBasicExecutionRole
        - Version: '2012-10-17' 
          Statement:
            - Effect: Allow
              Action:
              - ec2:DescribeVpcs
              - ec2:DescribeNetworkInterfaces
              Resource: "*"

  rNlbInfo:
    Type: Custom::NlbInfo
    DependsOn:
      - rNetworkLoadBalancer
    Properties:
      ServiceToken: !GetAtt rLambdaFunctionNLBIPAddresses.Arn
      NameFilter: !GetAtt rNetworkLoadBalancer.LoadBalancerFullName

######## Auxiliary Lambda function to create custom security group with source private IP addresses from NLB.
# It has to be custom, as we don't know how many addresses the NLB will have (one per subnet provided)

  rLambdaFunctionSecurityGroup:
    Type: AWS::Serverless::Function
    DependsOn:
      - rNlbInfo
    Properties:
      CodeUri: AuxLambdaFunctionSecGrp/
      Description: Create custom security group with NLB IP addresses as source
      Handler: index.lambda_handler
      MemorySize: 128
      Runtime: python3.6
      Timeout: 30
      Tags:
        Name: !Sub '${AWS::StackName} - Function to create custom security group'
      Environment: 
        Variables: 
          WhiteList: !GetAtt rNlbInfo.IpAddresses
          VpcId: !Ref pVpcId
          StackName: !Ref 'AWS::StackName'
      Policies:
        - AWSLambdaBasicExecutionRole
        - Version: '2012-10-17' 
          Statement:
            - Effect: Allow
              Action:
                - 'ec2:CreateSecurityGroup'
                - 'ec2:AuthorizeSecurityGroupIngress'
                - 'ec2:DeleteSecurityGroup'
              Resource: '*'                  

  rCustomSG:
    Type: "Custom::CustomSG"
    Properties:
      ServiceToken: !GetAtt rLambdaFunctionSecurityGroup.Arn
      Ports: 80
      GroupName: MyCustomSecGroup
      GroupDescription: Allows connections to port 80 from NLB private IP addresses
      

#####################################################
################## OUTPUTS ##########################
#####################################################

Outputs:

  oNetworkLoadBalancerArn:
    Description: Network Load Balancer ARN
    Value: !Ref rNetworkLoadBalancer
    Export:
      Name: !Sub '${AWS::StackName}-NetworkLoadBalancerArn'

  oNetworkLoadBalancerDNSName:
    Description: Network Load Balancer DNS Name
    Value: !GetAtt rNetworkLoadBalancer.DNSName
    Export:
      Name: !Sub '${AWS::StackName}-NetworkLoadBalancerDNSName'

  oVpcEndpointId:
    Description: VPC Endpoint Id
    Value: !Ref rVPCEndpoint
    Export:
      Name: !Sub '${AWS::StackName}-VPCEndpoint'

  oCustomSecurityGroup:
    Value: !GetAtt rCustomSG.SecGroupId
    Export:
      Name: !Sub '${AWS::StackName}-CustomSecurityGroup'