---
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

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

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

  ControlPlaneSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Cluster communication with worker nodes
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: !Ref VpcBlock
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          CidrIp: !Ref VpcBlock

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]]
