AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31 
Description: >
  AWS CloudFormation template for bootstraps BRConnector
  refer: https://github.com/aws-samples/sample-connector-for-bedrock
Metadata:
  LICENSE: MIT-0 License
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: 
          default: VPC settings for BRConnector
      - Parameters:
          - VpcId
          - SubnetId
          - BREndpoint

      - Label: 
          default: Compute settings for BRConnector
      - Parameters:
          - ComputeType

      - Label: 
          default: EC2 settings for BRConnector
      - Parameters:
          - EC2InstanceType
          - EC2InstanceAMI

      - Label: 
          default: Lambda settings for BRConnector
      - Parameters:
          - EcrRepo
          - LambdaArch

      - Label: 
          default: Settings for RDS PostgreSQL
      - Parameters:
          - StandaloneDB
          - PGDatabase
          - PGUser
          - PGPassword

      - Label: 
          default: Others Settings
      - Parameters:
          - KeepEc2
          - EnableCloudfront

#Mappings:
#  TheRegionYouAreDeployingIn:
#    us-west-2:
#      IsNotSupportedPleaseUseADifferentRegion: False
#    us-east-1:
#      IsNotSupportedPleaseUseADifferentRegion: True
#Conditions:
#  RunningInAllowedRegion: !Equals [!FindInMap [TheRegionYouAreDeployingIn, !Ref 'AWS::Region', IsNotSupportedPleaseUseADifferentRegion], True]

Parameters:
  VpcId:
    Description: select VPC which has public subnets
    Type: AWS::EC2::VPC::Id
  SubnetId:
    Description: select one public subnet
    Type: AWS::EC2::Subnet::Id
  BREndpoint:
    Description: >-
      Deploy a bedrock-runtime endpoint in your VPC ? It is mendortory, for running BRConnector on Lambda
      \nIf you already have one, choose false. \nIf you are new to this cfn template, choose true. 
    Type: String
    AllowedValues:
      - true
      - false
    Default: true

  ComputeType:
    Description: Choose using Lambda or EC2 to running BRConnector
    Type: String
    AllowedValues:
      - lambda
      - ec2
    Default: lambda

  EC2InstanceType:
    Description: EC2 instance type
    Type: String
    AllowedValues:
      - t3.medium
      - m5.large
    ConstraintDescription: Must be a valid instance type
    Default: t3.medium
  EC2InstanceAMI:
    Description: EC2 instance AMI is Amazon Linux 2023, do not support other distribution. 
    Type: String
    AllowedValues:
      - al2023-ami-kernel-default-x86_64
    Default: al2023-ami-kernel-default-x86_64

  EcrRepo:
    Description: >-
      Prefix of private repo name , repo full name will be brconnect-<AWS::StackName>
    Type: String
    Default: brconnector

  LambdaArch:
    Description: >-
      Choose Architecture amd64 or arm64 for Lambda runtime
    Type: String
    AllowedValues:
      - amd64
      - arm64
    Default: arm64

  StandaloneDB:
    Description: >-
      If true, deploy RDS PostgreSQL (db.t3.micro with 20GB storage) instead of PostgreSQL container in EC2
      If ComputeType set to `lambda`, this value always be true.
    Type: String
    AllowedValues:
      - true
      - false
    Default: false

  PGDatabase:
    Description: PostgreSQL default database name
    Type: String
    Default: brconnector_db
  PGUser:
    Description: PostgreSQL default user name
    Type: String
    Default: postgres
  PGPassword:
    Description: PostgreSQL default password
    Type: String
    Default: mysecretpassword
    
  KeepEc2:
    Description: keep ec2 instance for debugging when you deploy brconnector on lambda
    Type: String
    AllowedValues:
      - true
      - false
    Default: true

  EnableCloudfront:
    Description: Enable Cloudfront or not, when using Lambda ComputeType. If you use EC2, this option will be ignored
    Type: String
    AllowedValues:
      - true
      - false
    Default: true

Conditions:
  IsBREndpoint: !Equals [!Ref BREndpoint, 'true']
  IsStandaloneDB: !Or
    - !Equals [!Ref StandaloneDB, 'true']
    - !Condition IsLambda
  IsLambda: !Equals [!Ref ComputeType, 'lambda']
  IsEc2: !Equals [!Ref ComputeType, 'ec2']
  NotKeepEc2: !Not [!Equals [!Ref KeepEc2, 'true']]
  IsEnableCloudfrontEc2: !Condition IsEc2
  IsEnableCloudfrontLambda: !And
    - !Equals [!Ref EnableCloudfront, 'true']
    - !Condition IsLambda

Mappings:
  ArchitectureMapping:
    amd64:
      Architecture: x86_64
    arm64:
      Architecture: arm64

Resources:
  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      Tags:
        - Key: Environment
          Value: !Ref AWS::StackName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ec2.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      ManagedPolicyArns:
      # - arn:aws:iam::aws:policy/AdministratorAccess
      - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess
      - arn:aws:iam::aws:policy/AWSLambda_FullAccess
      Policies:
      - PolicyName: MyInlinePolicy-bedrock
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Sid: InvokeModel
            Action:
            - 'bedrock:InvokeModelWithResponseStream'
            - 'bedrock:InvokeModel'
            Resource: 'arn:aws:bedrock:*::foundation-model/*'
          - Effect: Allow
            Sid: ListFM
            Action:
            - 'bedrock:ListFoundationModels'
            Resource: '*'
      - PolicyName: MyInlinePolicy-ssm-parameter
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Sid: SSMPutParameter
            Action:
            - 'ssm:PutParameter'
            - 'ssm:GetParameter'
            - 'ssm:GetParameters'
            Resource: 'arn:aws:ssm:*:*:parameter/*'
      # - PolicyName: MyInlinePolicy-cfn-signal
      #   PolicyDocument:
      #     Version: '2012-10-17'
      #     Statement:
      #     - Effect: Allow
      #       Action: 'cloudformation:SignalResource'
      #       Resource: '*'
      #     - Effect: Allow
      #       Action: 's3:*'
      #       Resource: '*'

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
      - Ref: EC2Role

  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP and SSH
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 8866
          ToPort: 8866
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      VpcId: !Ref VpcId

  MySecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref InstanceSecurityGroup
      IpProtocol: "tcp"
      FromPort: "5432"
      ToPort: "5432"
      SourceSecurityGroupId: !Ref InstanceSecurityGroup

  MyPGParameterGroup:
    Type: AWS::RDS::DBParameterGroup
    Properties:
      Description: "My PostgreSQL Parameter Group"
      Family: "postgres16"
      Parameters:
        rds.force_ssl: "0"

  MyPG:
    Condition: IsStandaloneDB
    Type: AWS::RDS::DBInstance
    Properties:
      AllocatedStorage: '20'
      DBName: !Ref PGDatabase
      DBInstanceClass: db.t3.micro
      Engine: postgres
      EngineVersion: "16.3"
      MasterUsername: postgres
      MasterUserPassword: !Ref PGPassword
      DBParameterGroupName: !Ref MyPGParameterGroup
      VPCSecurityGroups:
        - !Ref InstanceSecurityGroup

  MyEC2Instance:
    DependsOn: 
    - MySSMParameterFirstUserKey 
    - MySSMParameterAdminKey
    - MySSMParameterPGDB
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: 
        Ref: EC2InstanceType
      ImageId: 
        Fn::Join:
        - ''
        - - resolve:ssm:/aws/service/ami-amazon-linux-latest/
          - Ref: EC2InstanceAMI
      KeyName: !Ref "AWS::NoValue"
      IamInstanceProfile: 
        Ref: EC2InstanceProfile
      NetworkInterfaces:
        - AssociatePublicIpAddress: 'true'
          DeviceIndex: '0'
          GroupSet:
            - !Ref InstanceSecurityGroup
          SubnetId: !Ref SubnetId
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 20
            VolumeType: gp2
            DeleteOnTermination: true
      Tags:
        - Key: Name
          Value: !Join ["", ["BRConnectorInstance-", !Ref AWS::StackName]]
      UserData:
        Fn::Base64: !Sub |-
          #!/bin/bash -xe
          # install packages & buildx
          yum install -y aws-cfn-bootstrap docker jq git cronie
          systemctl restart docker
          systemctl enable docker
          systemctl restart crond
          systemctl enable crond
          # mkdir -p ~/.docker/cli-plugins
          # wget -q -O ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.15.1/buildx-v0.15.1.linux-amd64
          # chmod a+x ~/.docker/cli-plugins/docker-buildx
          # docker run -t --rm --privileged tonistiigi/binfmt --install all
          # docker buildx inspect --bootstrap
          # docker buildx create --use --platform=linux/arm64,linux/amd64 --name multi-platform-builder

          # save default settings 
          export PGPWD=$(echo "${PGPassword}" |tee /root/pg-default-password.txt)
          export KEY=$(uuidgen |tee /root/admin-api-key.txt)
          export AWS_DEFAULT_REGION="${AWS::Region}"

          # start postgress db
          PGHOST=$(aws ssm get-parameter --name /brconnector/pgdb-${AWS::StackName} --query 'Parameter.Value' --output text)
          if [[ $PGHOST == "172.17.0.1" ]]; then
            docker run --restart=unless-stopped --name postgres -e POSTGRES_PASSWORD=${PGPassword} -p 5432:5432 -d postgres
            sleep 10; echo "CREATE DATABASE ${PGDatabase};" |docker exec -i postgres psql -U ${PGUser} 
          fi

          # start brconnector container
          docker run --restart=unless-stopped --name brconnector -p 8866:8866 -e AWS_DEFAULT_REGION=${AWS::Region} -e PGSQL_HOST=$PGHOST -e PGSQL_DATABASE=${PGDatabase} -e PGSQL_USER=${PGUser} -e PGSQL_PASSWORD=${PGPassword} -e ADMIN_API_KEY=$KEY -d cloudbeer/sample-connector-for-bedrock

          # create first user
          sleep 10;
          curl -X POST "http://localhost:8866/admin/api-key/apply" -H "Content-Type: application/json" -H "Authorization: Bearer $KEY" -d '{"name": "adminuser","group_id": 1,"role": "admin","email": "", "month_quota":"20"}'

          # list users
          curl "http://localhost:8866/admin/api-key/list" -H "Authorization: Bearer $KEY" |jq -r . |tee /root/first-user-key.txt
          USERKEY=$(cat /root/first-user-key.txt |jq -r '.data.items[0].api_key')
          aws ssm put-parameter --name /brconnector/first-user-key-${AWS::StackName} --value "$USERKEY" --type String --overwrite
          aws ssm put-parameter --name /brconnector/admin-key-${AWS::StackName} --value "$KEY" --type String --overwrite

          # add health check script to root's crontab
          cat >/root/healthcheck.sh <<-EOF
          #!/bin/bash
          CURR=\$(date +%Y%m%d-%H%M%S)
          STAT=\$(curl -sL -w '%{http_code}' -o /dev/null "http://localhost:8866")
          if [[ \$STAT -ne 200 ]]; then
            docker restart brconnector
            echo "restart brconnector at \$CURR"
          fi
          EOF
          chmod a+x /root/healthcheck.sh
          echo '*/5 * * * * sh -x /root/healthcheck.sh >>/root/healthcheck.log 2>&1' | /usr/bin/crontab -

          aws ecr get-login-password --region ${AWS::Region} | docker login --username AWS --password-stdin ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com
          docker pull --platform=linux/arm64 cloudbeer/sample-connector-for-bedrock-lambda
          docker tag cloudbeer/sample-connector-for-bedrock-lambda ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:arm64
          docker push ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:arm64
          docker pull --platform=linux/amd64 cloudbeer/sample-connector-for-bedrock-lambda
          docker tag cloudbeer/sample-connector-for-bedrock-lambda ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:amd64
          docker push ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:amd64
          docker manifest create ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:latest --amend ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:arm64 --amend ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:amd64
          docker manifest annotate ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:latest ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:arm64 --os linux --arch arm64
          docker manifest annotate ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:latest ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:amd64 --os linux --arch amd64
          docker manifest push ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:latest

          # prepare to delete self
          TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
          INST_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/dynamic/instance-identity/document |jq -r .instanceId)
          aws lambda update-function-configuration --function-name ${DeleteEC2Lambda} --environment "Variables={INSTANCE_ID=$INST_ID}"

          # call back to cfn
          /opt/aws/bin/cfn-signal -s true '${PrivateWaitHandle}' # variable handled by cfn
          if [[ ${KeepEc2} != "true" && ${ComputeType} == "lambda" ]]; then        
            aws lambda invoke --function-name ${DeleteEC2Lambda} /tmp/output.log
          fi

  PrivateWaitHandle:
    Type: AWS::CloudFormation::WaitConditionHandle

  PrivateWaitCondition:
    DependsOn: MyEC2Instance
    Type: AWS::CloudFormation::WaitCondition
    Properties:
      Handle: !Ref PrivateWaitHandle
      Timeout: '900' # about 10 min will receive signal
      Count: 1

  PrivateECRRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Sub "${EcrRepo}-${AWS::StackName}"

  MyCloudFrontDistribution:
    Condition: IsEnableCloudfrontEc2
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - Id: myEC2Origin
            DomainName: !GetAtt MyEC2Instance.PublicDnsName          
            CustomOriginConfig:
              HTTPPort: '8866'
              OriginProtocolPolicy: http-only
        Enabled: 'true'
        Comment: CloudFront distribution in front of EC2
        # ViewerProtocolPolicy: https-only
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
            - PUT
            - POST
            - PATCH
            - DELETE
          TargetOriginId: myEC2Origin
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Disable caching https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html#managed-cache-policy-caching-disabled
          OriginRequestPolicyId: 216adef6-5c7f-47e4-b989-5492eafa07d3 # Allow all Viewer https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html#managed-origin-request-policy-all-viewer
          # ForwardedValues:
          #   QueryString: 'false'
          #   Cookies:
          #     Forward: none
          ViewerProtocolPolicy: redirect-to-https
        ViewerCertificate:
          CloudFrontDefaultCertificate: 'true'

  MyCloudFrontLambda:
    Condition: IsEnableCloudfrontLambda
    DependsOn:
    - BRConnectorLambdaUrl
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - Id: LambdaOrigin
            DomainName: !Select [2, !Split ["/", !GetAtt BRConnectorLambdaUrl.FunctionUrl]]
            CustomOriginConfig:
              HTTPPort: 80
              HTTPSPort: 443
              OriginProtocolPolicy: https-only
        Enabled: 'true'
        Comment: CloudFront distribution in front of EC2
        # ViewerProtocolPolicy: https-only
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
            - PUT
            - POST
            - PATCH
            - DELETE
          TargetOriginId: LambdaOrigin
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Disable caching https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html#managed-cache-policy-caching-disabled
          OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html#managed-origin-request-policy-all-viewer-except-host-header
          # ForwardedValues:
          #   QueryString: 'false'
          #   Cookies:
          #     Forward: none
          ViewerProtocolPolicy: redirect-to-https
        ViewerCertificate:
          CloudFrontDefaultCertificate: 'true'

  MySSMParameterFirstUserKey:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Join ["", ["/brconnector/first-user-key-", !Ref AWS::StackName]]
      Type: String
      Value: Hello World
      Description: A parameter created by CloudFormation and updated by ec2
      Tier: Standard
      DataType: text

  MySSMParameterAdminKey:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Join ["", ["/brconnector/admin-key-", !Ref AWS::StackName]]
      Type: String
      Value: Hello World
      Description: A parameter created by CloudFormation and updated by ec2
      Tier: Standard
      DataType: text

  MySSMParameterPGDB:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Join ["", ["/brconnector/pgdb-", !Ref AWS::StackName]]
      Type: String
      Value: !If [IsStandaloneDB, !GetAtt MyPG.Endpoint.Address, "172.17.0.1"]
      Description: A parameter created by CloudFormation and updated by RDS PostgreSQL
      Tier: Standard
      DataType: text

  BRConnectorLambdaRole:
    Condition: IsLambda	
    Type: AWS::IAM::Role
    Properties:
      Tags:
        - Key: Environment
          Value: !Ref AWS::StackName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      ManagedPolicyArns:
      #- arn:aws:iam::aws:policy/AdministratorAccess
      - arn:aws:iam::aws:policy/AWSLambdaExecute
      - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
      - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
      Policies:
      - PolicyName: MyInlinePolicy-invokelambda
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Sid: InvokeLambda
            Action:
            - 'lambda:InvokeFunction'
            - 'lambda:InvokeFunctionUrl'
            Resource: '*'
      - PolicyName: MyInlinePolicy-bedrock
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Sid: InvokeModel
            Action:
            - 'bedrock:InvokeModelWithResponseStream'
            - 'bedrock:InvokeModel'
            Resource: 'arn:aws:bedrock:*::foundation-model/*'
          - Effect: Allow
            Sid: ListFM
            Action:
            - 'bedrock:ListFoundationModels'
            Resource: '*'
      - PolicyName: MyInlinePolicy-ssm-parameter
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Sid: SSMPutParameter
            Action:
            - 'ssm:PutParameter'
            - 'ssm:GetParameter'
            - 'ssm:GetParameters'
            Resource: 'arn:aws:ssm:*:*:parameter/*'
      - PolicyName: LambdaRDSAccess
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
          - Effect: Allow
            Action:
            - rds:Connect
            - rds:Describe*
            - rds:List*
            Resource: '*'

  # Lambda Function 
  BRConnectorLambda:
    Condition: IsLambda	
    DependsOn:
    - PrivateWaitCondition
    Type: AWS::Serverless::Function
    Properties:
      Architectures:
        - !FindInMap [ArchitectureMapping, !Ref LambdaArch, Architecture]
      ImageUri: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:${LambdaArch}" #!Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepo}-${AWS::StackName}:latest"
      PackageType: Image
      Policies:
        - AWSLambdaBasicExecutionRole 
      Role: !GetAtt BRConnectorLambdaRole.Arn
      MemorySize: 512
      Timeout: 60 
      Environment:
        Variables:
          ADMIN_API_KEY: !GetAtt MySSMParameterAdminKey.Value
          PGSQL_HOST: !GetAtt MyPG.Endpoint.Address
          PGSQL_DATABASE: !Ref PGDatabase
          PGSQL_USER: !Ref PGUser
          PGSQL_PASSWORD: !Ref PGPassword
      VpcConfig:
        SecurityGroupIds:
          - !Ref InstanceSecurityGroup
        SubnetIds:
          - !Ref SubnetId

  BRConnectorLambdaVersion:
    Condition: IsLambda	
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref BRConnectorLambda
#      ProvisionedConcurrencyConfiguration:
#      ProvisionedConcurrentExecutions: 3

  BRConnectorLambdaPermission:
    Condition: IsLambda	
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref BRConnectorLambda
      Action: lambda:InvokeFunctionUrl
      Principal: "*"
      FunctionUrlAuthType: NONE

  BRConnectorLambdaUrl:
    Condition: IsLambda	
    Type: AWS::Lambda::Url
    Properties:
      TargetFunctionArn: !Ref BRConnectorLambda
      AuthType: NONE
      InvokeMode: RESPONSE_STREAM

  BedrockEndpoint:
    Condition: IsBREndpoint
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.bedrock-runtime'
      VpcEndpointType: Interface
      VpcId: !Ref VpcId
      SubnetIds: 
      - !Ref SubnetId
      SecurityGroupIds:
      - !Ref InstanceSecurityGroup
      PrivateDnsEnabled: true

  DeleteEC2Lambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: !Sub |
          import boto3,os

          def lambda_handler(event, context):
              ec2 = boto3.resource('ec2')
              instance_id = os.environ.get('INSTANCE_ID')
              instance = ec2.Instance(instance_id)
              instance.terminate()
              print(f"Terminated instance: {instance_id}")
      Handler: index.lambda_handler
      Role: !GetAtt DeleteEC2LambdaExecutionRole.Arn
      Runtime: python3.11
      MemorySize: 128
      Timeout: 60 
      Environment:
        Variables:
          INSTANCE_ID: ""

  DeleteEC2LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AmazonEC2FullAccess

# DeleteEC2InstancePermission:
#   Type: AWS::Lambda::Permission
#   Properties:
#     FunctionName: !GetAtt DeleteEC2Lambda.Arn
#     Action: 'lambda:InvokeFunction'
#     Principal: cloudformation.amazonaws.com

#  DeleteEC2CustomResource:
#    Condition: NotKeepEc2
#    DependsOn: 
#    - PrivateWaitCondition
#    Type: AWS::CloudFormation::CustomResource
#    Properties:
#      ServiceToken: !GetAtt DeleteEC2Lambda.Arn
#      InstanceId: !Ref MyEC2Instance

Outputs:
  CloudFrontEc2URL:
    Condition: IsEnableCloudfrontEc2
    Description: CloudFront URL
    Value: 
      Fn::Join:
      - ''
      - - https://
        - Fn::GetAtt: 
          - MyCloudFrontDistribution
          - DomainName
  CloudFrontLambdaURL:
    Condition: IsEnableCloudfrontLambda
    Description: CloudFront URL
    Value: 
      Fn::Join:
      - ''
      - - https://
        - Fn::GetAtt: 
          - MyCloudFrontLambda
          - DomainName
  MyPGEndpoint:
    Condition: IsStandaloneDB
    Description: The Endpoint of the RDS PostgreSQL
    Value: !GetAtt MyPG.Endpoint.Address
  EC2InstanceId:
    Description: The ID of the EC2 instance
    Value: !Ref MyEC2Instance
  MySSMParameterFirstUserKey:
    Description: first user key
    Value: !GetAtt MySSMParameterFirstUserKey.Value
  MySSMParameterAdminKey:
    Description: admin key
    Value: !GetAtt MySSMParameterAdminKey.Value
  MyFunctionUrl:
    Condition: IsLambda
    Description: lambda url
    Value: !GetAtt BRConnectorLambdaUrl.FunctionUrl
