AWSTemplateFormatVersion: "2010-09-09"
Description: "Creates the resources for the fuzzball operator."
Parameters:

  FuzzballVersion:
    Description: 'The version of Fuzzball to deploy. Must be a valid semantic version without the "v" prefix.'
    Type: "String"
    Default: "3.3.0"

  PostgresEngineVersion:
    Description: "The version of the PostgreSQL engine to use. The latest 16.x version is recommended. Run the following command to see available options: aws rds describe-db-engine-versions --engine postgres --query 'DBEngineVersions[].EngineVersion' --output json"
    Type: "String"
    Default: "16.6"

  DomainHostedZoneId:
    Description: The id of the Route53 hosted zone to put Fuzzball and all of it's subdomains under. This is used for the FuzzballOrchestrate CRD.
    Type: String

  Domain:
    Description: The Route53 domain to put Fuzzball and all of it's subdomains under. This is used for the FuzzballOrchestrate CRD.
    Type: String

  LetsEncryptEmail:
    Description: Email address for Let's Encrypt
    Type: String

  KeycloakRealmName:
    Description: Realm display name, also used as organization name by Fuzzball.
    Type: String
    Default: "Fuzzball"

  KeycloakOwnerEmail:
    Description: Fuzzball organization owner email.
    Type: String

  KeycloakUsername:
    Description: Admin username within master realm.
    Type: String
    Default: "keycloak"

  KeycloakPassword:
    Description: Admin password within master realm.
    Type: String
    NoEcho: true

  KeycloakDefaultUserPassword:
    Description: Default user password for users created in the realm including the organization owner.
    Type: String
    NoEcho: true

  InitialInstanceTypes:
    Description: Comma-separated list of instance types to configure on initial cluster setup (e.g., t3a.2xlarge,p3.2xlarge). If empty, no instance types are configured.
    Type: String
    Default: ""

  OverwriteInstanceTypes:
    Description: "When true, overwrite existing instance types with InitialInstanceTypes on stack updates."
    Type: String
    Default: "false"
    AllowedValues:
      - "true"
      - "false"

  DatadogAPIKey:
    Description: "The Datadog API key. If not provided, Datadog will not be configured."
    Type: "String"
    Default: ""
    NoEcho: true

  CloudwatchEnabled:
    Description: "Enable Cloudwatch logging for the runner."
    Type: "String"
    Default: "true"

  ClusterAdminUsers:
    Description: "OPTIONAL: Comma separated list of users to give admin access."
    Type: String
    Default: ""

  EnableRDSDeletionProtection:
    Description: "Enable RDS deletion protection. May may require manual intervention during the deletion of the stack."
    Type: String
    Default: "false"
    AllowedValues:
      - "true"
      - "false"

  EnableRDSDRSnapshot:
    Description: "Enable cross-region automated backups replication for RDS."
    Type: String
    Default: "true"
    AllowedValues:
      - "true"
      - "false"

  SSOAdminRoleArn:
    Description: "OPTIONAL: The arn of the sso assumed role relative to the user(s) you want to have admin access. Ex: arn:aws:iam::ACCOUNT_ID:role/AWSReservedSSO_1234"
    Type: String
    Default: ""

  LegacyNaming:
    Description: "Use legacy resource naming (without stack name prefix). Set to true for existing stacks to avoid resource recreation."
    Type: String
    Default: "false"
    AllowedValues:
      - "true"
      - "false"

Conditions:
  UseLegacyNaming: !Equals [!Ref LegacyNaming, "true"]

Resources:

  # ECSRunnerLambdaExecutionRole defines permissions for the lambda
  ECSRunnerLambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: LambdaECSPermissions
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ecs:RunTask
                  - ecs:StopTask
                  - ecs:ListTasks
                  - ecs:DescribeTasks
                  - ec2:DescribeVpcs
                  - ec2:DescribeSubnets
                  - ec2:DescribeSecurityGroups
                  - ecs:DescribeClusters
                Resource: "*"
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource:
                  - !GetAtt FuzzballPulumiRunnerTaskExecutionRole.Arn
                  - !GetAtt FuzzballPulumiRunnerTaskRole.Arn
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}-ECSRunnerLambda-*"
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-ECSRunnerLambdaExecutionRole
        - Key: ciq:product
          Value: "fuzzball"

  # FuzzballPulumiRunnerTaskExecutionRole defines permissions for the task container agent
  FuzzballPulumiRunnerTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ecs-tasks.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: ECRPermissions
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              # ECR permissions for pulling images
              - Effect: Allow
                Action:
                  - ecr:GetAuthorizationToken
                Resource: "*"
              - Effect: Allow
                Action:
                  - ecr:BatchCheckLayerAvailability
                  - ecr:GetDownloadUrlForLayer
                  - ecr:BatchGetImage
                Resource:
                  - !Sub "arn:aws:ecr:us-east-1:709825985650:repository/ciq/*"
                  - !Sub "arn:aws:ecr:${AWS::Region}:274458771119:repository/ctrliq/cfn-task-runner"

              # EC2 permissions for ENI management
              - Effect: Allow
                Action:
                  - ec2:CreateNetworkInterface
                Resource: "*"
                Condition:
                  StringEquals:
                    "aws:RequestTag/ciq:product": "fuzzball"
              - Effect: Allow
                Action:
                  - ec2:AttachNetworkInterface
                  - ec2:DescribeNetworkInterfaces
                  - ec2:DeleteNetworkInterface
                Resource: "*"
                Condition:
                  StringEquals:
                    "aws:ResourceTag/ciq:product": "fuzzball"

              # CloudWatch Logs permissions
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${AWS::StackName}-FuzzballPulumiRunnerLogGroup-*:*"
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-FuzzballPulumiRunnerTaskExecutionRole
        - Key: ciq:product
          Value: "fuzzball"

  FuzzballPulumiRunnerTaskRolePolicyECRS3KMS:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - ecr:GetAuthorizationToken
            Resource: "*"
          - Effect: Allow
            Action:
              - ecr:BatchGetImage
              - ecr:GetDownloadUrlForLayer
              - ecr:BatchCheckLayerAvailability
            Resource:
              - !Sub "arn:aws:ecr:us-east-1:709825985650:repository/ciq/*"

          # S3 permissions for Pulumi state and other buckets
          - Effect: Allow
            Action:
              - s3:CreateBucket
              - s3:PutBucketTagging
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - s3:CreateBucket
              - s3:GetBucketTagging
              - s3:PutBucketTagging
              - s3:GetBucketLocation
              - s3:HeadBucket
              - s3:ListBucket
              - s3:GetBucketAcl
              - s3:GetBucketCORS
              - s3:GetBucketWebsite
              - s3:GetBucketVersioning
              - s3:GetAccelerateConfiguration
              - s3:GetBucketRequestPayment
              - s3:GetBucketLogging
              - s3:GetLifecycleConfiguration
              - s3:GetReplicationConfiguration
              - s3:GetEncryptionConfiguration
              - s3:GetBucketObjectLockConfiguration
              - s3:DeleteBucket
            Resource:
              - !Sub "arn:aws:s3:::com.ciq.aws-fuzzball-*-log-*"
              - !Sub "arn:aws:s3:::com.ciq.aws-fuzzball-*-audit-*"

          - Effect: Allow
            Action:
              - s3:ListBucket
            Resource:
              - !GetAtt FuzzballPulumiRunnerStateBucket.Arn

          - Effect: Allow
            Action:
              - s3:GetObject
              - s3:PutObject
              - s3:DeleteObject
            Resource:
              - !Sub "${FuzzballPulumiRunnerStateBucket.Arn}/*"

          - Effect: Allow
            Action:
              - s3:CreateBucket
              - s3:PutBucketTagging
              - s3:DeleteBucket
              - s3:PutBucketPolicy
              - s3:PutBucketEncryption
              - s3:PutBucketPublicAccessBlock
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

          # KMS permissions for state encryption and other keys
          - Effect: Allow
            Action:
              - kms:CreateKey
              - kms:TagResource
              - kms:Decrypt
              - kms:GenerateDataKey
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - kms:CreateKey
              - kms:TagResource
              - kms:GenerateDataKey
              - kms:Decrypt
              - kms:Encrypt
              - kms:DescribeKey
              - kms:ScheduleKeyDeletion
              - kms:EnableKeyRotation
              - kms:PutKeyPolicy
              - kms:GetKeyPolicy
              - kms:GetKeyRotationStatus
              - kms:ListResourceTags
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

  FuzzballPulumiRunnerTaskRolePolicySTSRDSEFS:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # STS permissions for role assumption and temporary credentials
          - Effect: Allow
            Action:
              - sts:AssumeRole
              - sts:GetCallerIdentity
              - sts:GetSessionToken
              - sts:GetFederationToken
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

          # RDS permissions
          - Effect: Allow
            Action:
              - rds:DescribeDBParameterGroups
              - rds:DescribeDBParameters
              - rds:DescribeDBSubnetGroups
              - rds:DescribeDBInstances
              - rds:DescribeDBInstanceAutomatedBackups
            Resource: "*"
          - Effect: Allow
            Action:
              - rds:CreateDBInstance
              - rds:AddTagsToResource
              - rds:CreateDBSubnetGroup
              - rds:CreateDBParameterGroup
              - rds:CreateOptionGroup
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - rds:StartDBInstanceAutomatedBackupsReplication
              - rds:StopDBInstanceAutomatedBackupsReplication
            Resource:
              - !Sub "arn:aws:rds:us-east-1:${AWS::AccountId}:auto-backup:*"
              - !Sub "arn:aws:rds:us-east-2:${AWS::AccountId}:auto-backup:*"
              - !Sub "arn:aws:rds:us-west-1:${AWS::AccountId}:auto-backup:*"
              - !Sub "arn:aws:rds:us-west-2:${AWS::AccountId}:auto-backup:*"
              - !Sub "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:db:aws-fuzzball-*-rds-postgres"
          - Effect: Allow
            Action:
              - rds:CreateDBInstance
              - rds:AddTagsToResource
              - rds:CreateDBSubnetGroup
              - rds:CreateDBParameterGroup
              - rds:CreateOptionGroup
              - rds:DeleteDBInstance
              - rds:DescribeDBInstances
              - rds:ModifyDBInstance
              - rds:ListTagsForResource
              - rds:RemoveTagsFromResource
              - rds:DeleteDBSubnetGroup
              - rds:ModifyDBSubnetGroup
              - rds:DeleteDBParameterGroup
              - rds:ModifyDBParameterGroup
              - rds:DeleteOptionGroup
              - rds:ModifyOptionGroup
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

          # EFS permissions
          - Effect: Allow
            Action:
              - elasticfilesystem:DescribeAccessPoints
              - elasticfilesystem:DescribeMountTargetSecurityGroups
              - elasticfilesystem:DescribeMountTargets
            Resource: "*"
          - Effect: Allow
            Action:
              - elasticfilesystem:CreateFileSystem
              - elasticfilesystem:CreateAccessPoint
              - elasticfilesystem:CreateMountTarget
              - elasticfilesystem:CreateTags
              - elasticfilesystem:TagResource
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - elasticfilesystem:CreateFileSystem
              - elasticfilesystem:CreateAccessPoint
              - elasticfilesystem:CreateTags
              - elasticfilesystem:TagResource
              - elasticfilesystem:CreateMountTarget
              - elasticfilesystem:DeleteFileSystem
              - elasticfilesystem:DescribeFileSystems
              - elasticfilesystem:DeleteMountTarget
              - elasticfilesystem:DeleteAccessPoint
              - elasticfilesystem:DescribeMountTargets
              - elasticfilesystem:DeleteTags
              - elasticfilesystem:PutLifecycleConfiguration
              - elasticfilesystem:DescribeLifecycleConfiguration
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

          # Organizations permissions for billing account detection
          - Effect: Allow
            Action:
              - organizations:DescribeAccount
            Resource: "*"

  FuzzballPulumiRunnerTaskRolePolicyEKSR53TAGCFN:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # EKS permissions
          - Effect: Allow
            Action:
              - eks:DescribeAddon
              - eks:DescribeAddonVersions
              - eks:DescribeNodegroup
              - eks:DescribeCluster
              - eks:DescribeUpdate
              - eks:ListNodegroups
            Resource: "*"
          - Effect: Allow
            Action:
              - eks:DeleteAddon
            Resource:
              - !Sub "arn:aws:eks:${AWS::Region}:${AWS::AccountId}:addon/aws-fuzzball-*/amazon-cloudwatch-observability/*"
              - !Sub "arn:aws:eks:${AWS::Region}:${AWS::AccountId}:addon/aws-fuzzball-*/aws-ebs-csi-driver/*"
          - Effect: Allow
            Action:
              - eks:AccessKubernetesApi
            Resource: "*"
            Condition:
              StringLike:
                eks:namespace:
                  - "amazon-cloudwatch"
                  - "autoscaling"
                  - "cert-manager"
                  - "database"
                  - "default"
                  - "fuzzball*"
                  - "identity"
                  - "ingress"
                  - "kube-node-lease"
          - Effect: Allow
            Action:
              - eks:CreateCluster
              - eks:TagResource
              - eks:CreateNodegroup
              - eks:CreateAddon
              - eks:UpdateAddon
              - eks:UpdateNodegroupConfig
              - eks:UpdateNodegroupVersion
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - eks:CreateCluster
              - eks:TagResource
              - eks:CreateNodegroup
              - eks:DeleteCluster
              - eks:DescribeCluster
              - eks:ListClusters
              - eks:ListNodegroups
              - eks:UpdateClusterConfig
              - eks:UpdateClusterVersion
              - eks:UntagResource
              - eks:DeleteNodegroup
              - eks:UpdateNodegroupConfig
              - eks:UpdateNodegroupVersion
              - eks:CreateAddon
              - eks:UpdateAddon
              - eks:ListAddons
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

          # Route53 permissions for DNS
          - Effect: Allow
            Action:
              - route53:GetHostedZone
              - route53:ChangeResourceRecordSets
              - route53:ListResourceRecordSets
              - route53:GetChange
            Resource: "*"

          # Tag permissions
          - Effect: Allow
            Action:
              - tag:GetResources
            Resource: "*"
          - Effect: Allow
            Action:
              - tag:TagResources
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - tag:TagResources
              - tag:UntagResources
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

          # CloudFormation permissions
          - Effect: Allow
            Action:
              - cloudformation:DescribeStacks
              - cloudformation:DescribeStackEvents
            Resource: "*"
          - Effect: Allow
            Action:
              - cloudformation:CreateStack
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - cloudformation:CreateStack
              - cloudformation:ListStacks
              - cloudformation:GetTemplateSummary
              - cloudformation:DeleteStack
              - cloudformation:UpdateStack
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

  FuzzballPulumiRunnerTaskRolePolicyEC2:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # EC2 permissions needed for VPC and EKS
          - Effect: Allow
            Action:
              - ec2:RevokeSecurityGroupIngress
              - ec2:AuthorizeSecurityGroupIngress
            Resource: "*"
            Condition:
              StringLike:
                "aws:ResourceTag/Name": "eks-cluster-sg-aws-fuzzball-*"
          - Effect: Allow
            Action:
              - ec2:CreateTags
              - ec2:CreateVpc
              - ec2:CreateSubnet
              - ec2:CreateSecurityGroup
              - ec2:CreateRouteTable
              - ec2:CreateRoute
              - ec2:CreateNatGateway
              - ec2:AllocateAddress
              - ec2:ImportKeyPair
              - ec2:CreateInternetGateway
              - ec2:AttachInternetGateway
              - ec2:RevokeSecurityGroupEgress
              - ec2:RevokeSecurityGroupIngress
              - ec2:CreateLaunchTemplate
              - ec2:CreateLaunchTemplateVersion
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - ec2:DescribeVpcs
              - ec2:DescribeSubnets
              - ec2:DescribeSecurityGroups
              - ec2:DescribeAddresses
              - ec2:DescribeAvailabilityZones
              - ec2:DescribeInstances
              - ec2:DescribeInstanceTypes
              - ec2:DescribeKeyPairs
              - ec2:DescribeTags
              - ec2:DescribeVpcAttribute
              - ec2:DescribeNetworkInterfaces
              - ec2:DescribeInternetGateways
              - ec2:DescribeNatGateways
              - ec2:DescribeRouteTables
              - ec2:DisassociateAddress
              - ec2:DescribeSecurityGroupRules
              - ec2:DescribeAddressesAttribute
              - ec2:DescribeLaunchTemplates
              - ec2:DescribeLaunchTemplateVersions
              - ec2:RunInstances
            Resource: "*"
          - Effect: Allow
            Action:
              - ec2:CreateSubnet
              - ec2:CreateSecurityGroup
              - ec2:CreateRouteTable
              - ec2:CreateRoute
              - ec2:CreateNatGateway
              - ec2:AttachInternetGateway
              - ec2:DeleteTags
              - ec2:ModifyVpcAttribute
              - ec2:ModifySubnetAttribute
              - ec2:AuthorizeSecurityGroupIngress
              - ec2:AuthorizeSecurityGroupEgress
              - ec2:RevokeSecurityGroupIngress
              - ec2:RevokeSecurityGroupEgress
              - ec2:AssociateRouteTable
              - ec2:ReleaseAddress
              - ec2:DetachInternetGateway
              - ec2:DeleteVpc
              - ec2:DeleteKeyPair
              - ec2:DeleteSubnet
              - ec2:DeleteSecurityGroup
              - ec2:DeleteInternetGateway
              - ec2:DisassociateRouteTable
              - ec2:DeleteRouteTable
              - ec2:DeleteNatGateway
              - ec2:CreateTags
              - ec2:CreateVpc
              - ec2:AllocateAddress
              - ec2:ImportKeyPair
              - ec2:CreateInternetGateway
              - ec2:ModifyLaunchTemplate
              - ec2:DeleteLaunchTemplate
              - ec2:DeleteLaunchTemplateVersions
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"

  FuzzballPulumiRunnerTaskRolePolicyIAM:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:

          # IAM permissions for EKS roles
          - Effect: Allow
            Action:
              - iam:ListVirtualMFADevices
              - iam:ListMFADevices
            Resource: "*"
          - Effect: Allow
            Action: iam:GetRole
            Resource: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/*"

          - Effect: Allow
            Action:
              - iam:CreateRole
              - iam:CreateAccessKey
              - iam:CreateInstanceProfile
              - iam:CreateUser
              - iam:CreatePolicy
              - iam:TagRole
              - iam:TagUser
              - iam:TagInstanceProfile
              - iam:CreateOpenIDConnectProvider
              - iam:GetRole
              - iam:GetPolicy
              - iam:GetPolicyVersion
              - iam:ListPolicyVersions
              - iam:ListUserPolicies
              - iam:ListAttachedUserPolicies
            Resource: "*"
            Condition:
              StringEquals:
                "aws:RequestTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - iam:CreateRole
              - iam:CreateInstanceProfile
              - iam:CreateUser
              - iam:CreatePolicy
              - iam:TagRole
              - iam:TagUser
              - iam:TagInstanceProfile
              - iam:CreateOpenIDConnectProvider
              - iam:GetUser
              - iam:DeleteRole
              - iam:GetRole
              - iam:GetPolicy
              - iam:GetRolePolicy
              - iam:DeleteRolePolicy
              - iam:DetachRolePolicy
              - iam:ListRolePolicies
              - iam:ListAttachedRolePolicies
              - iam:DeleteInstanceProfile
              - iam:AddRoleToInstanceProfile
              - iam:RemoveRoleFromInstanceProfile
              - iam:DeleteAccessKey
              - iam:ListInstanceProfilesForRole
              - iam:GetInstanceProfile
              - iam:CreateAccessKey
              - iam:ListAccessKeys
              - iam:ListGroupsForUser
              - iam:ListSSHPublicKeys
              - iam:ListUserPolicies
              - iam:ListPolicyVersions
              - iam:TagOpenIDConnectProvider
              - iam:GetOpenIDConnectProvider
              - iam:DeleteOpenIDConnectProvider
              - iam:ListUserPolicies
              - iam:ListVirtualMFADevices
              - iam:DeleteLoginProfile
              - iam:ListSigningCertificates
              - iam:ListServiceSpecificCredentials
              - iam:DeleteUser
              - iam:TagPolicy
              - iam:GetPolicy
              - iam:GetPolicyVersion
              - iam:ListAttachedUserPolicies
              - iam:AttachUserPolicy
              - iam:DetachUserPolicy
              - iam:PutUserPolicy
              - iam:DeleteUserPolicy
            Resource: "*"
            Condition:
              StringEquals:
                "aws:ResourceTag/ciq:product": "fuzzball"
          - Effect: Allow
            Action:
              - iam:PassRole
              - iam:UpdateAssumeRolePolicy
            Resource:
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-eks"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-nodes"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-addon-csi"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-ec2-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/com.ciq.aws-fuzzball-*-audit-audit-bucket-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/com.ciq.aws-fuzzball-*-log-log-bucket-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-operator-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-provision-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-efs-image-cache-efs-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-efs-workflow-io-efs-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-efs-efs-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-autoscaler-autoscaler-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-billing-role-*"

  FuzzballPulumiRunnerTaskRolePolicyCustomPolicies:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - iam:CreatePolicy
              - iam:GetPolicy
              - iam:GetPolicyVersion
              - iam:ListPolicyVersions
              - iam:DeletePolicy
            Resource:
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-EC2RolePolicy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-EC2RolePolicy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-OperatorRolePolicy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-OperatorRolePolicy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-ProvisionRolePolicy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-ProvisionRolePolicy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-AutoscalerRolePolicy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-AutoscalerRolePolicy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-BillingRolePolicy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-BillingRolePolicy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/cloudWatchAgentPolicy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/cloudWatchAgentPolicy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-efs-policy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-efs-policy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-image-cache-efs-policy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-workflow-io-efs-policy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-image-cache-efs-policy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-workflow-io-efs-policy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/com.ciq.aws-fuzzball-*-log-log-bucket-role-policy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/com.ciq.aws-fuzzball-*-log-log-bucket-role-policy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/com.ciq.aws-fuzzball-*-audit-audit-bucket-role-policy-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/com.ciq.aws-fuzzball-*-audit-audit-bucket-role-policy-*:version/*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-cert-manager-route53-policy"
              - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-cert-manager-route53-policy:version/*"

  FuzzballPulumiRunnerTaskRolePolicyIAMAttachments:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - iam:AttachRolePolicy
            Resource:
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-eks"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-nodes"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-addon-csi"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-ec2-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/com.ciq.aws-fuzzball-*-audit-audit-bucket-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/com.ciq.aws-fuzzball-*-log-log-bucket-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-operator-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-provision-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-efs-efs-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-efs-image-cache-efs-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-efs-workflow-io-efs-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-autoscaler-autoscaler-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-billing-role-*"
              - !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-fuzzball-*-cert-manager-route53"
            Condition:
              ArnLike:
                iam:PolicyARN:
                  - "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
                  - "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
                  - "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
                  - "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
                  - "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
                  - "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController"
                  - "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
                  - "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
                  - "arn:aws:iam::aws:policy/service-role/AmazonEFSCSIDriverPolicy"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/cloudWatchAgentPolicy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-ProvisionRolePolicy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-EC2RolePolicy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-OperatorRolePolicy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-AutoscalerRolePolicy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-BillingRolePolicy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-image-cache-efs-policy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-workflow-io-efs-policy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-efs-efs-policy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/com.ciq.aws-fuzzball-*-audit-audit-bucket-role-policy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/com.ciq.aws-fuzzball-*-log-log-bucket-role-policy-*"
                  - !Sub "arn:aws:iam::${AWS::AccountId}:policy/aws-fuzzball-*-cert-manager-route53-policy"


  # FuzzballPulumiRunnerTaskRole defines permissions for the task
  FuzzballPulumiRunnerTaskRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ecs-tasks.amazonaws.com"
            Action: "sts:AssumeRole"
      ManagedPolicyArns:
        - !Ref FuzzballPulumiRunnerTaskRolePolicyECRS3KMS
        - !Ref FuzzballPulumiRunnerTaskRolePolicySTSRDSEFS
        - !Ref FuzzballPulumiRunnerTaskRolePolicyEKSR53TAGCFN
        - !Ref FuzzballPulumiRunnerTaskRolePolicyIAM
        - !Ref FuzzballPulumiRunnerTaskRolePolicyIAMAttachments
        - !Ref FuzzballPulumiRunnerTaskRolePolicyEC2
        - !Ref FuzzballPulumiRunnerTaskRolePolicyCustomPolicies
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-FuzzballPulumiRunnerTaskRole
        - Key: ciq:product
          Value: "fuzzball"

  # FuzzballPulumiRunnerCluster is the cluster where the runner executes
  FuzzballPulumiRunnerCluster:
    Type: AWS::ECS::Cluster
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-FuzzballPulumiRunnerCluster
        - Key: ciq:product
          Value: "fuzzball"

  # FuzzballPulumiRunnerKMSKey is used to encrypt the pulumi state
  FuzzballPulumiRunnerKMSKey:
    Type: AWS::KMS::Key
    Properties:
      Description: "KMS key for encrypting Pulumi state in S3"
      EnableKeyRotation: true
      KeyPolicy:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
            Action: "kms:*"
            Resource: "*"
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-FuzzballPulumiRunnerKMSKey
        - Key: ciq:product
          Value: "fuzzball"

  # FuzzballPulumiRunnerStateBucket stores the pulumi state
  FuzzballPulumiRunnerStateBucket:
    Type: AWS::S3::Bucket
    Properties:
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: !Ref FuzzballPulumiRunnerKMSKey
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-FuzzballPulumiRunnerStateBucket
        - Key: ciq:product
          Value: "fuzzball"

  FuzzballPulumiRunnerStateBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref FuzzballPulumiRunnerStateBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !GetAtt FuzzballPulumiRunnerTaskRole.Arn
            Action: s3:*
            Resource:
              - !Sub "arn:aws:s3:::${FuzzballPulumiRunnerStateBucket}"
              - !Sub "arn:aws:s3:::${FuzzballPulumiRunnerStateBucket}/*"

  # FuzzballPulumiRunnerTaskDefinition defines the pulumi runner as a fargate task
  FuzzballPulumiRunnerTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !If
        - UseLegacyNaming
        - pulumi-runner
        - !Sub "${AWS::StackName}-pulumi-runner"
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      Cpu: "1024"
      Memory: "2048"
      ExecutionRoleArn: !Ref FuzzballPulumiRunnerTaskExecutionRole
      TaskRoleArn: !Ref FuzzballPulumiRunnerTaskRole
      RuntimePlatform:
        OperatingSystemFamily: LINUX
      ContainerDefinitions:
        - Name: RunFuzzballPulumiRunner
          Image: !Sub 709825985650.dkr.ecr.us-east-1.amazonaws.com/ciq/aws-pulumi-runner:${FuzzballVersion}
          Essential: true
          Environment:
            - Name: CIQ_ENV
              Value: !Sub Env
          LogConfiguration:
            LogDriver: "awslogs"
            Options:
              awslogs-group: !Ref FuzzballPulumiRunnerLogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: "ecs"
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-FuzzballPulumiRunnerTaskDefinition
        - Key: ciq:product
          Value: "fuzzball"

  # FuzzballPulumiRunnerLogGroup captures logs from the runner
  FuzzballPulumiRunnerLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 30
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-fuzzball-pulumi-runner-logs
        - Key: ciq:product
          Value: "fuzzball"

  # ECSRunnerLambda configures and starts the pulumi runner task
  ECSRunnerLambda:
    Type: AWS::Lambda::Function
    Properties:
      PackageType: Image
      Architectures:
        - x86_64
      Role: !GetAtt ECSRunnerLambdaExecutionRole.Arn
      Timeout: 30
      MemorySize: 128
      Code:
        ImageUri: !Sub "274458771119.dkr.ecr.${AWS::Region}.amazonaws.com/ctrliq/cfn-task-runner:${FuzzballVersion}"
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-ECSRunnerLambda
        - Key: ciq:product
          Value: "fuzzball"

  # ManageClusterForTask runs before the runner to ensure the cluster is ready for startup and teardown
  ManageClusterForTask:
    Type: Custom::RunEcsTask
    DependsOn:
      - FuzzballPulumiRunnerCluster
      - ECSRunnerLambda
      - FuzzballPulumiRunnerKMSKey
    Properties:
      Debug: true
      ManageCluster: true
      Cluster: !Ref FuzzballPulumiRunnerCluster
      TaskDefinition: !Ref FuzzballPulumiRunnerTaskDefinition
      ServiceToken: !GetAtt ECSRunnerLambda.Arn

  # RunFuzzballPulumiRunner triggers the pulumi runner
  RunFuzzballPulumiRunner:
    Type: Custom::RunEcsTask
    DependsOn:
      - ManageClusterForTask
    Properties:
      Debug: true
      ServiceToken: !GetAtt ECSRunnerLambda.Arn
      Cluster: !Ref FuzzballPulumiRunnerCluster
      TaskDefinition: !Ref FuzzballPulumiRunnerTaskDefinition
      TaskParameters: !Sub |
        {
          "stackName": "${AWS::StackName}",
          "region": "${AWS::Region}",
          "accountId": "${AWS::AccountId}",
          "fuzzballVersion": "${FuzzballVersion}",
          "postgresEngineVersion": "${PostgresEngineVersion}",
          "domainHostedZoneId": "${DomainHostedZoneId}",
          "domain": "${Domain}",
          "letsEncryptEmail": "${LetsEncryptEmail}",
          "keycloakRealmName": "${KeycloakRealmName}",
          "keycloakOwnerEmail": "${KeycloakOwnerEmail}",
          "keycloakUsername": "${KeycloakUsername}",
          "keycloakPassword": "${KeycloakPassword}",
          "keycloakDefaultUserPassword": "${KeycloakDefaultUserPassword}",
          "initialInstanceTypes": "${InitialInstanceTypes}",
          "overwriteInstanceTypes": ${OverwriteInstanceTypes},
          "pulumiStateBucketUrl": "s3://${FuzzballPulumiRunnerStateBucket}",
          "pulumiStateKmsKey": "${FuzzballPulumiRunnerKMSKey}",
          "cloudwatchEnabled": "${CloudwatchEnabled}",
          "clusterAdminUsers": "${ClusterAdminUsers}",
          "deletionProtection": ${EnableRDSDeletionProtection},
          "fuzzballAdminSSORole": "${SSOAdminRoleArn}",
          "rdsDRSnapshotEnabled": ${EnableRDSDRSnapshot}
        }


Outputs:
 KubeConfigCommand:
   Value: !Sub "aws eks update-kubeconfig --region ${AWS::Region} --name ${RunFuzzballPulumiRunner.EKSClusterName}"
