AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS Cluster with Fargate, ALB Ingress Controller (AWS Load Balancer Controller), and ECR'

Parameters:
  Environment:
    Type: String
    Default: dev
    AllowedValues: [dev, prod]
  KubernetesVersion:
    Type: String
    Default: '1.32'
    Description: EKS Kubernetes version

Resources:
  # VPC
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub 'eks-vpc-${Environment}'

  PublicSubnetOne:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub 'eks-public-1-${Environment}'
        - Key: kubernetes.io/role/elb
          Value: '1'

  PublicSubnetTwo:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub 'eks-public-2-${Environment}'
        - Key: kubernetes.io/role/elb
          Value: '1'

  PrivateSubnetOne:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub 'eks-private-1-${Environment}'
        - Key: kubernetes.io/role/internal-elb
          Value: '1'

  PrivateSubnetTwo:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.3.0/24
      AvailabilityZone: !Select [1, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub 'eks-private-2-${Environment}'
        - Key: kubernetes.io/role/internal-elb
          Value: '1'

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  GatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  NatGatewayEIP:
    Type: AWS::EC2::EIP
    DependsOn: GatewayAttachment
    Properties:
      Domain: vpc

  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayEIP.AllocationId
      SubnetId: !Ref PublicSubnetOne

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

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: GatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetOneAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetOne
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetTwoAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetTwo
      RouteTableId: !Ref PublicRouteTable

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

  PrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway

  PrivateSubnetOneAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetOne
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetTwoAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetTwo
      RouteTableId: !Ref PrivateRouteTable

  # Security Groups
  ClusterSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EKS cluster security group
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'eks-cluster-sg-${Environment}'

  # IAM Role for EKS Cluster
  EKSClusterRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: eks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy

  # EKS Cluster
  EKSCluster:
    Type: AWS::EKS::Cluster
    Properties:
      Name: !Sub 'eks-cluster-${Environment}'
      Version: !Ref KubernetesVersion
      RoleArn: !GetAtt EKSClusterRole.Arn
      ResourcesVpcConfig:
        SubnetIds:
          - !Ref PublicSubnetOne
          - !Ref PublicSubnetTwo
          - !Ref PrivateSubnetOne
          - !Ref PrivateSubnetTwo
        SecurityGroupIds:
          - !Ref ClusterSecurityGroup
        EndpointPublicAccess: true
        EndpointPrivateAccess: true
      Logging:
        ClusterLogging:
          EnabledTypes:
            - Type: api
            - Type: audit
            - Type: authenticator
            - Type: controllerManager
            - Type: scheduler
      Tags:
        - Key: Environment
          Value: !Ref Environment

  # Fargate Profile for system pods
  FargateProfileSystem:
    Type: AWS::EKS::FargateProfile
    Properties:
      ClusterName: !Ref EKSCluster
      FargateProfileName: !Sub 'fargate-system-${Environment}'
      PodExecutionRoleArn: !GetAtt FargatePodExecutionRole.Arn
      Subnets:
        - !Ref PrivateSubnetOne
        - !Ref PrivateSubnetTwo
      Selectors:
        - Namespace: kube-system
        - Namespace: default

  # Fargate Profile for application pods
  FargateProfileApp:
    Type: AWS::EKS::FargateProfile
    Properties:
      ClusterName: !Ref EKSCluster
      FargateProfileName: !Sub 'fargate-app-${Environment}'
      PodExecutionRoleArn: !GetAtt FargatePodExecutionRole.Arn
      Subnets:
        - !Ref PrivateSubnetOne
        - !Ref PrivateSubnetTwo
      Selectors:
        - Namespace: app
          Labels:
            environment: !Ref Environment

  # IAM Role for Fargate Pod Execution
  FargatePodExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: eks-fargate-pods.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy

  # ECR Repository
  ECRRepository:
    Type: AWS::ECR::Repository
    Properties:
      ImageScanningConfiguration:
        ScanOnPush: true
      EncryptionConfiguration:
        EncryptionType: KMS
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [{
              "rulePriority": 1,
              "description": "Keep last 10 images",
              "selection": {
                "tagStatus": "any",
                "countType": "imageCountMoreThan",
                "countNumber": 10
              },
              "action": { "type": "expire" }
            }]
          }
      Tags:
        - Key: Environment
          Value: !Ref Environment

  # OIDC Provider for IRSA (IAM Roles for Service Accounts)
  # Note: OIDCProvider requires the cluster OIDC issuer URL.
  # After cluster creation, retrieve it with:
  #   aws eks describe-cluster --name <cluster-name> --query "cluster.identity.oidc.issuer"
  # Then create the OIDC provider via:
  #   eksctl utils associate-iam-oidc-provider --cluster <cluster-name> --approve

  # IAM Role for AWS Load Balancer Controller (IRSA)
  # The AWS Load Balancer Controller replaces the legacy ALB Ingress Controller.
  # It is installed via Helm after cluster creation and uses IRSA for permissions.
  AWSLoadBalancerControllerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'AmazonEKSLoadBalancerControllerRole-${Environment}'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Federated: !Sub 'arn:aws:iam::${AWS::AccountId}:oidc-provider/oidc.eks.${AWS::Region}.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE'
            Action: sts:AssumeRoleWithWebIdentity
            Condition:
              StringEquals:
                'oidc.eks.${AWS::Region}.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub': 'system:serviceaccount:kube-system:aws-load-balancer-controller'
                'oidc.eks.${AWS::Region}.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud': 'sts.amazonaws.com'
      Policies:
        - PolicyName: AWSLoadBalancerControllerPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - ec2:DescribeAccountAttributes
                  - ec2:DescribeAddresses
                  - ec2:DescribeAvailabilityZones
                  - ec2:DescribeInternetGateways
                  - ec2:DescribeVpcs
                  - ec2:DescribeSubnets
                  - ec2:DescribeSecurityGroups
                  - ec2:DescribeInstances
                  - ec2:DescribeNetworkInterfaces
                  - ec2:DescribeTags
                  - ec2:GetCoipPoolUsage
                  - ec2:DescribeCoipPools
                  - elasticloadbalancing:DescribeLoadBalancers
                  - elasticloadbalancing:DescribeLoadBalancerAttributes
                  - elasticloadbalancing:DescribeListeners
                  - elasticloadbalancing:DescribeListenerCertificates
                  - elasticloadbalancing:DescribeSSLPolicies
                  - elasticloadbalancing:DescribeRules
                  - elasticloadbalancing:DescribeTargetGroups
                  - elasticloadbalancing:DescribeTargetGroupAttributes
                  - elasticloadbalancing:DescribeTargetHealth
                  - elasticloadbalancing:DescribeTags
                Resource: '*'
              - Effect: Allow
                Action:
                  - cognito-idp:DescribeUserPoolClient
                  - acm:ListCertificates
                  - acm:DescribeCertificate
                  - iam:ListServerCertificates
                  - iam:GetServerCertificate
                  - waf-regional:GetWebACL
                  - waf-regional:GetWebACLForResource
                  - waf-regional:AssociateWebACL
                  - waf-regional:DisassociateWebACL
                  - wafv2:GetWebACL
                  - wafv2:GetWebACLForResource
                  - wafv2:AssociateWebACL
                  - wafv2:DisassociateWebACL
                  - shield:GetSubscriptionState
                  - shield:DescribeProtection
                  - shield:CreateProtection
                  - shield:DeleteProtection
                Resource: '*'
              - Effect: Allow
                Action:
                  - ec2:AuthorizeSecurityGroupIngress
                  - ec2:RevokeSecurityGroupIngress
                  - ec2:CreateSecurityGroup
                  - ec2:CreateTags
                  - ec2:DeleteTags
                  - ec2:DeleteSecurityGroup
                  - ec2:DescribeSecurityGroupRules
                  - ec2:ModifySecurityGroupRules
                  - ec2:RevokeSecurityGroupEgress
                Resource: '*'
              - Effect: Allow
                Action:
                  - elasticloadbalancing:CreateLoadBalancer
                  - elasticloadbalancing:CreateTargetGroup
                  - elasticloadbalancing:CreateListener
                  - elasticloadbalancing:DeleteListener
                  - elasticloadbalancing:CreateRule
                  - elasticloadbalancing:DeleteRule
                  - elasticloadbalancing:AddTags
                  - elasticloadbalancing:RemoveTags
                  - elasticloadbalancing:ModifyLoadBalancerAttributes
                  - elasticloadbalancing:SetIpAddressType
                  - elasticloadbalancing:SetSecurityGroups
                  - elasticloadbalancing:SetSubnets
                  - elasticloadbalancing:DeleteLoadBalancer
                  - elasticloadbalancing:ModifyTargetGroup
                  - elasticloadbalancing:ModifyTargetGroupAttributes
                  - elasticloadbalancing:DeleteTargetGroup
                  - elasticloadbalancing:RegisterTargets
                  - elasticloadbalancing:DeregisterTargets
                  - elasticloadbalancing:SetWebAcl
                  - elasticloadbalancing:ModifyListener
                  - elasticloadbalancing:AddListenerCertificates
                  - elasticloadbalancing:RemoveListenerCertificates
                  - elasticloadbalancing:ModifyRule
                Resource: '*'

  # CloudWatch Log Group for EKS
  EKSLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/eks/${EKSCluster}/cluster'
      RetentionInDays: 7

Outputs:
  ClusterName:
    Description: EKS Cluster name
    Value: !Ref EKSCluster
    Export:
      Name: !Sub '${AWS::StackName}-ClusterName'

  ClusterArn:
    Description: EKS Cluster ARN
    Value: !GetAtt EKSCluster.Arn
    Export:
      Name: !Sub '${AWS::StackName}-ClusterArn'

  ClusterEndpoint:
    Description: EKS Cluster API endpoint
    Value: !GetAtt EKSCluster.Endpoint
    Export:
      Name: !Sub '${AWS::StackName}-ClusterEndpoint'

  ECRRepositoryUri:
    Description: ECR Repository URI
    Value: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}'
    Export:
      Name: !Sub '${AWS::StackName}-ECRRepositoryUri'

  FargatePodExecutionRoleArn:
    Description: Fargate Pod Execution Role ARN
    Value: !GetAtt FargatePodExecutionRole.Arn
    Export:
      Name: !Sub '${AWS::StackName}-FargatePodExecutionRoleArn'

  LoadBalancerControllerRoleArn:
    Description: IAM Role ARN for AWS Load Balancer Controller (IRSA)
    Value: !GetAtt AWSLoadBalancerControllerRole.Arn
    Export:
      Name: !Sub '${AWS::StackName}-LBControllerRoleArn'
