AWSTemplateFormatVersion: 2010-09-09

Description: >-
  This CloudFormation creates the VPC, DMS, RDS resources required for demonstrating the
  automated serverless way to run DMS tasks for data migration

Parameters:
  OracleDatabaseName:
    Default: awsora
    Description: Oracle Database name
    Type: String
  OracleMasterUsername:
    Default: admin
    Description: Oracle MasterUsername
    Type: String
  OracleMasterPassword:
    Description: Oracle MasterPassword
    Type: String
    Default: admin123
    NoEcho: True
  OracleInstanceType:
    Description: Oracle DB instance type
    Type: String
    Default: db.t2.medium
  DBSnapshotIdentifier:
    Description: The RDS Oracle snapshot name to restore to the new DB instance. Make certain that the snapshot exists.
    Type: String
    Default: arn:aws:rds:us-east-1:215675202451:snapshot:dms-automation-blog-oracle-snapshot
  OracleDBAllocatedStorage:
    Description: The Oracle RDS allocated storage in GB
    Type: String
    Default: 50
  OracleDBInstanceIdentifier:
    Description: The RDS Oracle database instance identifier
    Type: String
    Default: awsorains
  AuroraDBEngineVersion:
    Type: String
    Description: This is the engine version for Aurora MySql DB
    Default: 5.6.10a
  AuroraInstanceType: 
    Type: String
    Description: This is DB Instance Class.
    Default: db.t2.medium
  AuroraInstanceIdentifier:
    Description: Aurora database instance identifier
    Type: String
    Default: awsaurorains
  AuroraDatabaseName:
    Default: awsaurora
    Description: Aurora MySql Database name
    Type: String
  AuroraMasterUsername:
    Default: admin
    Description: Aurora MasterUsername
    Type: String
  AuroraMasterPassword:
    Description: Aurora MasterPassword
    Type: String
    Default: admin123
    NoEcho: True
  AuroraClusterIdentifier:
    Default: awsauroracluster
    Description: Aurora MySql Database Cluster name
    Type: String
  DmsReplInstanceStorage:
    Default: 50
    Description: Replication Instance storage size
    Type: String
  DmsVpcRoleExists:
    Default: 'No'
    Type: String
    Description: Choose 'Yes' if dms-vpc-role is present and 'No' if you want to provision one
    AllowedValues:
      - 'Yes'
      - 'No'
  MigrationType:
    Type: String
    Description: Please choose between DMS replication task migration type
    Default: full-load
    AllowedValues:
      - full-load
      #- full-load-and-cdc
      #- cdc
  DmsReplInstanceEngineVersion:
    Default: 3.1.3
    Description: Replication Instance engine version
    Type: String
  DmsKMSKeyId:
    Type: String
    Description: Specify the Amazon Resource Name (ARN) of the AWS Key Management Service master key that is used to encrypt the content on the replication instance
  ReplicationInstanceClassIn:
    Default: dms.t2.medium
    Description: Replication Instance Class
    Type: String
  ReplicationInstanceIdentifier:
    Default: dms-blog-replication-instance
    Description: AWS DMS Replication Instance name
    Type: String
  ReplicationTaskIdentifier:
    Default: ora2aurmysql-copy
    Description: AWS DMS Replication Task name used for this blog
    Type: String
  ClientIP:
    Description: >-
      The IP address range that can be used to connect to the RDS instances and
      EC2 instance from your local machine.It must be a valid IP CIDR range of
      the form x.x.x.x/x.Pls get your address using checkip.amazonaws.com or
      whatsmyip.org
    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: >-
      It must be a valid IP CIDR range of the form x.x.x.x/x. Suggest to enable
      access to your IP address only. Pls get your address using
      checkip.amazonaws.com or whatsmyip.org.

Rules:
  SubnetsInVPC:
    Assertions:
      - Assert:
          'Fn::EachMemberIn':
            - 'Fn::ValueOfAll':
                - 'AWS::EC2::Subnet::Id'
                - VpcId
            - 'Fn::RefAll': 'AWS::EC2::VPC::Id'
        AssertDescription: All subnets must in the VPC

Metadata:
  'AWS::CloudFormation::Interface':
    ParameterGroups:
      - Label:
          default: Configuration to deploy the resources
        Parameters:
          - ClientIP
      - Label:
          default: Source RDS Oracle database configuration
        Parameters:
          - OracleDatabaseName
          - OracleMasterUsername
          - OracleMasterPassword
          - OracleInstanceType
          - DBSnapshotIdentifier
          - OracleDBAllocatedStorage
          - OracleDBInstanceIdentifier
      - Label:
          default: Target RDS MySql Aurora database configuration
        Parameters:
          - AuroraClusterIdentifier
          - AuroraDatabaseName
          - AuroraMasterUsername
          - AuroraMasterPassword
          - AuroraDBEngineVersion
          - AuroraInstanceIdentifier
          - AuroraInstanceType
      - Label:
          default: AWS DMS and migration specific configuration
        Parameters:
          - ReplicationInstanceIdentifier
          - DmsReplInstanceEngineVersion
          - DmsReplInstanceStorage
          - DmsKMSKeyId
          - ReplicationInstanceClassIn
          - ReplicationTaskIdentifier
          - DmsVpcRoleExists

Conditions:
  SkipDmsVpcRoleCreation:  !Equals [ !Ref DmsVpcRoleExists, 'No' ]

Resources:
  resVPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: DmsAutomationBlogVPC

  resPublicSubnetInAZ1A:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref resVPC
      AvailabilityZone: us-east-1a
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: 'True'
      Tags:
        - Key: Name
          Value: DmsAutomationBlogSubnetInAZ1A

  resPublicSubnetInAZ1B:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref resVPC
      AvailabilityZone: us-east-1b
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: 'True'
      Tags:
        - Key: Name
          Value: DmsAutomationBlogSubnetInAZ1B

  resInternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Name
          Value: DmsAutomationBlogInternetGateway

  resGatewayAttachment:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      InternetGatewayId: !Ref resInternetGateway
      VpcId: !Ref resVPC

  resPublicRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref resVPC

  resPublicRoute:
    Type: 'AWS::EC2::Route'
    Properties:
      RouteTableId: !Ref resPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref resInternetGateway
    DependsOn:
      - resGatewayAttachment

  resPublicSubnetRouteAssociation1A:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref resPublicRouteTable
      SubnetId: !Ref resPublicSubnetInAZ1A

  resPublicSubnetRouteAssociation1B:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref resPublicRouteTable
      SubnetId: !Ref resPublicSubnetInAZ1B

  resSGBase:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: ora2mysqlblogsecuritygroup
      GroupDescription: Base Security Group
      VpcId: !Ref resVPC
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          IpProtocol: '-1'
          FromPort: -1
          ToPort: -1
      SecurityGroupIngress:
        - IpProtocol: tcp
          CidrIp: !Ref ClientIP
          FromPort: '3306'
          ToPort: '3306'
        - IpProtocol: tcp
          CidrIp: !Ref ClientIP
          FromPort: '1521'
          ToPort: '1521'

  resSGBaseIngress:
    Type: 'AWS::EC2::SecurityGroupIngress'
    Properties:
      GroupId: !GetAtt 
        - resSGBase
        - GroupId
      IpProtocol: '-1'
      FromPort: '-1'
      ToPort: '-1'
      SourceSecurityGroupId: !GetAtt 
        - resSGBase
        - GroupId

  resOraDBSubnetGroup:
    Type: 'AWS::RDS::DBSubnetGroup'
    Properties:
      DBSubnetGroupDescription: Subnet available for the RDS DB Oracle Instance
      SubnetIds:
        - !Ref resPublicSubnetInAZ1A
        - !Ref resPublicSubnetInAZ1B
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

  resOracleDB:
    Type: 'AWS::RDS::DBInstance'
    Properties:
      DBName: !Ref OracleDatabaseName
      AllocatedStorage: !Ref OracleDBAllocatedStorage
      MasterUsername: !Ref OracleMasterUsername
      MasterUserPassword: !Ref OracleMasterPassword
      DBInstanceClass: !Ref OracleInstanceType
      DBInstanceIdentifier: !Ref OracleDBInstanceIdentifier
      Engine: oracle-ee
      LicenseModel: bring-your-own-license
      PubliclyAccessible: 'true'
      AvailabilityZone: us-east-1a
      MultiAZ: 'false'
      BackupRetentionPeriod: '7'
      DBSubnetGroupName: !Ref resOraDBSubnetGroup
      DBSnapshotIdentifier: !Ref DBSnapshotIdentifier
      VPCSecurityGroups:
        - !Ref resSGBase
      StorageType: gp2
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

  resAuroraDBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: Subnets available for the RDS Aurora MySql Instance
      SubnetIds: 
        - !Ref resPublicSubnetInAZ1A
        - !Ref resPublicSubnetInAZ1B
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

  resAuroraCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      DatabaseName: !Ref AuroraDatabaseName
      DBClusterIdentifier: !Ref AuroraClusterIdentifier
      MasterUsername: !Ref AuroraMasterUsername
      MasterUserPassword: !Ref AuroraMasterPassword
      Engine: aurora
      EngineVersion: !Ref AuroraDBEngineVersion 
      DBSubnetGroupName: !Ref resAuroraDBSubnetGroup
      VpcSecurityGroupIds: 
        - !Ref resSGBase
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

  resAuroraInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      DBSubnetGroupName: !Ref resAuroraDBSubnetGroup
      Engine: aurora
      DBInstanceIdentifier: !Ref AuroraInstanceIdentifier
      DBClusterIdentifier: !Ref resAuroraCluster
      PubliclyAccessible: 'true'
      DBInstanceClass: !Ref AuroraInstanceType
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

  resDMSVPCRole:
    Condition: SkipDmsVpcRoleCreation
    Type: AWS::IAM::Role
    Properties:
      RoleName: 'dms-vpc-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - dms.amazonaws.com 
          Action:
          - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole

  resDMSReplctSbntGrp:
    Type: AWS::DMS::ReplicationSubnetGroup
    Properties:
      ReplicationSubnetGroupDescription: Subnets available for the DMS Replication Instance
      SubnetIds: 
        - !Ref resPublicSubnetInAZ1A
        - !Ref resPublicSubnetInAZ1B
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

  resDMSReplctInstnc:
    Type: AWS::DMS::ReplicationInstance
    Properties:
      PubliclyAccessible: false
      ReplicationInstanceClass: !Ref ReplicationInstanceClassIn
      ReplicationInstanceIdentifier: !Ref ReplicationInstanceIdentifier
      KmsKeyId: !Ref DmsKMSKeyId
      MultiAZ: true
      EngineVersion: !Ref DmsReplInstanceEngineVersion
      AllocatedStorage: !Ref DmsReplInstanceStorage
      ReplicationSubnetGroupIdentifier: !Ref resDMSReplctSbntGrp
      VpcSecurityGroupIds:
        - !Ref resSGBase
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

  resOnPremOracleEndPntSrc:
    Type: AWS::DMS::Endpoint
    DependsOn: resDMSReplctInstnc
    Properties:
      EndpointIdentifier: "dms-blog-onpremise-oracle-endpoint"
      EngineName: oracle
      DatabaseName: !Ref OracleDatabaseName
      EndpointType: "source"
      Username: !Ref OracleMasterUsername
      Password: !Ref OracleMasterPassword
      ServerName: !GetAtt resOracleDB.Endpoint.Address
      Port: 1521

  resCloudMySqlEndPntTgt:
    Type: AWS::DMS::Endpoint
    DependsOn: resDMSReplctInstnc
    Properties:
      EndpointIdentifier: "dms-blog-cloud-aurmysql-endpoint"
      EngineName: aurora
      DatabaseName: !Ref AuroraDatabaseName
      EndpointType: "target"
      Username: !Ref AuroraMasterUsername
      Password: !Ref AuroraMasterPassword
      ServerName: !GetAtt resAuroraCluster.Endpoint.Address
      Port: 3306

  resDMSReplicationTask:
    Type: AWS::DMS::ReplicationTask
    Properties:
      ReplicationTaskIdentifier: !Ref ReplicationTaskIdentifier
      SourceEndpointArn: !Ref resOnPremOracleEndPntSrc
      TargetEndpointArn: !Ref resCloudMySqlEndPntTgt
      ReplicationInstanceArn: !Ref resDMSReplctInstnc
      MigrationType: !Ref MigrationType
      ReplicationTaskSettings: "{
          \"ValidationSettings\":   {
            \"EnableValidation\":true
          },
          \"Logging\": {
            \"EnableLogging\": true
          }
      }"
      TableMappings: "{
          \"rules\": [
              {
                  \"rule-type\": \"selection\",
                  \"rule-id\": \"1\",
                  \"rule-name\": \"1\",
                  \"object-locator\": {
                      \"schema-name\": \"ADMIN\",
                      \"table-name\": \"EMPLOYEES\"
                  },
                  \"rule-action\": \"include\"
              }
          ]
      }"
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'

Outputs:
  StackName:
    Description: Stack name
    Value: !Ref 'AWS::StackName'
  VPCID:    
    Description: VPC ID Created For the Blog
    Value: !Ref resVPC
  SubnetID1A:    
    Description: Subnet ID created in Availabilty Zone 1A
    Value: !Ref resPublicSubnetInAZ1A
  SubnetID1B:    
    Description: Subnet ID created in Availabilty Zone 1B
    Value: !Ref resPublicSubnetInAZ1B
  Regionname:
    Description: Region name
    Value: !Ref 'AWS::Region'
  SecurityGroupId:    
    Description: Security Group that was created and assigned to all components.
    Value: !GetAtt 
      - resSGBase
      - GroupId
  AuroraClusterEndpoint:    
    Description: Aurora MySql Cluster Endpoint Address (target datastore)
    Value: !GetAtt resAuroraCluster.Endpoint.Address
  OracleEndPoint:    
    Description: RDS Oracle Endpoint Address (source datastore)
    Value: !GetAtt 
      - resOracleDB
      - Endpoint.Address
  DMSReplctInstncArn:    
    Description: Replication instance ARN
    Value: !Ref resDMSReplctInstnc
  DMSOracleEndPntArn:    
    Description: DMS Endpoint ARN for Source DB 
    Value: !Ref resOnPremOracleEndPntSrc
  DMSAurMySqlEndPntArn:    
    Description: DMS Endpoint ARN for Target DB
    Value: !Ref resCloudMySqlEndPntTgt
  ReplicationTaskIdentifier:
    Description: Provisioned Replication task identifier
    Value: !Ref ReplicationTaskIdentifier
    Export:
      Name: ReplicationTaskIdentifier
