AWSTemplateFormatVersion: 2010-09-09
Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: 'AWS::EC2::KeyPair::KeyName'
    ConstraintDescription: Can contain only ASCII characters.

  SSHLocation:
    Description: The IP address range that can be used to SSH to the EC2 instances
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 0.0.0.0/0
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x

  DocDBUsername:
    Type: String
    Description: Username for the Amazon DocumentDB cluster 

  DocDBPassword:
    Type: String
    Description: Password for the Amazon DocumentDB cluster
    NoEcho: true
    MinLength: 8

  DocDBInstanceType:
    Type: String
    Description: Instance type for DocumentDB cluster
    Default: db.t3.medium
    AllowedValues:
      - db.t3.medium
      - db.r5.large
      - db.r5.xlarge
      - db.r5.2xlarge
      - db.r5.4xlarge
      - db.r5.12xlarge
      - db.r5.24xlarge
      
  EC2InstanceType:
    Type: String
    Description: Instance type for EC2 instance
    Default: m5.large
    AllowedValues:
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5.4xlarge
      - m5.8xlarge
      - m5.12xlarge
      - m5.16xlarge
      - m5.24xlarge

  MongoDBUsername:
    Type: String
    Description: Username for the MongoDB database
  
  MongoDBPassword:
    Type: String
    Description: Password for the MongoDB database
    NoEcho: true
    MinLength: 8
  

Metadata:
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      - 
        Label: 
          default: "DocumentDB Configuration"
        Parameters: 
          - DocDBInstanceType
          - DocDBUsername
          - DocDBPassword
      - 
        Label: 
          default: "EC2 Configuration"
        Parameters: 
          - EC2InstanceType
          - KeyName
          - SSHLocation
      -
        Label:
          default: "MongoDB Configuration"
        Parameters:
          - MongoDBUsername
          - MongoDBPassword

Mappings:
  SubnetConfig:
    VPC:
      CIDR: 10.0.0.0/16
    PublicOne:
      CIDR: 10.0.0.0/24
    PrivateOne:
      CIDR: 10.0.1.0/24
    PrivateTwo:
      CIDR: 10.0.2.0/24
    PrivateThree:
      CIDR: 10.0.3.0/24
  RegionAMI:
    ap-northeast-1:
      "AMI": "ami-0cc75a8978fbbc969"
    ap-northeast-2:
      "AMI": "ami-0bd7691bf6470fe9c"
    ap-south-1:
      "AMI": "ami-0ebc1ac48dfd14136"
    ap-southeast-1:
      "AMI": "ami-0cd31be676780afa7"
    ap-southeast-2:
      "AMI": "ami-0ded330691a314693"
    ca-central-1:
      "AMI": "ami-013d1df4bcea6ba95"
    eu-central-1:
      "AMI": "ami-0c115dbd34c69a004"
    eu-west-1:
      "AMI": "ami-07d9160fa81ccffb5"
    eu-west-2:
      "AMI": "ami-0a13d44dccf1f5cf6"
    eu-west-3:
      "AMI": "ami-093fa4c538885becf"
    us-east-1:
      "AMI": "ami-02354e95b39ca8dec"
    us-east-2:
      "AMI": "ami-07c8bc5c1ce9598c3"
    us-west-2:
      "AMI": "ami-0873b46c45c11058d"
Resources:
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      CidrBlock: !FindInMap 
        - SubnetConfig
        - VPC
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-VPC
  PublicSubnetOne:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select 
        - 0
        - !GetAZs 
          Ref: 'AWS::Region'
      VpcId: !Ref VPC
      CidrBlock: !FindInMap 
        - SubnetConfig
        - PublicOne
        - CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-PublicSubnet
  PrivateSubnetOne:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select 
        - 0
        - !GetAZs 
          Ref: 'AWS::Region'
      VpcId: !Ref VPC
      CidrBlock: !FindInMap 
        - SubnetConfig
        - PrivateOne
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-PrivateSubnetOne
  PrivateSubnetTwo:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select 
        - 1
        - !GetAZs 
          Ref: 'AWS::Region'
      VpcId: !Ref VPC
      CidrBlock: !FindInMap 
        - SubnetConfig
        - PrivateTwo
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-PrivateSubnetTwo
  PrivateSubnetThree:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select 
        - 2
        - !GetAZs 
          Ref: 'AWS::Region'
      VpcId: !Ref VPC
      CidrBlock: !FindInMap 
        - SubnetConfig
        - PrivateThree
        - CIDR
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-PrivateSubnetThree
  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
  GatewayAttachement:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
  PublicRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: GatewayAttachement
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicSubnetOneRouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnetOne
      RouteTableId: !Ref PublicRouteTable
  PrivateRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
  PrivateSubnetOneRouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnetOne
  PrivateSubnetTwoRouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnetTwo
  PrivateSubnetThreeRouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnetThree

# DocumentDB
  DocumentDBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: Amazon DocumentDB Security Group
      GroupName: !Sub ${AWS::StackName}-SG-DocumentDB
      VpcId: !Ref VPC
      SecurityGroupIngress:
         - IpProtocol: tcp
           CidrIp: 10.0.0.0/16
           FromPort: 27017
           ToPort: 27017
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-SG-DocumentDB
 
  DocumentDBSubnetGroup:
    Type: AWS::DocDB::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: Subnet Group for DocumentDB
      DBSubnetGroupName: !Sub ${AWS::StackName}-SG-DocumentDB
      SubnetIds: 
         - !Ref PrivateSubnetOne
         - !Ref PrivateSubnetTwo
         - !Ref PrivateSubnetThree
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-SG-DocumentDB

  DocumentDBCluster:  
    Type: AWS::DocDB::DBCluster
    Properties: 
      DBClusterIdentifier: !Sub ${AWS::StackName}-DocumentDB
      MasterUsername: !Ref DocDBUsername
      MasterUserPassword: !Ref DocDBPassword
      DBSubnetGroupName : !Ref DocumentDBSubnetGroup
      StorageEncrypted: yes
      Tags: 
        - Key: Name
          Value: !Sub ${AWS::StackName}-DocumentDB
      VpcSecurityGroupIds: 
        - !Ref DocumentDBSecurityGroup
    DependsOn: VPC

  DocumentDBInstance1: 
    Type: AWS::DocDB::DBInstance
    Properties: 
      DBClusterIdentifier: !Ref DocumentDBCluster
      DBInstanceClass: !Ref DocDBInstanceType
      Tags: 
        - Key: Name
          Value: !Sub ${AWS::StackName}-DocumentDBInstance1

  DocumentDBInstance2: 
    Type: AWS::DocDB::DBInstance
    Properties: 
      DBClusterIdentifier: !Ref DocumentDBCluster
      DBInstanceClass: !Ref DocDBInstanceType
      Tags: 
        - Key: Name
          Value: !Sub ${AWS::StackName}-DocumentDBInstance2

  DocumentDBInstance3: 
    Type: AWS::DocDB::DBInstance
    Properties: 
      DBClusterIdentifier: !Ref DocumentDBCluster
      DBInstanceClass: !Ref DocDBInstanceType
      Tags: 
        - Key: Name
          Value: !Sub ${AWS::StackName}-DocumentDBInstance3

  DocDBSecret:
    Type: 'AWS::SecretsManager::Secret'
    Properties:
      Name: !Sub ${AWS::StackName}-DocDBSecret
      Description: This secret has the credentials for the DocumentDB cluster
      SecretString:
        !Join
          - '' 
          - - '{"username":"'
            - !Ref DocDBUsername
            - '","password":"'
            - !Ref DocDBPassword
            - '", "ssl": true}'
  
  SecretDocDBClusterAttachment:
    Type: AWS::SecretsManager::SecretTargetAttachment
    Properties:
      SecretId: !Ref DocDBSecret
      TargetId: !Ref DocumentDBCluster
      TargetType: AWS::DocDB::DBCluster

# EC2 
  ClientInstanceSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Enable SSH access via port 22
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHLocation

  ClientEC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref KeyName
      IamInstanceProfile: !Ref EC2InstanceProfile
      AvailabilityZone: !Select 
        - 0
        - !GetAZs 
          Ref: 'AWS::Region'
      SubnetId: !Ref PublicSubnetOne
      SecurityGroupIds:
        - !Ref ClientInstanceSecurityGroup
        - !Ref DocumentDBSecurityGroup
      ImageId: !FindInMap 
        - RegionAMI
        - !Ref 'AWS::Region'
        - AMI
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-ClientInstance
      UserData: 
        Fn::Base64:
          !Sub |
            #!/bin/bash
            yum update -y 
            yum install python3.7 -y
            yum install java-1.8.0-openjdk-devel -y
            cd /home/ec2-user
            mkdir /home/ec2-user/setup
            cd /home/ec2-user/setup
            wget https://bootstrap.pypa.io/get-pip.py
            sudo python3.7 get-pip.py
            sudo python3.7 -m pip install boto3 pymongo
            echo "downloading mongondb-org 4.0 packages from https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/RPMS/ ..."  >>/tmp/mongosetup.log
            wget https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/RPMS/mongodb-org-4.0.21-1.amzn1.x86_64.rpm
            wget https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/RPMS/mongodb-org-server-4.0.21-1.amzn1.x86_64.rpm
            wget https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/RPMS/mongodb-org-shell-4.0.21-1.amzn1.x86_64.rpm
            wget https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/RPMS/mongodb-org-mongos-4.0.21-1.amzn1.x86_64.rpm
            wget https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/RPMS/mongodb-org-tools-4.0.21-1.amzn1.x86_64.rpm
            echo "running rpm for mongodb-org installation..."  >>/tmp/mongosetup.log
            rpm -Uvh mongodb-org-tools-4.0.21-1.amzn1.x86_64.rpm >>/tmp/mongosetup.log
            rpm -Uvh mongodb-org-mongos-4.0.21-1.amzn1.x86_64.rpm >>/tmp/mongosetup.log
            rpm -Uvh mongodb-org-server-4.0.21-1.amzn1.x86_64.rpm >>/tmp/mongosetup.log
            rpm -Uvh mongodb-org-shell-4.0.21-1.amzn1.x86_64.rpm >>/tmp/mongosetup.log
            rpm -Uvh mongodb-org-4.0.21-1.amzn1.x86_64.rpm >>/tmp/mongosetup.log
            echo "starting mongo service... " >>/tmp/mongosetup.log
            service mongod start >>/tmp/mongosetup.log
            chkconfig mongod on >>/tmp/mongosetup.log
            echo "db = db.getSiblingDB('admin') " | tee -a file.js
            echo "db.createUser({ user: \"${MongoDBUsername}\" , pwd: \"${MongoDBPassword}\", roles: [{ role : \"read\", db : \"local\"  },\"userAdminAnyDatabase\", \"dbAdminAnyDatabase\", \"readWriteAnyDatabase\", \"clusterManager\", \"clusterAdmin\"]})"  | tee -a file.js
            echo "complete user creation and permissions"  >>/tmp/mongosetup.log
            mongo file.js >>/tmp/mongosetup.log
            sed -i 's/127.0.0.1/0.0.0.0/g' /etc/mongod.conf
            sed -i '/#security:/d' /etc/mongod.conf
            echo "security:" | tee -a /etc/mongod.conf
            echo " authorization: 'enabled'" | tee -a /etc/mongod.conf
            service mongod restart
            echo "db = db.getSiblingDB('admin') " | tee file2.js
            echo "rs.initiate( {_id : \"rs0\",members: [{ _id: 0, host: \"localhost:27017\" }]})" | tee -a file2.js
            echo "rs.conf()" | tee -a file2.js
            echo "setup replication configuration..."  >>/tmp/mongosetup.log
            sed -i '/#replication:/d' /etc/mongod.conf
            echo "replication:" | tee -a /etc/mongod.conf
            echo "   replSetName: "rs0"" | tee -a /etc/mongod.conf
            echo "restarting mongo service with replicaset.. " >>/tmp/mongosetup.log
            service mongod restart >>/tmp/mongosetup.log
            echo "setup replication..."  >>/tmp/mongosetup.log
            mongo mongodb://${MongoDBUsername}:${MongoDBPassword}@localhost/admin file2.js  >>/tmp/mongosetup.log
            cd /home/ec2-user
            wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

  EC2Role:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: ''
            Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: 'sts:AssumeRole'
      Path: /
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AmazonS3FullAccess'
      Policies: 
        - 
          PolicyName: !Sub ${AWS::StackName}-DocumentDBSecretPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - 
                Effect: Allow
                Action:
                  - secretsmanager:GetSecretValue
                Resource: !Ref DocDBSecret

  EC2InstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      InstanceProfileName: !Sub ${AWS::StackName}-EC2Profile
      Roles:
        - !Ref EC2Role

Outputs:
  VPCId:
    Description: The ID of the VPC created
    Value: !Ref VPC
  PublicSubnetOne:
    Description: The name of the public subnet created
    Value: !Ref PublicSubnetOne
  PrivateSubnetOne:
    Description: The ID of private subnet one created
    Value: !Ref PrivateSubnetOne
  PrivateSubnetTwo:
    Description: The ID of private subnet two created
    Value: !Ref PrivateSubnetTwo
  PrivateSubnetThree:
    Description: The ID of private subnet three created
    Value: !Ref PrivateSubnetThree
  ClientEC2InstancePublicDNS:
    Description: The Public DNS for the MirrorMaker EC2 instance
    Value: !GetAtt 
      - ClientEC2Instance
      - PublicDnsName
  DocumentDBClusterName:
    Value: !Ref DocumentDBCluster
  DocumentDBCluster:
    Value: !GetAtt DocumentDBCluster.Endpoint
  DocDBSecret:
    Value: !Ref DocDBSecret
