AWS DMS Data Migration from MySQL to SQL Server

Problem

When using AWS Relational Database Service (RDS), we may need to migrate data from a third-party database such as MySQL to SQL Server. Let’s see how this can be done.

Solution

AWS provides the Database Migration Service (DMS) for migrating from one database to another. In this article I explain how to migrate MySQL running on AWS RDS to SQL Server, also running on RDS. The procedure to migrate some other third-party database to SQL Server on RDS is like this only with a few differences. At the outset, when we migrate one database platform to another type of database, it’s called heterogeneous migration, which sets the scope of this article. The only caveat is that the migration path should be supported by the AWS Database Migration Service.

Setting the Environment

Create an AWS account if you don’t already have one. Download and install SQL Server Management Studio (SSMS) on your local machine, if not already installed.

Creating an IAM User

We need an IAM user to be able to access other AWS services that we need, services like AWS RDS, Cloud Shell for connecting to MySQL, KMS for data encryption, and optionally CloudWatch for logging. Let’s first create an IAM Policy with all the required permissions and policy statements. Then, we can create an IAM user based on the IAM Policy. We use this IAM user to login to the AWS Management Console and use the AWS DMS service to perform the migration. However, first login to the AWS Management Console as the Root user.

Creating an IAM Policy

To create an IAM policy click on Create policy in the IAM Dashboard.

Create IAM Policy

Select the JSON tab to specify the policy permissions in JSON format.

Choose JSON Format for policy

It’s best to create a policy that even includes permissions for services that we use only occasionally because it’s not always convenient to update a policy for each new service we may need. Copy the following JSON listing to the Policy editor text area.

--MSSQLTips.com (JSON)
 
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "dms:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "ds:*",
            "Resource": "*"
        },
    {
       "Effect": "Allow",
       "Action": "cloudshell:*",
       "Resource": "*"
    },
        {
            "Effect": "Allow",
            "Action": "rds:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "ec2:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "cloudwatch:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "aws-marketplace:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "logs:*",
            "Resource": "*"
        }
    ]
}

Click Next.

In the Review and create screen, specify a policy name (for e.g. DMS).

Review and create policy

Scroll down and click Create policy.

Create policy

A new IAM policy has been created.

Policy has been created

Creating the User

Select Users in the IAM dashboard and click on Create user.

Users Create user

Specify a User name (e.g. sqlserver_user) and select the option Provide user access to the AWS Management Console. Scroll and specify a console password. Click Next.

Specify user details

In Set permissions select Attach policies directly.

Attach policy to user

In the Permissions policy table, select the policy (DMS) that we created earlier. Scroll and click Next.

Select policy to attach

Review the user’s details.

Review user details

Click Create user. A new IAM user has been created. Note the listed Console sign-in details. We use the Console sign-in URL to login with the User name and Console password. It is important to copy and keep the console password because this is the only time it can be copied.

Creating the Source MySQL Instance on RDS

To create the source database instance, login to the AWS Management Console as the Root user. Select the RDS service. Select Databases in the margin. Click Create database.

RDS>Create database

Choose database creation method as Full configuration. Choose database engine as MySQL.

Choose MySQL database engine

Select the Engine version next. Select a template based on use case. As we are not deploying the database into production, select Dev/Test.

Choose template as Dev/Test

We don’t need a high availability deployment; therefore, select Single-AZ DB instance deployment (1 instance).

Choose Single-AZ deployment

Specify a DB instance identifier (e.g., mysql-database). This is not the database name, which we set later. The master username is admin by default.

Specify settings

Choose the Self-managed credentials management option. Specify, and confirm, the master user password. Note the password as we’ll need it to access the MySQL database.

Choose self-managed credentials

Select Password authentication as the database authentication method.

Choose Password authentication

Keep the default settings for instance type, storage, and select Yes for Public access. Without public access, we can’t access the database from a local machine. However, as we will discover, we won’t need to use public access as we can use the integrated cloud shell. Choose the default VPC Security group.

Choose VPC and public access

Specify a database name, testdb for example. We’ll use this database to demonstrate migration.

Specify a database to create

We can deselect the options to perform backups, monitoring, connecting to an EC2 instance, and other ancillary operations if we don’t need them. Choose the option to Create new VPC. We’ll use this VPC to create all our resources needed to perform a migration. Click Create database.

Create database button

A new RDS based MySQL instance has been created.

MySQL On RDS has been created

Creating the Target SQL Server Instance on RDS

Using the same procedure as the MySQL instance, create a SQL Server instance by choosing a database engine as SQL Server.

Choose SQL Server target server

Notably, choose from the Enterprise, Standard, Developer, and Workgroup editions only as the Web and Express are not supported for a SQL Server migration target.

Choose Standard Edition, or one of the supported ones

Choose the engine version.

Choose SQL Server engine version

Specify database identifier (for e.g., sqlserve-rds). The default master username is admin. Select the Self-managed credentials management option and specify a master password.

Configure settings

Select the same VPC and the default DB subnet group as for the MySQL instance. Set Public access to Yes.

Configure public access and VPC

The Inbound/Outbound rules of the associated Security Group should allow access from a local machine. Select the Availability Zone as the same as for the MySQL instance. We keep all resources in the same VPC and AZ to avoid network issues.

Configure Availability Zone

Click Create database.

Create database button

We have both the source and the target database instances running and available on RDS.

SQL Server on RDS has been created

Configuring MySQL for Migration

Next, we connect to the MySQL database and create a sample table in the testdb that we created. For the MySQL instance, select the Connectivity & security tab. Select the Connect using > CloudShell option. Click Launch CloudShell.

Launch CloudShell for MySQL On RDS

Specify a new environment name and click Create and run.

Configure a CloudShell environment

Specify password for the MySQL database and press Enter.

Provide password for admin user

A connection to the MySQL database is established as shown in client shell.

MySQL CloudShell started

List databases with show databases command.

List databases

Set the database to testdb with use testdb command. Create a sample database called Employee by running the following script:

--MSSQLTips.com (T-SQL)
CREATE TABLE Employee (
       empid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
       lname VARCHAR (35),
       fname VARCHAR (35),
       dept INT,
       age INT,
       since INT,
       INDEX idx1(dept,age,lname)
);

The table that we use to migrate data to SQL Server has been created.

Run script to create table

Next, add sample data to the table with the following script.

--MSSQLTips.com (T-SQL)
INSERT INTO Employee (lname, fname, dept, age, since)
VALUES ('abbot','john',1,26,2020),
       ('smith','jon',30,45,2017),
       ('branch','bob',4,34,2019),
       ('smith','bob',15,55,2018),
       ('carlyle','joe',10,35,2021);

Then, verify that data is added by running a SELECT query.

--MSSQLTips.com (T-SQL)
Select * from Employee;

The table data listed is shown in CloudShell.

Run scripts to add data, and to list data

Next, create a MySQL user with same name as the IAM user (sqlserver_user) we created earlier. When we connect and perform migration using this IAM user, it needs the privileges to select etc. from the source database.

--MSSQLTips.com (T-SQL)
CREATE USER 'sqlserver_user'@'%' IDENTIFIED BY 'some password';

Run the following script to grant the necessary privileges to the user.

--MSSQLTips.com (T-SQL)
GRANT ALTER, CREATE, DROP, INDEX, INSERT, UPDATE, DELETE, SELECT ON *.* TO 'sqlserver_user'@'%';

The privileges are granted to the DMS user.

Grant privileges to DMS user

Configuring SQL Server for Migration

First, we obtain the connection endpoint to connect to the AWS RDS based SQL Server instance. For this, select the Connectivity and security tab for the AWS RDS instance in the Management Console. Choose Connect using > Endpoints. Note the Master username as we need it to connect. The port will be included in the endpoint itself; therefore, we don’t need to note it separately.

Connect using > Endpoints

Copy the Endpoint.

Copy Endpoint

Open SQL Server Management Studio, and select File > Connect Object Explorer from the toolbar.

Create connection to SQL Server from SQL Server Management Studio

Add Server Name as the Endpoint we copied from the AWS Management Console. Select Authentication as SQL Server Authentication. Specify User Name as admin, and add the Password for admin. Click the down arrow adjacent the Database Name and the SSMS should get connected to SQL Server if all the connection information is correct. It will list the databases. Select the master database. Next, click Connect in the dialog.

Connect

The Object Explorer should list the new connection, and the databases.

Connection created

Create Database and User

Run the following statement in a query editor.

--MSSQLTips.com (T-SQL)
CREATE DATABASE testdb;

A new database has been created. This is the target database for the migration.

Create testdb

Run the following script in a query editor.

--MSSQLTips.com (T-SQL)
USE [testdb];
CREATE LOGIN sqlserver_user WITH PASSWORD = 'some password';
CREATE USER sqlserver_user FOR LOGIN sqlserver_user;
ALTER ROLE db_owner ADD MEMBER sqlserver_user;

This script sets the database to testdb. It creates a “Login,” at the server level as the security principal used to connect to the SQL Server instance itself. Next, it maps the server login to a user identity sqlserver_user inside the database at the database level. Then, it adds the new user to the db_owner fixed database role.

Create Login and map to DMS user

Run the following script to verify that the DMS user is indeed made the db_owner.

--MSSQLTips.com (T-SQL)
USE [testdb];
 
SELECT IS_ROLEMEMBER('db_owner', 'sqlserver_user') AS IsDbOwner;

It should list 1 in the result, which confirms the sqlserver_user has the db_owner role.

Verify DMS User has db_owner role

Next, run the following script to grant the necessary privileges to the sqlserver_user.

--MSSQLTips.com (T-SQL)
USE [testdb];
GO
-- 2. Create the schema if it's not already there.
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'awsdms_control')
BEGIN
    EXEC('CREATE SCHEMA [awsdms_control]');
END
GO
-- 3. Grant full control of the schema to the DMS user
-- (Replaces: GRANT ALL PRIVILEGES ON awsdms_control.*)
GRANT ALTER, CONTROL, DELETE, INSERT, SELECT, UPDATE, REFERENCES 
ON SCHEMA::[awsdms_control] TO [sqlserver_user];
GO

It checks the system catalog to see if a schema named awsdms_control already exists. Further, AWS DMS uses this schema to store control tables (like checkpoints and replication status) during a migration. It checks the system catalog to see if a schema named awsdms_control already exists. Then it gives sqlserver_user full administrative rights only over this schema. The user can create tables (ALTER), manage security (CONTROL), and perform all standard data operations (INSERT, SELECT, etc.).

Grant privileges to DMS User in SQL Server

Check User Permissions

Run the following script to verify that the DMS user has the necessary privileges.

--MSSQLTips.com (T-SQL)
USE [testdb];
SELECT 
    name AS DatabaseUser, 
    permission_name 
FROM sys.database_permissions AS dp
JOIN sys.database_principals AS pr ON dp.grantee_principal_id = pr.principal_id
WHERE pr.name = 'sqlserver_user';

It lists all the privileges granted.

List privileges

Getting Started with Migration

To get started with performing a migration from MySQL to SQL Server, we logout as the root user, and log back in as the DMS user sqlserver_user that we have created and configured in both the databases. The user listed at the top-right should be the DMS user.

Log in as DMS User

Navigate to the DMS > Get started screen. Select the Migrate and replicate option.

Choose Migrate or replicate

Then, select the Serverless tab. The Serverless data movement automatically scales resources needed for a migration.

Choose Serverless mode

Select Migrate or replicate in the margin, if not already expanded.

Migrate or replicate in navigation margin

The two important top-level choices we have made are to migrate or replicate data, and to use serverless data movement.

Creating an Endpoint for MySQL

To create an endpoint for MySQL, click Create endpoints.

Start to create an endpoint

In the Create endpoint page, select Endpoint type as Source endpoint. Further, click the checkbox Select RDS DB instance.

Choose Source endpoint type

Select the RDS instance as the one we created earlier, mysql-database.

Choose MySQL RDS instance

Next, configure the endpoint. An Endpoint identifier by the same name as the RDS instance is added by default. Select Source engine as MySQL. An ARN is not needed.

Configure endpoint

Select the option Provide access information manually under Access to endpoint database. The Server name and Port are added automatically.

Configure access to endpoint

Specify the Password for the admin user and select none in Secure Socket Layer mode.

Provide password for admin

Keep other settings as the default. Scroll down and click Create endpoint.

Click Create endpoint

A new endpoint has been created for the source database.

MySQL endpoint has been created

Creating an Endpoint for SQL Server

Click on Create endpoint to create an endpoint for SQL Server.

Start to create endpoint for SQL Server

This time, select Endpoint type as Target endpoint. Again, click the checkbox Select RDS DB instance.

Choose Endpoint type as "Target endpoint"

Select the RDS instance that we created for SQL Server.

Select SQL Server RDS instance to migrate to

Again, the Endpoint identifier has been set to the same name as the RDS instance. The Target engine should be Microsoft SQL Server.

Choose target engine as SQL Server

Select Provide access information manually under Access to endpoint database. Specify the Password for the admin user.

Provide access information manually

Select none for SSL mode. Specify the target Database name as the one we have created both in the source and the target instances, which is testdb.

Provide target database name

Click Create endpoint.

Click Create endpoint

The target endpoint has been created.

Target endpoint has been created

Creating a Migration Task

To create a migration task, click Create task in the top-level page.

Create migration task

In the Create task page, specify a Task identifier, mysql-to-sqlserver, for example.

Configure task settings

Select the Source database endpoint that we just configured for MySQL. Next, select the Target database endpoint that we configured for SQL Server. Select Task mode as Serverless.

Configure source and target endpoints, and Serverless mode

Select Task type as Migrate only. This option migrates data only once (Full Load). One other option is Migrate and replicate, which migrates data from source to target and replicates ongoing changes to migrate additional data that may have been added to the source database. The third option is Replicate only, which replicates data without performing a Full Load. Keep the Editing mode setting as the default. Choose the Drop tables on target option for Target table preparation mode.

Choose Task type as "Migrate only"

Set Additional Settings

Set the Include LOB columns in replication to Do not include LOB columns. Click the checkbox Turn on validation under Data validation to validate the migrated data. We have the option to turn on CloudWatch logs.

Turn on validation

For Table mappings we must add one selection rule with an include action. Click Add new selection rule.

Add new selection rule

Specify Schema name as testdb, and Schema table name as Employee.

Provide a schema name and a table in that schema

Select Action as Include. Similarly, we can include/exclude other tables individually. Because we have specified a schema name and a table name, this is the only table that will be migrated. The table will be created in the SQL Server because we chose to drop existing tables and create new ones earlier. Alternatively, we can choose to migrate all tables with % for Schema table name and then exclude individual tables that we don’t want to migrate with selection rules that specify the Exclude action. We can even migrate all schemas by specifying % for Schema name.

Select Include action

Optionally, we can add transformation rules with Add new transformation rule to perform transformation, like adding a prefix/suffix to a table/column name. With the simple table we are migrating we won’t use any transformation rules.

Optionally, Add new transformation rule

Under Compute settings, select the same VPC, Subnet group, and VPC Security group that we use for the RDS instances.

Configure VPC

Under Availability, select a Single-AZ deployment and set the Availability Zone to be the same as for the RDS instances.

Configure Availability

Specify minimum and maximum DMS capacity, which in turn determines the memory, compute, and networking resources to be provisioned.

Configure compute capacity

Click Create task.

Click Create task

A new task has been created.

A new migration task has been created

Running the Migration Task

To perform the data migration, select the migration task in the table and select Actions > Start from the dropdown.

Actions>Start migration

To perform premigration assessment to verify that we have configured the source and target databases properly, we have the option to Turn on Premigration assessment, and Stop Replication On Premigration Assessment Failure.

Optionally, choose Premigration assessments

Further, we can select individual assessments that we want to perform. By default, all assessments are performed. These are useful for first-time migration users because these identify the precise cause why a migration may be failing. However, if you have performed a few similar migrations before, you can switch off the premigration assessments.

Select specific/all assessments

Click Start task to start the task.

Click Start task

First, it provisions capacity, and then it performs the premigration assessments.

Task started

Once the task has been started, it starts the data migration after the premigration assessments have completed successfully. If there are any issues, they are listed in the result of the premigration assessments.

Result of premigration assessments indicate issues if any

Note; however, after fixing the indicated issue/s the premigration assessments should be skipped in the next run of the migration task because it will continue to list the same issues even though you may have fixed them. This is because it caches the metadata, and obtains the data for the assessments from the metadata. If you want to perform a new premigration assessments you should delete the existing RDS instances, endpoints and migration task, to create new artifacts with all issues fixed.

Once, the migration has been completed successfully, it indicates it has been Completed in the Serverless replications table.

Full Load migration completed

Exploring Migrated Data

Let’s explore what got migrated and what didn’t. The Object Explorer lists the testdb.Employee table, which verifies that the table has been migrated from MySQL to SQL Server.

The table has been migrated to SQL Server

We can discover the migrated table in the SQL Server Management Studio.

SQL Server Management Studio can be used to explore migrate table

The migration has migrated the data as well.

Data has been migrated as well

The migration does migrate the primary key. However, the migration did not migrate the index from the source database in MySQL. This is a limitation we need to aware of – that it doesn’t migrate secondary indexes. We can discover best practices for a DMS migration, and those specific to SQL Server as the target.

Secondary index is not migrated

Summary

In this article, we explain the procedure to migrate table data from a MySQL instance running on AWS to a SQL Server instance also running on RDS using AWS DMS. First, we create an IAM user to perform a DMS migration. Then we create and configure the source and target databases, followed by performing a migration.

Next Steps

Leave a Reply

Your email address will not be published. Required fields are marked *