AWSTemplateFormatVersion: 2010-09-09
Resources:
  EC2VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: ocdtracker-api

  EC2InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Name
          Value: ocdtracker-api

  EC2VPCGatewayAttachment:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      InternetGatewayId: !Ref EC2InternetGateway
      VpcId: !Ref EC2VPC

  EC2Subnet1:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: eu-west-1a
      CidrBlock: 10.0.32.0/20
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: ocdtracker-api
      VpcId: !Ref EC2VPC

  EC2Subnet2:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: eu-west-1b
      CidrBlock: 10.0.0.0/20
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: ocdtracker-api
      VpcId: !Ref EC2VPC

  EC2Subnet3:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: eu-west-1c
      CidrBlock: 10.0.16.0/20
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: ocdtracker-api
      VpcId: !Ref EC2VPC

  EC2RouteTablePublic:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      Tags:
        - Key: Name
          Value: ocdtracker-api
      VpcId: !Ref EC2VPC

  EC2RoutePublic:
    Type: 'AWS::EC2::Route'
    DependsOn: EC2VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref EC2RouteTablePublic
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref EC2InternetGateway

  EC2SubnetRouteTableAssociationPublicSubnet1:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref EC2RouteTablePublic
      SubnetId: !Ref EC2Subnet1

  EC2SubnetRouteTableAssociationPublicSubnet2:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref EC2RouteTablePublic
      SubnetId: !Ref EC2Subnet2

  EC2SubnetRouteTableAssociationPublicSubnet3:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref EC2RouteTablePublic
      SubnetId: !Ref EC2Subnet3

  EC2SecurityGroupWebServer:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Allow SSH and 8080 access from anywhere
      GroupName: ocdtracker-api
      SecurityGroupEgress:
        - IpProtocol: '-1'
          CidrIp: 0.0.0.0/0
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: ocdtracker-api
      VpcId: !Ref EC2VPC

  RDSDBSubnetGroup:
    Type: 'AWS::RDS::DBSubnetGroup'
    Properties:
      DBSubnetGroupDescription: AWS::RDS::DBSubnetGroup
      SubnetIds:
        - !Ref EC2Subnet1
        - !Ref EC2Subnet2
        - !Ref EC2Subnet3
      Tags:
        - Key: Name
          Value: ocdtracker-api

  SecretsManagerSecretTargetAttachment:
    Type: 'AWS::SecretsManager::SecretTargetAttachment'
    Properties:
      SecretId: !Ref SecretsManagerSecretPostgresCreds
      TargetId: !Ref RDSDBInstance
      TargetType: 'AWS::RDS::DBInstance'

  RDSDBInstance:
    Type: 'AWS::RDS::DBInstance'
    Properties:
      AllocatedStorage: 20
      AvailabilityZone: eu-west-1a
      BackupRetentionPeriod: 2
      CACertificateIdentifier: rds-ca-2019
      CopyTagsToSnapshot: true
      DBInstanceClass: db.t3.micro
      DBInstanceIdentifier: ocdtracker-api
      DBName: ocdtracker
      DBSubnetGroupName: !Ref RDSDBSubnetGroup
      DeleteAutomatedBackups: true
      Engine: postgres
      EngineVersion: 14.4
      MasterUsername: !Sub '{{resolve:secretsmanager:${SecretsManagerSecretPostgresCreds}::username}}'
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${SecretsManagerSecretPostgresCreds}::password}}'
      MaxAllocatedStorage: 25
      PreferredBackupWindow: '23:03-23:33'
      PreferredMaintenanceWindow: 'tue:03:19-tue:03:49'
      PubliclyAccessible: true
      Tags:
        - Key: Name
          Value: ocdtracker-api
      VPCSecurityGroups:
        - !GetAtt EC2SecurityGroupPostgres.GroupId

  EC2SecurityGroupPostgres:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Allow Postgres access
      SecurityGroupEgress:
        - IpProtocol: '-1'
          CidrIp: 0.0.0.0/0
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: ocdtracker-api
      VpcId: !Ref EC2VPC

  EC2Instance:
    Type: 'AWS::EC2::Instance'
    DependsOn: RDSDBInstance
    Properties:
      AvailabilityZone: eu-west-1a
      IamInstanceProfile: !Ref IAMInstanceProfile
      ImageId: ami-089950bc622d39ed8
      InstanceType: t2.micro
      PropagateTagsToVolumeOnCreation: true
      SecurityGroupIds:
        - !Ref EC2SecurityGroupWebServer
      SubnetId: !Ref EC2Subnet1
      Tags:
        - Key: Name
          Value: ocdtracker-api
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          amazon-linux-extras install docker -y
          service docker start
          usermod -aG docker ec2-user
          chmod 666 /var/run/docker.sock
          systemctl enable docker
          AWS_ACCOUNT_ID=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | awk -F'"' '/"accountId"/ { print $4 }')
          AWS_REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | grep region | awk -F\" '{print $4}')
          REPO_URL=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/ocdtracker-api
          aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $REPO_URL
          IMAGE=$REPO_URL:latest
          docker pull $IMAGE
          if [ $? -eq 1 ]; then
            yum install git -y
            mkdir ocdtracker-api
            cd ocdtracker-api
            git clone https://github.com/cecobask/ocdtracker-api.git .
            docker build -t $IMAGE .
            docker push $IMAGE
          fi
          docker run --name ocdtracker-api -d -p 8080:8080 $IMAGE

  IAMInstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      InstanceProfileName: ocdtracker-api
      Path: /
      Roles:
        - !Ref IAMRoleOCDTrackerAPI

  IAMRoleOCDTrackerAPI:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: 'sts:AssumeRole'
      MaxSessionDuration: 3600
      Path: /
      RoleName: ocdtracker-api
      Tags:
        - Key: Name
          Value: ocdtracker-api

  IAMRoleGithubActions:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Federated: !GetAtt IAMOIDCProviderGithubActions.Arn
            Action: 'sts:AssumeRoleWithWebIdentity'
            Condition:
              StringEquals:
                'token.actions.githubusercontent.com:sub': 'repo:cecobask/ocdtracker-api:ref:refs/heads/main'
                'token.actions.githubusercontent.com:aud': sts.amazonaws.com
      MaxSessionDuration: 3600
      Path: /
      RoleName: github-actions
      Tags:
        - Key: Name
          Value: ocdtracker-api

  SecretsManagerSecretPostgresCreds:
    Type: 'AWS::SecretsManager::Secret'
    Properties:
      GenerateSecretString:
        ExcludeCharacters: '"@/\'
        GenerateStringKey: password
        PasswordLength: 32
        RequireEachIncludedType: true
        SecretStringTemplate: '{"username": "postgres"}'
      Name: postgres-creds
      Tags:
        - Key: Name
          Value: ocdtracker-api

  IAMPolicyOCDTrackerAPI:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: 'ecr:GetAuthorizationToken'
            Resource: '*'
          - Effect: Allow
            Action:
              - 'secretsmanager:GetSecretValue'
            Resource:
              - !Ref SecretsManagerSecretPostgresCreds
          - Effect: Allow
            Action:
              - 'ecr:BatchGetImage'
              - 'ecr:BatchCheckLayerAvailability'
              - 'ecr:CompleteLayerUpload'
              - 'ecr:GetDownloadUrlForLayer'
              - 'ecr:InitiateLayerUpload'
              - 'ecr:PutImage'
              - 'ecr:UploadLayerPart'
            Resource:
              - !GetAtt ECRRepository.Arn
          - Effect: Allow
            Action:
              - 's3:GetObject'
            Resource:
              - 'arn:aws:s3:::ocdtracker-api/google-app-creds.json'
      PolicyName: ocdtracker-api
      Roles:
        - !Ref IAMRoleOCDTrackerAPI

  IAMPolicyGithubActions:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - 'ecr:GetDownloadUrlForLayer'
              - 'ecr:BatchGetImage'
              - 'ecr:CompleteLayerUpload'
              - 'ecr:UploadLayerPart'
              - 'ecr:InitiateLayerUpload'
              - 'ecr:BatchCheckLayerAvailability'
              - 'ecr:PutImage'
            Resource: !GetAtt ECRRepository.Arn
          - Effect: Allow
            Action: 'ecr:GetAuthorizationToken'
            Resource: '*'
      PolicyName: github-actions
      Roles:
        - !Ref IAMRoleGithubActions

  IAMOIDCProviderGithubActions:
    Type: 'AWS::IAM::OIDCProvider'
    Properties:
      ClientIdList:
        - sts.amazonaws.com
      Tags:
        - Key: Name
          Value: ocdtracker-api
      ThumbprintList:
        - 6938fd4d98bab03faadb97b34396831e3780aea1
      Url: https://token.actions.githubusercontent.com

  ECRRepository:
    Type: 'AWS::ECR::Repository'
    Properties:
      EncryptionConfiguration:
        EncryptionType: AES256
      ImageScanningConfiguration:
        ScanOnPush: true
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 1,
                "description": "remove untagged images",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "imageCountMoreThan",
                  "countNumber": 1
                },
                "action": {
                  "type": "expire"
                }
              }
            ]
          }
      RepositoryName: ocdtracker-api