AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template for Trino 475 with S3 Tables integration - Least Privilege'

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Infrastructure Configuration"
        Parameters:
          - KeyName
          - VpcId
          - SubnetId
          - S3TablesBucketName
          - AwsRegion
          - TrinoInstanceType
          - CreateIamUser

Parameters:
  AwsRegion:
    Description: AWS Region for S3 and Glue services
    Type: String
    Default: us-west-2

  KeyName:
    Description: Name of an existing EC2 KeyPair for SSH access
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: Must be the name of an existing EC2 KeyPair

  S3TablesBucketName:
    Description: Name of the S3 bucket for S3 Tables
    Type: String
    AllowedPattern: "[a-z0-9][a-z0-9.-]*"
    ConstraintDescription: Bucket name must contain only lowercase letters, numbers, dots (.), and hyphens (-).

  TrinoInstanceType:
    Description: EC2 Instance type for Trino server
    Type: String
    Default: t3.xlarge

  VpcId:
    Description: VPC where the EC2 instance will be deployed
    Type: AWS::EC2::VPC::Id

  SubnetId:
    Description: Public subnet for the EC2 instance
    Type: AWS::EC2::Subnet::Id

Mappings:
  RegionMap:
    us-east-1:
      AMI: ami-0a3c3a20c09d6f377
    us-east-2:
      AMI: ami-0f5daaa3a7fb3378b
    us-west-1:
      AMI: ami-0ce2cb35386df23c8
    us-west-2:
      AMI: ami-0b36cd6786bcfe120
    eu-west-1:
      AMI: ami-0bb4c991fa89d4551
    eu-central-1:
      AMI: ami-09439f09c55136ecf
    ap-southeast-1:
      AMI: ami-073998dcb56023d25
    ap-southeast-2:
      AMI: ami-0d0175e9dbb94e0d2

Resources:

  S3TablesTableBucket:
    Type: AWS::S3Tables::TableBucket
    Properties:
      TableBucketName: !Ref S3TablesBucketName
      UnreferencedFileRemoval:
        Status: Enabled
        UnreferencedDays: 7
        NoncurrentDays: 7
  
  TrinoSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Trino server
      VpcId: !Ref VpcId

  TrinoInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${AWS::StackName}-trino-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
          - Effect: Allow
            Principal:
              AWS: "*"
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                aws:PrincipalAccount: !Ref AWS::AccountId
                aws:PrincipalArn: !Sub arn:aws:iam::${AWS::AccountId}:role/${AWS::StackName}-trino-role
      ManagedPolicyArns:
        - !Ref TrinoS3TablesRolePolicy


  TrinoS3TablesRolePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: "Policy for Trino S3 Tables access"
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - s3tables:CreateTable
              - s3tables:GetTable
              - s3tables:ListTables
              - s3tables:DeleteTable
              - s3tables:GetNamespace
              - s3tables:ListNamespaces
              - s3tables:CreateNamespace
              - s3tables:GetTableBucket
              - s3tables:CreateTableBucket
              - s3tables:ListTableBuckets
              - s3tables:PutTableData
              - s3tables:GetTableData
              - s3tables:GetTableMetadataLocation
              - s3tables:UpdateTableMetadataLocation
              - s3tables:UpdateNamespace
              - s3tables:DeleteNamespace
              - s3tables:DeleteTableBucket
            Resource: 
              - !Sub "arn:aws:s3tables:${AWS::Region}:${AWS::AccountId}:bucket/${S3TablesBucketName}"
              - !Sub "arn:aws:s3tables:${AWS::Region}:${AWS::AccountId}:bucket/${S3TablesBucketName}/*"
          - Effect: Allow
            Action:
              - s3:GetObject
              - s3:PutObject
              - s3:DeleteObject
              - s3:ListBucket
              - s3:GetBucketLocation
            Resource: 
              - !Sub "arn:aws:s3:::${S3TablesBucketName}"
              - !Sub "arn:aws:s3:::${S3TablesBucketName}/*"
              
  TrinoInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref TrinoInstanceRole
  TrinoInstance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref TrinoInstanceType
      ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AMI]
      KeyName: !Ref KeyName
      IamInstanceProfile: !Ref TrinoInstanceProfile
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          GroupSet: [!Ref TrinoSecurityGroup]
          SubnetId: !Ref SubnetId
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe

          # Enable detailed logging
          exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
          echo "Starting user data script execution..."

          # Update system
          echo "Updating system packages..."
          yum update -y

          # Install dependencies
          echo "Installing dependencies..."
          yum install -y jq aws-cli

          # Install Java 23
          echo "Installing Java 23..."
          rpm --import https://yum.corretto.aws/corretto.key
          curl -L -o /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo
          yum install -y java-23-amazon-corretto-devel

          # Create data directory
          echo "Creating data directory..."
          mkdir -p /var/trino/data

          # Download and configure Trino
          echo "Downloading and configuring Trino..."
          cd /home/ec2-user
          wget https://repo1.maven.org/maven2/io/trino/trino-server/475/trino-server-475.tar.gz
          tar -xvf trino-server-475.tar.gz

          # Create config directories
          echo "Creating config directories..."
          mkdir -p /home/ec2-user/trino-server-475/etc
          mkdir -p /home/ec2-user/trino-server-475/etc/catalog

          # Create node.properties
          echo "Creating node.properties..."
          cat > /home/ec2-user/trino-server-475/etc/node.properties << EOF
          node.environment=tests
          node.id=12345
          node.data-dir=/var/trino/data
          EOF

          # Create jvm.config
          echo "Creating jvm.config..."
          cat > /home/ec2-user/trino-server-475/etc/jvm.config << EOF
          -server
          -Xmx16G
          -XX:InitialRAMPercentage=80
          -XX:MaxRAMPercentage=80
          -XX:G1HeapRegionSize=32M
          -XX:+ExplicitGCInvokesConcurrent
          -XX:+ExitOnOutOfMemoryError
          -XX:+HeapDumpOnOutOfMemoryError
          -XX:-OmitStackTraceInFastThrow
          -XX:ReservedCodeCacheSize=512M
          -XX:PerMethodRecompilationCutoff=10000
          -XX:PerBytecodeRecompilationCutoff=10000
          -Djdk.attach.allowAttachSelf=true
          -Djdk.nio.maxCachedBufferSize=2000000
          -Dfile.encoding=UTF-8
          -XX:+EnableDynamicAgentLoading
          EOF

          # Create config.properties
          echo "Creating config.properties..."
          cat > /home/ec2-user/trino-server-475/etc/config.properties << EOF
          coordinator=true
          node-scheduler.include-coordinator=true
          http-server.http.port=8080
          discovery.uri=http://localhost:8080
          EOF

          # Create S3 Tables catalog configuration
          echo "Creating S3 Tables catalog configuration..."
          cat > /home/ec2-user/trino-server-475/etc/catalog/s3tables_irc.properties << EOF
          connector.name=iceberg
          iceberg.catalog.type=rest
          iceberg.rest-catalog.uri=https://s3tables.${AwsRegion}.amazonaws.com/iceberg
          iceberg.rest-catalog.warehouse=arn:aws:s3tables:${AwsRegion}:${AWS::AccountId}:bucket/${S3TablesBucketName}
          iceberg.rest-catalog.sigv4-enabled=true
          iceberg.rest-catalog.signing-name=s3tables
          iceberg.rest-catalog.view-endpoints-enabled=false
          fs.hadoop.enabled=false
          fs.native-s3.enabled=true
          s3.region=${AwsRegion}
          s3.iam-role=${TrinoInstanceRole.Arn}
          s3.role-session-name=trino-s3tables-session
          EOF

          # Download Trino CLI
          echo "Downloading Trino CLI..."
          wget https://repo1.maven.org/maven2/io/trino/trino-cli/475/trino-cli-475-executable.jar
          chmod +x trino-cli-475-executable.jar
          mv trino-cli-475-executable.jar /home/ec2-user/trino-cli

          # Adjust permissions
          echo "Adjusting permissions..."
          chown -R ec2-user:ec2-user /home/ec2-user
          chown -R ec2-user:ec2-user /var/trino

          # Start Trino server
          echo "Starting Trino server..."
          cd /home/ec2-user/trino-server-475
          sudo -u ec2-user bin/launcher start

          echo "User data script completed successfully"
      Tags:
          - Key: Name 
            Value: TrinoServer

Outputs:
  PublicDNS:
    Description: Public DNS name of the Trino instance
    Value: !GetAtt TrinoInstance.PublicDnsName
    
  SSHCommand:
    Description: Command to SSH into the Trino instance
    Value: !Join ["", ["ssh -i ", !Ref KeyName, ".pem ec2-user@", !GetAtt TrinoInstance.PublicDnsName]]
    
  TrinoURL:
    Description: URL to access Trino Web UI
    Value: !Join ["", ["http://", !GetAtt TrinoInstance.PublicDnsName, ":8080"]]

  TableBucketName:
    Description: Name of the S3 Tables table bucket
    Value: !Ref S3TablesTableBucket
    
  TableBucketARN:
    Description: ARN of the S3 Tables table bucket
    Value: !GetAtt S3TablesTableBucket.TableBucketARN