Architecture#

SqlServerDmsDiagram

Reference

  • AWS Database Migration Workshop
  • Developer Center
  • Database Freedom

Customers

When to use?

  • Migrate to AWS and benefit
  • Modernize applications

Migration paths

  • Lift and shift
  • Replatforming
  • Refactoring

Essential concepts

Best practices

Microsft SQL in an EC2#

// security group
this.ec2SecurityGrroup = new aws_ec2.SecurityGroup(
this,
'SecurityGroupForEc2MySql',
{
securityGroupName: 'SecurityGroupForEc2MySql',
vpc: vpc
}
)

open port for RDP and db

this.ec2SecurityGrroup.addIngressRule(
// RDP access
aws_ec2.Peer.anyIpv4(),
aws_ec2.Port.tcp(1521)
)
this.ec2SecurityGrroup.addIngressRule(
// RDP access
aws_ec2.Peer.anyIpv4(),
aws_ec2.Port.tcp(3389)
)
this.ec2SecurityGrroup.addIngressRule(
// sql server access
aws_ec2.Peer.anyIpv4(),
aws_ec2.Port.tcp(1433)
)

role for ec2

// role for ec2
const role = new aws_iam.Role(this, 'RoleForEc2DbMigrationDemo', {
roleName: 'RoleForEc2DbMigrationDemo',
assumedBy: new aws_iam.ServicePrincipal('ec2.amazonaws.com')
})
role.addManagedPolicy(
aws_iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore')
)
role.addManagedPolicy(
aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
'AWSCloudFormationReadOnlyAccess'
)
)
role.attachInlinePolicy(
new aws_iam.Policy(this, 'PolicyForEc2DbMigrationDemo', {
policyName: 'PolicyForEc2DbMigrationDemo',
statements: [
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
actions: ['secretsmanager:GetSecretValue'],
resources: ['arn:aws:secretsmanager:*']
})
]
})
)

ec2 hosting Microsoft SQL by an AMI

// ec2 host MySQL
this.ec2 = new aws_ec2.Instance(this, 'Ec2HostMySQLDemo', {
instanceName: 'Ec2HostMySqlDemo',
keyName: props.keyPair,
instanceType: aws_ec2.InstanceType.of(
aws_ec2.InstanceClass.M5,
aws_ec2.InstanceSize.LARGE
),
blockDevices: [
{
deviceName: '/dev/sda1',
volume: aws_ec2.BlockDeviceVolume.ebs(250, {
deleteOnTermination: true,
iops: 2000,
volumeType: aws_ec2.EbsDeviceVolumeType.IO1
})
}
],
machineImage: aws_ec2.MachineImage.genericWindows({
'ap-southeast-1': props.amiImage
}),
vpc: vpc,
role: role,
securityGroup: this.ec2SecurityGrroup,
vpcSubnets: {
subnetType: aws_ec2.SubnetType.PUBLIC
}
})

Aurora MySQL target#

db security group

// db security group
this.dbSecurityGroup = new aws_ec2.SecurityGroup(this, 'SecurityGroupForDb', {
securityGroupName: 'SecurityGroupForDb',
vpc: vpc
})
this.dbSecurityGroup.addIngressRule(
aws_ec2.Peer.securityGroupId(this.ec2SecurityGrroup.securityGroupId),
aws_ec2.Port.tcp(3306)
)

db subnet group

// subnet group for aurora
const subnetGroup = new aws_rds.SubnetGroup(this, 'AuroraSubnetGroup', {
vpc: vpc,
removalPolicy: RemovalPolicy.DESTROY,
description: 'subnet group for aurora clsuter',
subnetGroupName: 'AuroraSubnetGroup',
vpcSubnets: {
subnetType: aws_ec2.SubnetType.PRIVATE_WITH_NAT
}
})

aurora cluster

// amazon qurora mysql
this.aurora = new aws_rds.DatabaseCluster(this, 'AuroraCluster', {
removalPolicy: RemovalPolicy.DESTROY,
defaultDatabaseName: props.dbName,
clusterIdentifier: 'AuroraClusterDemo',
engine: aws_rds.DatabaseClusterEngine.auroraMysql({
version: aws_rds.AuroraMysqlEngineVersion.VER_2_08_1
}),
// production: secrete manager
credentials: {
username: 'admin',
password: SecretValue.unsafePlainText('Password1')
},
instanceProps: {
instanceType: aws_ec2.InstanceType.of(
aws_ec2.InstanceClass.BURSTABLE2,
aws_ec2.InstanceSize.SMALL
),
vpcSubnets: {
subnets: vpc.privateSubnets.map(subnet => subnet)
},
vpc: vpc,
securityGroups: [this.dbSecurityGroup]
},
deletionProtection: false,
instances: 1,
subnetGroup: subnetGroup
})

DMS role#

export class DmsVpcRole extends Stack {
public readonly role: aws_iam.Role
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props)
this.role = new aws_iam.Role(this, 'DmsVpcRole', {
roleName: 'dms-vpc-role',
assumedBy: new aws_iam.ServicePrincipal('dms.amazonaws.com')
})
this.role.addManagedPolicy(
aws_iam.ManagedPolicy.fromManagedPolicyArn(
this,
'AmazonDMSVPCManagementRole',
'arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole'
)
)
new CfnOutput(this, 'TheDmsVpcRole', {
value: this.role.roleArn
})
}
}

SCT and schema conversion HERE#

  • Install the AWS SCT

  • Check

  • Create a DB migration project.

  • Connect to source database - mycrosoft - 1433 - dms_sample table

  • Review the migration assessment report

  • Connect to target database - arurora - primary endponit - 3306

  • Assert report view - action items - see issues

  • Modify the procedural code - action items - generateTransferAcvitity

PRINT (concat('max t: ',@max_tik_id,' min t: ', @min_tik_id, 'max p: ',@max_p_id,' min p: ', @min_p_id));

by this in 3 occurencs

--first 2 use line below
SELECT concat('max t: ',@max_tik_id,' min t: ', @min_tik_id, 'max p: ',@max_p_id,' min p: ', @min_p_id);
--third one use
SELECT ('Sorry, no tickets are available for transfer.');
  • apply to database dms_sample_dbo schema

DMS replication instance and endpoints#

get existed vpc

// get existed vpc
const vpc = aws_ec2.Vpc.fromLookup(this, 'Vpc', {
vpcId: props.vpcId,
vpcName: props.vpcName
})

dms subnet group

// dms subnet group
const dmSubnet = new aws_dms.CfnReplicationSubnetGroup(
this,
'SubnetGroupForDms',
{
replicationSubnetGroupDescription: 'subnet group for replication demo',
subnetIds: vpc.privateSubnets.map(subnet => subnet.subnetId),
replicationSubnetGroupIdentifier: 'SubnetGroupForDmsId'
}
)

replication instance

// dms database migration service
const remplicationIstance = new aws_dms.CfnReplicationInstance(
this,
"ReplicationInstanceDemo",
{
replicationInstanceClass: "dms.t2.medium",
allocatedStorage: 256,
engineVersion: "3.4.6",
multiAz: false,
publiclyAccessible: false,
resourceIdentifier: "ReplicationInstanceDemo",
vpcSecurityGroupIds: [
props.dbStack.dbSecurityGroup.securityGroupId,
props.dbStack.ec2SecurityGrroup.securityGroupId,
],
availabilityZone: "ap-southeast-1a",
replicationSubnetGroupIdentifier:
dmSubnet.replicationSubnetGroupIdentifier!.toLowerCase(),
}
);
remplicationIstance.addDependsOn(dmSubnet);
target endpoints
```tsx
// dms target endpoints
const targetEndpoint = new aws_dms.CfnEndpoint(
this,
"DmsTargetEndpointAuroraMySql",
{
endpointType: "target",
endpointIdentifier: "targetEndpointId",
engineName: "aurora",
// default dbname aurora mysql
// databaseName: "tempdb",
serverName: props.dbStack.aurora.clusterEndpoint.hostname,
port: 3306,
username: "admin",
// default non
sslMode: "none",
// production => secret manager
password: "Password1",
mySqlSettings: {},
}
);

source endpoint

// dms source endpoint
const sourceEndpoint = new aws_dms.CfnEndpoint(
this,
'DmsSourceEndpointRdsSqlServerEc2',
{
endpointType: 'source',
endpointIdentifier: 'sourceEndpointId',
engineName: 'sqlserver',
databaseName: 'dms_sample',
serverName: props.dbStack.ec2.instancePublicDnsName,
port: 1433,
username: 'awssct',
password: 'Password1',
sslMode: 'none'
}
)

replication task with rules

// dms replication task
const replicationSelectTablesTask = new aws_dms.CfnReplicationTask(
this,
'DmsReplicationTaskSelectDemo',
{
// full-load, cdc, full-load-and-cdc
migrationType: 'full-load',
// replication instance ref indicates its arn
replicationInstanceArn: remplicationIstance.ref,
sourceEndpointArn: sourceEndpoint.ref,
tableMappings: JSON.stringify({
rules: [
{
'rule-type': 'selection',
'rule-id': '200548593',
'rule-name': '200548593',
'object-locator': {
'schema-name': 'dbo',
'table-name': 'player'
},
'rule-action': 'include',
filters: []
},
{
'rule-type': 'transformation',
'rule-id': '876674182',
'rule-name': '876674182',
'rule-target': 'schema',
'object-locator': {
'schema-name': 'dbo'
},
'rule-action': 'rename',
value: 'dms_sample_dbo',
'old-value': null
}
]
}),
targetEndpointArn: targetEndpoint.ref
}
)

Check results#

Screen Shot 2022-06-22 at 13 03 13

Screen Shot 2022-06-22 at 13 12 12

Screen Shot 2022-06-22 at 13 12 43