---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS Sample VPC - Private only subnets. These subnets do not have access to the Internet and this templates creates the recommended VPC Endpoints for running EKS Clusters.'

Parameters:

  VpcBlock:
    Type: String
    Default: 192.168.0.0/16
    Description: The CIDR range for the VPC. This should be a valid private (RFC 1918) CIDR range.

  PrivateSubnet01Block:
    Type: String
    Default: 192.168.0.0/18
    Description: CidrBlock for private subnet 01 within the VPC

  PrivateSubnet02Block:
    Type: String
    Default: 192.168.64.0/18
    Description: CidrBlock for private subnet 02 within the VPC

  PrivateSubnet03Block:
    Type: String
    Default: 192.168.128.0/18
    Description: CidrBlock for private subnet 03 within the VPC

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: "Primary Worker Network Configuration"
        Parameters:
          - VpcBlock
          - PrivateSubnet01Block
          - PrivateSubnet02Block
          - PrivateSubnet03Block
  
Conditions:
  Has2Azs:
    Fn::Or:
      - Fn::Equals:
        - {Ref: 'AWS::Region'}
        - ap-south-1
      - Fn::Equals:
        - {Ref: 'AWS::Region'}
        - ap-northeast-2
      - Fn::Equals:
        - {Ref: 'AWS::Region'}
        - ca-central-1
      - Fn::Equals:
        - {Ref: 'AWS::Region'}
        - cn-north-1
      - Fn::Equals:
        - {Ref: 'AWS::Region'}
        - sa-east-1
      - Fn::Equals:
        - {Ref: 'AWS::Region'}
        - us-west-1

  HasMoreThan2Azs:
    Fn::Not:
      - Condition: Has2Azs

Mappings:
  ServiceNamePrefixMap:
    aws:
      Name: com.amazonaws
    aws-cn:
      Name: cn.com.amazonaws
    aws-us-gov:
      Name: com.amazonaws

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock:  !Ref VpcBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}-VPC'

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

  PrivateSubnet01:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Private Subnet 01
    Properties:
      AvailabilityZone:
        Fn::Select:
        - '0'
        - Fn::GetAZs:
            Ref: AWS::Region
      CidrBlock:
        Ref: PrivateSubnet01Block
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: !Sub "${AWS::StackName}-PrivateSubnet01"
      - Key: kubernetes.io/role/internal-elb
        Value: 1

  PrivateSubnet02:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Private Subnet 02
    Properties:
      AvailabilityZone:
        Fn::Select:
        - '1'
        - Fn::GetAZs:
            Ref: AWS::Region
      CidrBlock:
        Ref: PrivateSubnet02Block
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: !Sub "${AWS::StackName}-PrivateSubnet02"
      - Key: kubernetes.io/role/internal-elb
        Value: 1

  PrivateSubnet03:
    Condition: HasMoreThan2Azs
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Private Subnet 03
    Properties:
      AvailabilityZone:
        Fn::Select:
        - '2'
        - Fn::GetAZs:
            Ref: AWS::Region
      CidrBlock:
        Ref: PrivateSubnet03Block
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: !Sub "${AWS::StackName}-PrivateSubnet03"
      - Key: kubernetes.io/role/internal-elb
        Value: 1

  PrivateSubnet01RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet01
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnet02RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet02
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnet03RouteTableAssociation:
    Condition: HasMoreThan2Azs
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet03
      RouteTableId: !Ref PrivateRouteTable
     
  EndpointSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group to govern who can access the endpoints
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: !Ref VpcBlock
          
  S3APIEndpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      VpcEndpointType: Gateway
      RouteTableIds:
        - !Ref PrivateRouteTable
      VpcId: !Ref VPC

  ECRAPIEndpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      ServiceName: 
        !Join [ ".", [ !FindInMap [ServiceNamePrefixMap, !Ref "AWS::Partition", Name], !Ref "AWS::Region", "ecr.api" ] ]
      VpcEndpointType: Interface
      PrivateDnsEnabled: true 
      SecurityGroupIds:
        - !GetAtt EndpointSecurityGroup.GroupId
      SubnetIds:
        - !Ref PrivateSubnet01
        - !Ref PrivateSubnet02
        - Fn::If:
          - HasMoreThan2Azs
          - !Ref PrivateSubnet03
          - !Ref AWS::NoValue
      VpcId: !Ref VPC

  ECRDockerEndpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      ServiceName: 
        !Join [ ".", [ !FindInMap [ServiceNamePrefixMap, !Ref "AWS::Partition", Name], !Ref "AWS::Region", "ecr.dkr" ] ]
      VpcEndpointType: Interface
      PrivateDnsEnabled: true 
      SecurityGroupIds:
        - !GetAtt EndpointSecurityGroup.GroupId
      SubnetIds:
        - !Ref PrivateSubnet01
        - !Ref PrivateSubnet02
        - Fn::If:
          - HasMoreThan2Azs
          - !Ref PrivateSubnet03
          - !Ref AWS::NoValue
      VpcId: !Ref VPC

  EC2Endpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      ServiceName: 
        !Join [ ".", [ !FindInMap [ServiceNamePrefixMap, !Ref "AWS::Partition", Name], !Ref "AWS::Region", "ec2" ] ]
      VpcEndpointType: Interface
      PrivateDnsEnabled: true 
      SecurityGroupIds:
        - !GetAtt EndpointSecurityGroup.GroupId
      SubnetIds:
        - !Ref PrivateSubnet01
        - !Ref PrivateSubnet02
        - Fn::If:
          - HasMoreThan2Azs
          - !Ref PrivateSubnet03
          - !Ref AWS::NoValue
      VpcId: !Ref VPC

  CWLogsEndpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.logs"
      VpcEndpointType: Interface
      PrivateDnsEnabled: true 
      SecurityGroupIds:
        - !GetAtt EndpointSecurityGroup.GroupId
      SubnetIds:
        - !Ref PrivateSubnet01
        - !Ref PrivateSubnet02
        - Fn::If:
          - HasMoreThan2Azs
          - !Ref PrivateSubnet03
          - !Ref AWS::NoValue
      VpcId: !Ref VPC

  STSEndpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      ServiceName: 
        !Join [ ".", [ !FindInMap [ServiceNamePrefixMap, !Ref "AWS::Partition", Name], !Ref "AWS::Region", "sts" ] ]
      VpcEndpointType: Interface
      PrivateDnsEnabled: true 
      SecurityGroupIds:
        - !GetAtt EndpointSecurityGroup.GroupId
      SubnetIds:
        - !Ref PrivateSubnet01
        - !Ref PrivateSubnet02
        - Fn::If:
          - HasMoreThan2Azs
          - !Ref PrivateSubnet03
          - !Ref AWS::NoValue
      VpcId: !Ref VPC

  ControlPlaneSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Cluster communication with worker nodes
      VpcId: !Ref VPC

Outputs:

  VpcId:
    Description: The VPC ID
    Value: !Ref VPC

  SubnetIds:
    Description: All subnets in the VPC
    Value:
      Fn::If:
      - HasMoreThan2Azs
      - !Join [ ",", [ !Ref PrivateSubnet01, !Ref PrivateSubnet02, !Ref PrivateSubnet03 ] ]
      - !Join [ ",", [ !Ref PrivateSubnet01, !Ref PrivateSubnet02 ] ]

  SecurityGroups:
    Description: Security group for the cluster control plane communication with worker nodes
    Value: !Join [ ",", [ !Ref ControlPlaneSecurityGroup ] ]