AWSTemplateFormatVersion: "2010-09-09"
Description: "DBTop Monitoring Solution"
Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      - 
        Label: 
          default: "Application Update - Disclaimer"
        Parameters:
          - ApplicationUpdateEnabled
          - ApplicationUpdateUrl

      - 
        Label: 
          default: "General Configuration"
        Parameters:
          - Username
          - AMIId
          - InstanceType
          - GitHubRepository

      - 
        Label: 
          default: "Network Configuration"
        Parameters:
          - VPCParam
          - SubnetParam
          - PublicAccess
          - SGInboundAccess
      
    ParameterLabels:
      Username: 
        default: "Specify username for application access, temporary credentials will be sent by email."
      AMIId: 
        default: "Specify AWS Linux AMI to be used for Application Deployment."
      InstanceType: 
        default: "Specify instance type for Application Deployment."
      VPCParam: 
        default: "Select VPC for Application Deployment."
      SubnetParam: 
        default: "Select Subnet for Application Deployment, this subnet needs internet outbound access to reach AWS APIs."
      PublicAccess: 
        default: "The deployment will assign private IP Address by default to access the application, you can assign Public IP Address to access the application in case you need it, Select (true) to assign Public IP Address."
      SGInboundAccess: 
        default: "Specify CIDR inbound access rule, this will grant network access for the application."
      GitHubRepository:
        default: "AWS Github Repository source used for deployment."
      ApplicationUpdateEnabled:
        default: "Disclaimer : This Application could able to verify new versions, it will help to keep update with most popular features. Customer can review the code and validate data scope. Selecting (true) I acknowledge that this Application will access to url to verify new versions."
      ApplicationUpdateUrl:
        default: "URL used to verify new application versions."
      
        
Parameters:

  VPCParam:
      Type: AWS::EC2::VPC::Id
      Description: Select VPC

  SubnetParam:
    Type: AWS::EC2::Subnet::Id
    Description: Select Subnet

  AMIId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Description: AWS AMI
    Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64'

  Username:
      Type: String
      Description: Username (email)
      AllowedPattern: "\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}"

  InstanceType:
      Type: String
      Description: InstanceType
      Default: t3a.medium
      AllowedValues:
      - t3a.micro
      - t3a.small
      - t3a.medium
      - t3a.large
      - t3a.xlarge
  PublicAccess:
      Type: String
      Description: Assign Public IP Address
      Default: "false"
      AllowedValues:
      - "true"
      - "false"

  SGInboundAccess:
      Type: String
      Description: CIDR (0.0.0.0/0)
      
  GitHubRepository:
      Type: String
      Description: AWS Github Repository
      Default: aws-samples
  
  ApplicationUpdateUrl:
      Type: String
      Description: URL
      Default: https://version.code.ds.wwcs.aws.dev/
      
  ApplicationUpdateEnabled:
      Type: String
      Description: Option
      Default: "true"
      AllowedValues:
      - "true"
      - "false"
  
Conditions:
    isPublic: !Equals [ !Ref PublicAccess, true]
      
Resources:
    InstanceProfile: 
        Type: "AWS::IAM::InstanceProfile"
        DependsOn: IAMRoleEC2
        Properties: 
          Path: "/"
          Roles:  [!Ref IAMRoleEC2]
          
          
    IAMPolicyEc2:
        Type: AWS::IAM::ManagedPolicy
        Properties:
            ManagedPolicyName: !Join [ "-", ["policy-ec2-db-top-solution", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
            PolicyDocument: !Sub |
                {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "logs:GetLogEvents"
                            ],
                            "Resource": [
                                "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:RDSOSMetrics:log-stream:*"
                            ]
                        },
                        {
                            "Effect": "Allow",
                            "Action": [
                                "cloudwatch:GetMetricData"
                            ],
                            "Resource": "*"
                        },
                        {
                            "Effect": "Allow",
                            "Action": [
                                "rds:DescribeDBInstances"
                            ],
                            "Resource": "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:db:*"
                        },
                        {
                            "Effect": "Allow",
                            "Action": [
                                "rds:DescribeDBShardGroups"
                            ],
                            "Resource": "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:shard-group:*"                            
                         },
                         {
                            "Effect": "Allow",
                            "Action": [
                                "rds:DescribeDBClusters"
                            ],
                            "Resource": "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:cluster:*"
                        },
                        {
                            "Effect": "Allow",
                            "Action": [
                                "elasticache:DescribeReplicationGroups"
                            ],
                            "Resource": "arn:aws:elasticache:${AWS::Region}:${AWS::AccountId}:replicationgroup:*"
                        },
                        {
                            "Effect": "Allow",
                            "Action": [
                                "elasticache:DescribeServerlessCaches"
                            ],
                            "Resource": "arn:aws:elasticache:${AWS::Region}:${AWS::AccountId}:serverlesscache:*"
                        },
                        {
                			"Effect": "Allow",
                			"Action": "memorydb:DescribeClusters",
                			"Resource": "arn:aws:memorydb:${AWS::Region}:${AWS::AccountId}:cluster/*"
                		},
                		{
                            "Effect": "Allow",
                            "Action": "docdb-elastic:GetCluster",
                            "Resource": "arn:aws:docdb-elastic:${AWS::Region}:${AWS::AccountId}:cluster/*"
                        },
                        {
                            "Effect": "Allow",
                            "Action": "docdb-elastic:ListClusters",
                            "Resource": "*"
                        },
                        {
                			"Effect": "Allow",
                			"Action": "dynamodb:DescribeTable",
                			"Resource": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*"
                		},
                		{
                			"Effect": "Allow",
                			"Action": "dynamodb:ListTables",
                			"Resource": "*"
                		}
                    ]
                }
        
        
    IAMRoleEC2:
        Type: "AWS::IAM::Role"
        DependsOn: IAMPolicyEc2
        Properties:
            Path: "/"
            RoleName: !Join [ "-", ["role-ec2-db-top-solution", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
            AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
            MaxSessionDuration: 3600
            Description: "Allows EC2 instance to call AWS services on your behalf."
            ManagedPolicyArns:
                - !Ref IAMPolicyEc2
                - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
                
    IAMRoleCognito:
        Type: "AWS::IAM::Role"
        Properties:
            Path: "/"
            RoleName: !Join [ "-", ["role-cognito-db-top-solution", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
            AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"cognito-idp.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
            MaxSessionDuration: 3600
            Description: "Allows Cognito to use SMS MFA on your behalf."
            Policies:
            - PolicyName: "CognitoPolicy"
              PolicyDocument: 
                Version: "2012-10-17"
                Statement: 
                  - Effect: "Allow"
                    Action:
                      - "sns:publish"
                    Resource: "*"

    EC2Instance:
        Type: "AWS::EC2::Instance"
        DependsOn: [CognitoUserPool,IAMRoleEC2]
        Properties:
            ImageId: !Ref AMIId
            InstanceType: !Ref InstanceType
            Tenancy: "default"
            EbsOptimized: true
            SourceDestCheck: true
            BlockDeviceMappings: 
              - 
                DeviceName: "/dev/xvda"
                Ebs: 
                    Encrypted: false
                    VolumeSize: 20
                    VolumeType: "gp2"
                    DeleteOnTermination: true
            IamInstanceProfile: !Ref InstanceProfile
            NetworkInterfaces: 
              - AssociatePublicIpAddress: !Ref PublicAccess
                DeviceIndex: "0"
                GroupSet: 
                  - Ref: VPCSecurityGroup
                SubnetId: 
                  Ref: SubnetParam
            UserData:
                Fn::Base64: 
                  !Sub |
                    #!/bin/bash
                    sudo mkdir -p /aws/apps
                    
                    cd /tmp
                    sudo yum install -y git
                    git clone https://github.com/${GitHubRepository}/db-top-monitoring.git
                    cd db-top-monitoring
                    sudo cp -r server frontend conf /aws/apps
                    
                    echo '{ "aws_region": "${AWS::Region}","aws_cognito_user_pool_id": "${CognitoUserPool}","aws_cognito_user_pool_web_client_id": "${CognitoUserPoolClient}","aws_api_port": 3000,  "aws_token_expiration":24, "aws_application_update_url" : "${ApplicationUpdateUrl}", "aws_application_update_enabled" : "${ApplicationUpdateEnabled}" }' > /aws/apps/conf/aws-exports.json
                    cd /aws/apps
                    sudo -u ec2-user sh conf/setup.sh 2>&1 | tee /tmp/setup.log
                    
                    sudo sed -i 's/GitHubRepository/${GitHubRepository}/g' /aws/apps/conf/update.sh
       
            Tags: 
              - 
                Key: "Name"
                Value: !Join [ "-", ["ec2-db-top-solution", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
              
    
    VPCSecurityGroup:
        Type: "AWS::EC2::SecurityGroup"
        Properties:
            GroupDescription: !Join [ "_", ["sg_security_group_db_top_solution", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
            GroupName: !Join [ "_", ["sg_security_group_db_top_solution", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
            VpcId: !Ref VPCParam
            SecurityGroupIngress: 
                - 
                    CidrIp: !Ref SGInboundAccess
                    FromPort: 443
                    IpProtocol: "tcp"
                    ToPort: 443
                    
            SecurityGroupEgress: 
                - 
                    CidrIp: "0.0.0.0/0"
                    IpProtocol: "-1"


    CognitoUserPool:
        Type: "AWS::Cognito::UserPool"
        Properties:
            UserPoolName: !Join [ "-", ["AwsDbTopSolutionUserPool", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
            Policies: 
                PasswordPolicy: 
                    MinimumLength: 8
                    RequireUppercase: true
                    RequireLowercase: true
                    RequireNumbers: true
                    RequireSymbols: true
                    TemporaryPasswordValidityDays: 7
            LambdaConfig: {}
            AutoVerifiedAttributes: 
              - "email"
            UsernameAttributes: 
              - "email"
            MfaConfiguration: "OPTIONAL"
            SmsConfiguration: 
                SnsCallerArn: !GetAtt IAMRoleCognito.Arn 
                SnsRegion: !Ref AWS::Region
            EmailConfiguration: 
                EmailSendingAccount: "COGNITO_DEFAULT"
            AdminCreateUserConfig: 
                AllowAdminCreateUserOnly: true
            UserPoolTags: {}
            AccountRecoverySetting: 
                RecoveryMechanisms: 
                  - 
                    Priority: 1
                    Name: "verified_email"
            UsernameConfiguration: 
                CaseSensitive: false
            VerificationMessageTemplate: 
                DefaultEmailOption: "CONFIRM_WITH_CODE"

    CognitoUserPoolClient:
        Type: "AWS::Cognito::UserPoolClient"
        Properties:
            UserPoolId: !Ref CognitoUserPool
            ClientName: !Join [ "-", ["AwsDbTopSolutionUserPoolClient", !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
            RefreshTokenValidity: 1
            ReadAttributes: 
              - "address"
              - "birthdate"
              - "email"
              - "email_verified"
              - "family_name"
              - "gender"
              - "given_name"
              - "locale"
              - "middle_name"
              - "name"
              - "nickname"
              - "phone_number"
              - "phone_number_verified"
              - "picture"
              - "preferred_username"
              - "profile"
              - "updated_at"
              - "website"
              - "zoneinfo"
            WriteAttributes: 
              - "address"
              - "birthdate"
              - "email"
              - "family_name"
              - "gender"
              - "given_name"
              - "locale"
              - "middle_name"
              - "name"
              - "nickname"
              - "phone_number"
              - "picture"
              - "preferred_username"
              - "profile"
              - "updated_at"
              - "website"
              - "zoneinfo"
            ExplicitAuthFlows: 
              - "ALLOW_REFRESH_TOKEN_AUTH"
              - "ALLOW_USER_SRP_AUTH"
            PreventUserExistenceErrors: "ENABLED"
            AllowedOAuthFlowsUserPoolClient: false
            IdTokenValidity: 1440
            AccessTokenValidity: 1440
            TokenValidityUnits: 
                AccessToken: "minutes"
                IdToken: "minutes"
                RefreshToken: "days"

    CognitoUserPoolUser:
        Type: "AWS::Cognito::UserPoolUser"
        Properties:
            Username: !Ref Username
            UserPoolId: !Ref CognitoUserPool
            UserAttributes: 
              - 
                Name: "email_verified"
                Value: "true"
              - 
                Name: "email"
                Value: !Ref Username

Outputs:
    PublicAppURL:
        Description: Public Endpoint
        Value: !Join [ "", ["https://", !GetAtt EC2Instance.PublicIp]]
        Condition: isPublic
    PrivateAppURL:
        Description: Private Endpoint
        Value: !Join [ "", ["https://", !GetAtt EC2Instance.PrivateIp]]