AWS DMS: Replication Instance, Endpoints and Full Load
-
Phase 0: Before you start
-
Phase 1: The foundation
-
Phase 2: Operations and security
-
Phase 3: SQL Server on AWS
-
Phase 4: Troubleshooting step by step
-
Phase 5: Final project and career
Most SQL Server moves to AWS start the same way: copy the data across while the old server keeps running. An AWS DMS SQL Server migration does that with a replication instance that reads from your source and writes to your target, table by table.
You’ll build a pretend on-premises SQL Server on EC2, create an RDS for SQL Server Standard target, and run a DMS full-load task for 50,003 rows. Then you’ll see exactly what DMS didn’t copy, and fix two errors every migration team meets.
| Time | You need | By the end you can |
|---|---|---|
| 120 minutes (about 40 of it waiting for servers) | Lessons 42 to 46 done (Start-SqlLab.ps1, the saatshop-full.bak file in your backup bucket), your laptop signed in as saat-admin. Cost: about USD 3 if you finish and clean up within two hours. DMS doesn’t support SQL Server Express, so this lab uses two SQL Server Standard servers, and their licenses are most of the bill. Lesson 48 reuses them, so do it within 7 days. | Explain DMS replication instances, endpoints and tasks, prepare a SQL Server source user, run and monitor a full-load task, check row counts, and know what DMS leaves behind. |
What it is, in one line
An AWS DMS SQL Server migration copies tables and rows from a source database to a target using a replication instance, endpoints and a task.
In Simple Words
The removals van
Moving house, you hire a van and two movers. One stands at the old house, one at the new house, and the van drives the boxes between them.
DMS works the same way. The source endpoint is the old house, the target endpoint is the new house, and the replication instance is the van. The task is the list of rooms to empty.
Movers carry boxes; they don’t rebuild your kitchen cupboards or rewire the lights. DMS carries rows. It doesn’t rebuild indexes, defaults or stored procedures.
Engineer's View
How DMS works
A replication instance is a managed EC2 instance running the DMS engine in your subnets (a replication subnet group), with a security group that must reach both databases. You pay for every hour it exists. DMS Serverless is the option that scales capacity for you.
Endpoints hold connection details; test-connection proves the instance can log in. A task has a migration type (full-load, cdc or full-load-and-cdc), table mappings with selection and transformation rules, and settings such as TargetTablePrepMode: DROP_AND_CREATE, TRUNCATE_BEFORE_LOAD or DO_NOTHING.
For SQL Server, DMS supports Enterprise, Standard, Workgroup and Developer editions as source and target; Web works only as a full-load source, and Express isn’t supported at all. A full-load source user needs db_datareader, VIEW DATABASE STATE, VIEW DEFINITION and VIEW SERVER STATE. DMS creates target tables with primary keys only: the identity property, defaults, secondary indexes, foreign keys, triggers and code aren’t migrated, which is what Lesson 49 handles.
How it works, step by step
Here is what happens in a full-load task.
1DMS connects to both sides
The replication instance logs in to the source as dms_user and to the target as an admin user. If either test fails, nothing else will work.
2Tables are prepared and loaded
For each selected table, DMS prepares the target (here: drop and create), reads the source in chunks and bulk-loads the target. Several tables load in parallel.
3The task stops when it is done
A full-load task moves to Stopped with “Load complete”. Table statistics show the rows loaded and each table’s state.
4Know the building blocks
Four things to create, and one list of limits to check before you promise a date:
Because DMS moves data and not schema, real projects create the schema on the target first (from scripts or Schema Conversion), then let DMS load the data.
✅ Checkpoint: You can explain what the replication instance, endpoints and task each do, and which SQL Server editions DMS supports.
Hands-on lab: migrate SaatShop to RDS with a DMS full load
Sign in and set names. The first command creates the target, which takes 20 minutes or so, so start it right away:
cd C:\aws-lab
aws login --profile saat-admin
$env:AWS_PROFILE = 'saat-admin'
$v = Get-Content C:\aws-lab\saat-vpc.json | ConvertFrom-Json
$acct = aws sts get-caller-identity --query Account --output text
$bak = "saat-sqlbak-$acct-ap-south-1-an"
aws rds create-db-instance --db-instance-identifier saat-sql-se --engine sqlserver-se --license-model license-included --db-instance-class db.m6i.large --allocated-storage 20 --storage-type gp3 --storage-encrypted --master-username saatadmin --manage-master-user-password --db-subnet-group-name saat-db-subnets --vpc-security-group-ids $v.DbSg --no-publicly-accessible --backup-retention-period 0 --tags Key=course,Value=saatvedha-aws Key=lesson,Value=47 --query "DBInstance.DBInstanceStatus" --output text
.\Start-SqlLab.ps1 -Db saat-sql-se -LocalPort 11434
sqlcmd -S "127.0.0.1,11434" -U saatadmin -C -Q "CREATE DATABASE SaatShop; SELECT SERVERPROPERTY('Edition') AS edition"You should see something like this:
creating
Tunnel ready on 127.0.0.1,11434 for saatadmin (password is in SQLCMDPASSWORD for this window)
edition
--------------------------
Standard Edition (64-bit)saat-sql-se is the target, with an empty SaatShop database. Backup retention is 0 to make creation faster; never do that in production.
1Build the “on-premises” SQL Server
Paste this whole block. It launches SQL Server 2022 Standard on EC2 (standing in for a server in your data centre) and lets it read your backup bucket:
$srcSg = aws ec2 create-security-group --group-name saat-onprem-sql-sg --description "Pretend on-prem SQL Server" --vpc-id $v.VpcId --query GroupId --output text
@{ Version = '2012-10-17'; Statement = @(@{ Effect = 'Allow'; Action = 's3:GetObject'; Resource = "arn:aws:s3:::$bak/*" }) } | ConvertTo-Json -Depth 5 | Set-Content read-bak.json
aws iam put-role-policy --role-name saat-ec2-ssm-role --policy-name saat-read-sqlbak --policy-document file://read-bak.json
$sqlAmi = aws ssm get-parameter --name /aws/service/ami-windows-latest/Windows_Server-2022-English-Full-SQL_2022_Standard --query Parameter.Value --output text
$src = aws ec2 run-instances --image-id $sqlAmi --instance-type t3.xlarge --subnet-id $v.PrivateA --security-group-ids $srcSg --iam-instance-profile Name=saat-ec2-ssm-profile --metadata-options HttpTokens=required --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=onprem-sql},{Key=course,Value=saatvedha-aws},{Key=lesson,Value=47}]' --query "Instances[0].InstanceId" --output text
do { Start-Sleep 30; $p = aws ssm describe-instance-information --filters "Key=InstanceIds,Values=$src" --query "InstanceInformationList[0].PingStatus" --output text } until ($p -eq 'Online')
$srcIp = aws ec2 describe-instances --instance-ids $src --query "Reservations[0].Instances[0].PrivateIpAddress" --output text
"$src $srcIp"You should see something like this:
i-0a1b2c3d4e5f60718 10.20.11.143The security group has no inbound rules yet. You’ll open 1433 only to DMS in step 3.
2Load SaatShop and create the DMS user on the source
Paste this whole block. Run Command restores your Lesson 46 backup, adds 50,000 orders and an index, and creates dms_user (about 5 minutes):
$dmsPw = aws secretsmanager get-random-password --password-length 20 --exclude-punctuation --require-each-included-type --query RandomPassword --output text
$setup = @'
$inst = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL').MSSQLSERVER
$reg = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$inst\MSSQLServer"
Set-ItemProperty $reg -Name LoginMode -Value 2
Set-ItemProperty "$reg\SuperSocketNetLib\Tcp" -Name Enabled -Value 1
New-NetFirewallRule -DisplayName 'SQL Server 1433' -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow | Out-Null
Stop-Service MSSQLSERVER -Force
net start MSSQLSERVER /mSQLCMD | Out-Null
Start-Sleep 15
sqlcmd -S . -E -C -b -Q "IF SUSER_ID('NT AUTHORITY\SYSTEM') IS NULL CREATE LOGIN [NT AUTHORITY\SYSTEM] FROM WINDOWS; ALTER SERVER ROLE sysadmin ADD MEMBER [NT AUTHORITY\SYSTEM];" | Out-Null
Stop-Service MSSQLSERVER -Force
Start-Service MSSQLSERVER
Set-Service SQLSERVERAGENT -StartupType Automatic
Start-Service SQLSERVERAGENT
New-Item C:\sqlbak -ItemType Directory -Force | Out-Null
Read-S3Object -BucketName 'BAK_HERE' -Key saatshop-full.bak -File C:\sqlbak\saatshop-full.bak | Out-Null
$dataDir = @(sqlcmd -S . -E -C -h -1 -W -Q "SET NOCOUNT ON; SELECT CAST(SERVERPROPERTY('InstanceDefaultDataPath') AS nvarchar(260))")[0].Trim()
$files = sqlcmd -S . -E -C -h -1 -W -s "|" -Q "SET NOCOUNT ON; RESTORE FILELISTONLY FROM DISK = 'C:\sqlbak\saatshop-full.bak'" | Where-Object { $_ -match '\|' }
$moves = foreach ($l in $files) { $p = $l -split '\|'; $ext = if ($p[2] -eq 'L') { 'ldf' } else { 'mdf' }; "MOVE '$($p[0])' TO '$dataDir$($p[0]).$ext'" }
sqlcmd -S . -E -C -b -Q "RESTORE DATABASE SaatShop FROM DISK = 'C:\sqlbak\saatshop-full.bak' WITH $($moves -join ', '), RECOVERY" | Out-Null
sqlcmd -S . -E -C -b -d SaatShop -Q "INSERT dbo.Orders (Customer, Amount) SELECT TOP (50000) CONCAT(N'Customer ', ROW_NUMBER() OVER (ORDER BY (SELECT NULL))), CAST(ABS(CHECKSUM(NEWID())) % 500000 / 100.0 AS decimal(10,2)) FROM sys.all_objects a CROSS JOIN sys.all_objects b; CREATE INDEX IX_Orders_Customer ON dbo.Orders (Customer);" | Out-Null
sqlcmd -S . -E -C -b -Q "CREATE LOGIN dms_user WITH PASSWORD = 'PW_HERE', CHECK_POLICY = ON; GRANT VIEW SERVER STATE TO dms_user;" | Out-Null
sqlcmd -S . -E -C -b -d SaatShop -Q "CREATE USER dms_user FOR LOGIN dms_user; ALTER ROLE db_datareader ADD MEMBER dms_user; GRANT VIEW DATABASE STATE TO dms_user; GRANT VIEW DEFINITION TO dms_user;" | Out-Null
sqlcmd -S . -E -C -h -1 -W -Q "SET NOCOUNT ON; SELECT CONCAT(SERVERPROPERTY('Edition'), ' | ', (SELECT COUNT(*) FROM SaatShop.dbo.Orders), ' orders | dms_user ', CASE WHEN SUSER_ID('dms_user') IS NULL THEN 'missing' ELSE 'ready' END)"
'@
$script = $setup -replace 'BAK_HERE', $bak -replace 'PW_HERE', $dmsPw
@{ commands = @($script -split "`r?`n"); executionTimeout = @('1800') } | ConvertTo-Json | Set-Content src-setup.json
$cmd = aws ssm send-command --document-name AWS-RunPowerShellScript --instance-ids $src --parameters file://src-setup.json --query Command.CommandId --output text
do { Start-Sleep 20; $st = aws ssm get-command-invocation --command-id $cmd --instance-id $src --query Status --output text 2>$null } until ($st -in 'Success', 'Failed', 'TimedOut')
aws ssm get-command-invocation --command-id $cmd --instance-id $src --query StandardOutputContent --output textYou should see something like this:
Standard Edition (64-bit) | 50003 orders | dms_user readyThe script turns on SQL authentication and TCP, opens the Windows firewall for 1433, and briefly starts SQL Server in single-user mode so Run Command can make itself sysadmin. That last trick is also how you get back into a SQL Server when nobody knows an admin password. dms_user gets only the read rights a full load needs.
3Create the replication instance and endpoints
Paste this whole block. The replication instance takes about 10 minutes:
'{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "dms.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' | Set-Content dms-trust.json
foreach ($r in @(@('dms-vpc-role', 'AmazonDMSVPCManagementRole'), @('dms-cloudwatch-logs-role', 'AmazonDMSCloudWatchLogsRole'))) {
aws iam get-role --role-name $r[0] 2>$null | Out-Null
if ($LASTEXITCODE -ne 0) { aws iam create-role --role-name $r[0] --assume-role-policy-document file://dms-trust.json | Out-Null; aws iam attach-role-policy --role-name $r[0] --policy-arn "arn:aws:iam::aws:policy/service-role/$($r[1])" }
}
Start-Sleep 15
$dmsSg = aws ec2 create-security-group --group-name saat-dms-sg --description "DMS replication instance" --vpc-id $v.VpcId --query GroupId --output text
aws ec2 authorize-security-group-ingress --group-id $srcSg --ip-permissions "IpProtocol=tcp,FromPort=1433,ToPort=1433,UserIdGroupPairs=[{GroupId=$dmsSg,Description=1433-from-dms}]" | Out-Null
aws ec2 authorize-security-group-ingress --group-id $v.DbSg --ip-permissions "IpProtocol=tcp,FromPort=1433,ToPort=1433,UserIdGroupPairs=[{GroupId=$dmsSg,Description=1433-from-dms}]" | Out-Null
aws dms create-replication-subnet-group --replication-subnet-group-identifier saat-dms-subnets --replication-subnet-group-description "saat-vpc private subnets" --subnet-ids $v.PrivateA $v.PrivateB | Out-Null
$ri = aws dms create-replication-instance --replication-instance-identifier saat-dms --replication-instance-class dms.t3.medium --allocated-storage 20 --replication-subnet-group-identifier saat-dms-subnets --vpc-security-group-ids $dmsSg --no-publicly-accessible --no-multi-az --tags Key=course,Value=saatvedha-aws Key=lesson,Value=47 --query ReplicationInstance.ReplicationInstanceArn --output text
aws dms wait replication-instance-available --filters "Name=replication-instance-arn,Values=$ri"
$seHost = aws rds describe-db-instances --db-instance-identifier saat-sql-se --query "DBInstances[0].Endpoint.Address" --output text
$srcEp = aws dms create-endpoint --endpoint-identifier saat-onprem-src --endpoint-type source --engine-name sqlserver --server-name $srcIp --port 1433 --database-name SaatShop --username dms_user --password $dmsPw --query Endpoint.EndpointArn --output text
$tgtEp = aws dms create-endpoint --endpoint-identifier saat-rds-se-tgt --endpoint-type target --engine-name sqlserver --server-name $seHost --port 1433 --database-name SaatShop --username saatadmin --password $env:SQLCMDPASSWORD --ssl-mode require --query Endpoint.EndpointArn --output text
foreach ($ep in $srcEp, $tgtEp) { aws dms test-connection --replication-instance-arn $ri --endpoint-arn $ep | Out-Null }
foreach ($ep in $srcEp, $tgtEp) { aws dms wait test-connection-succeeds --filters "Name=endpoint-arn,Values=$ep" }
aws dms describe-connections --filters "Name=replication-instance-arn,Values=$ri" --query "Connections[].[EndpointIdentifier,Status]" --output textYou should see something like this:
saat-onprem-src successful
saat-rds-se-tgt successfulBoth endpoints connect. The target uses SSL; the source doesn’t here, but a real on-premises source over VPN should use it too. In production, endpoints read passwords from Secrets Manager instead of the command line.

4Run the full-load task and check the result
Paste this whole block:
'{ "rules": [ { "rule-type": "selection", "rule-id": "1", "rule-name": "dbo-all", "object-locator": { "schema-name": "dbo", "table-name": "%" }, "rule-action": "include" } ] }' | Set-Content mappings.json
'{ "Logging": { "EnableLogging": true }, "FullLoadSettings": { "TargetTablePrepMode": "DROP_AND_CREATE" } }' | Set-Content task.json
$task = aws dms create-replication-task --replication-task-identifier saat-shop-full --source-endpoint-arn $srcEp --target-endpoint-arn $tgtEp --replication-instance-arn $ri --migration-type full-load --table-mappings file://mappings.json --replication-task-settings file://task.json --query ReplicationTask.ReplicationTaskArn --output text
aws dms wait replication-task-ready --filters "Name=replication-task-arn,Values=$task"
aws dms start-replication-task --replication-task-arn $task --start-replication-task-type start-replication | Out-Null
aws dms wait replication-task-stopped --filters "Name=replication-task-arn,Values=$task"
aws dms describe-replication-tasks --filters "Name=replication-task-arn,Values=$task" --query "ReplicationTasks[0].[Status,StopReason]" --output text
aws dms describe-table-statistics --replication-task-arn $task --query "TableStatistics[].[TableName,TableState,FullLoadRows]" --output text
sqlcmd -S "127.0.0.1,11434" -U saatadmin -C -d SaatShop -Q "SELECT COUNT(*) AS orders FROM dbo.Orders; SELECT name, is_identity FROM sys.columns WHERE object_id = OBJECT_ID('dbo.Orders') AND name = 'OrderId'; SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.Orders') AND name IS NOT NULL"You should see something like this:
stopped Stop Reason FULL_LOAD_ONLY_FINISHED
Orders Table completed 50003
orders
------
50003
name is_identity
-------- -----------
OrderId 0
name
---------------------------
PK__Orders__C3905BCF1A2B3C4DAll 50,003 rows arrived. But look closer: OrderId is no longer an identity column, the default on CreatedAt is gone, and IX_Orders_Customer wasn’t created.
If an app wrote to this table now, inserts would fail or run slowly. That’s why the schema comes first in real migrations (Lesson 49).

✅ Checkpoint: Both endpoints test successful, the task stopped with FULL_LOAD_ONLY_FINISHED, Orders shows Table completed with 50,003 rows, and the target has the rows but no identity or secondary index.
Break It, Then Fix It
Two problems that stop most first DMS tasks.
1Break it: the source blocks DMS
Remove the 1433 rule from the source security group, then test the source endpoint:
aws ec2 revoke-security-group-ingress --group-id $srcSg --ip-permissions "IpProtocol=tcp,FromPort=1433,ToPort=1433,UserIdGroupPairs=[{GroupId=$dmsSg}]" | Out-Null
aws dms test-connection --replication-instance-arn $ri --endpoint-arn $srcEp | Out-Null
do { Start-Sleep 15; $cs = aws dms describe-connections --filters "Name=endpoint-arn,Values=$srcEp" --query "Connections[0].Status" --output text } until ($cs -ne 'testing')
aws dms describe-connections --filters "Name=endpoint-arn,Values=$srcEp" --query "Connections[0].[Status,LastFailureMessage]" --output textYou’ll get something like this:
failed Test Endpoint failed: Application-Status: 1020912, Application-Message: Cannot connect to ODBC provider Network error has occurred, Application-Detailed-Message: RetCode: SQL_ERROR SqlState: HYT00 NativeError: 0 Message: [unixODBC][Microsoft][ODBC Driver for SQL Server]Login timeout expired“Login timeout expired” sounds like a password problem, but it means the network never answered. Wrong passwords give “Login failed for user” instead.
With an on-premises source, also check the firewall, the VPN routes and the server’s own firewall. Fix: allow 1433 from the DMS security group again and retest:
aws ec2 authorize-security-group-ingress --group-id $srcSg --ip-permissions "IpProtocol=tcp,FromPort=1433,ToPort=1433,UserIdGroupPairs=[{GroupId=$dmsSg,Description=1433-from-dms}]" | Out-Null
aws dms test-connection --replication-instance-arn $ri --endpoint-arn $srcEp | Out-Null
aws dms wait test-connection-succeeds --filters "Name=endpoint-arn,Values=$srcEp"
aws dms describe-connections --filters "Name=endpoint-arn,Values=$srcEp" --query "Connections[0].Status" --output textYou should see this:
successful2Break it: reload into a table that already has the rows
Switch the task to DO_NOTHING and reload the target:
'{ "FullLoadSettings": { "TargetTablePrepMode": "DO_NOTHING" } }' | Set-Content task-nothing.json
aws dms modify-replication-task --replication-task-arn $task --replication-task-settings file://task-nothing.json | Out-Null
aws dms wait replication-task-ready --filters "Name=replication-task-arn,Values=$task"
aws dms start-replication-task --replication-task-arn $task --start-replication-task-type reload-target | Out-Null
aws dms wait replication-task-stopped --filters "Name=replication-task-arn,Values=$task"
aws dms describe-table-statistics --replication-task-arn $task --query "TableStatistics[].[TableName,TableState,FullLoadRows]" --output textYou’ll get something like this:
Orders Table error 0
DO_NOTHING leaves the existing 50,003 rows in place, so every insert hits a primary key that already exists. The task log in CloudWatch Logs shows “Violation of PRIMARY KEY constraint”. Teams set DO_NOTHING to protect a schema they built, then forget the tables already have data from a test run.
Fix: use TRUNCATE_BEFORE_LOAD, which keeps your table design but empties it, then reload:
'{ "FullLoadSettings": { "TargetTablePrepMode": "TRUNCATE_BEFORE_LOAD" } }' | Set-Content task-truncate.json
aws dms modify-replication-task --replication-task-arn $task --replication-task-settings file://task-truncate.json | Out-Null
aws dms wait replication-task-ready --filters "Name=replication-task-arn,Values=$task"
aws dms start-replication-task --replication-task-arn $task --start-replication-task-type reload-target | Out-Null
aws dms wait replication-task-stopped --filters "Name=replication-task-arn,Values=$task"
aws dms describe-table-statistics --replication-task-arn $task --query "TableStatistics[].[TableName,TableState,FullLoadRows]" --output textYou should see something like this:
Orders Table completed 50003Now clean up. The replication instance bills every hour, so it goes. The endpoints, subnet group, security groups, onprem-sql and saat-sql-se stay (stopped) for Lesson 48:
aws dms delete-replication-task --replication-task-arn $task | Out-Null
aws dms wait replication-task-deleted --filters "Name=replication-task-arn,Values=$task"
aws dms delete-replication-instance --replication-instance-arn $ri | Out-Null
aws ec2 stop-instances --instance-ids $src | Out-Null
aws iam delete-role-policy --role-name saat-ec2-ssm-role --policy-name saat-read-sqlbak
Remove-Item read-bak.json, src-setup.json, dms-trust.json, mappings.json, task.json, task-nothing.json, task-truncate.json
.\Stop-SqlLab.ps1 -Db saat-sql-seRDS starts a stopped instance again after 7 days. If you won’t do Lesson 48 this week, delete the target now with aws rds delete-db-instance --db-instance-identifier saat-sql-se --skip-final-snapshot; Lesson 48 shows how to recreate it.
✅ Checkpoint: You fixed a login timeout with a security group rule and a table error with TRUNCATE_BEFORE_LOAD. The task and replication instance are deleted, onprem-sql is stopping, and saat-sql-se is stopping.
This happened at work
The migration that copied everything except the app
A travel company used DMS to move a 400 GB booking database from its data centre to RDS for SQL Server. The full load finished overnight, row counts matched on every table, and the project manager declared it done.
On go-live morning the booking app failed on its first insert: “Cannot insert the value NULL into column BookingId”. DMS had created the tables itself, without identity columns, defaults or the 60 nonclustered indexes. Searches that took milliseconds took minutes.
They rolled back to the data centre, scripted the schema from the source, created it on RDS first, and re-ran DMS with TRUNCATE_BEFORE_LOAD. Their checklist now compares indexes, identities, constraints and code, not just row counts.
Common mistakes and troubleshooting
| Mistake | What happens | What to do |
|---|---|---|
| Planning DMS with SQL Server Express | Endpoint can’t be used | Use Standard, Enterprise or Developer, or native backup |
| Letting DMS create the schema | No identity, defaults or indexes | Create the schema first, then TRUNCATE_BEFORE_LOAD |
| Reading “login timeout” as a password problem | Hours spent on the wrong fix | Check security groups, routes and firewalls |
| DO_NOTHING on tables with test data | Table error, duplicate keys | TRUNCATE_BEFORE_LOAD or clean the target |
| Checking only row counts | Missing objects found on go-live day | Compare schema objects and run app tests |
| Leaving the replication instance running | Hourly charges after the move | Delete it, or use DMS Serverless |
Interview questions
Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.
Fresher level
1. Can you explain in simple words what the main parts of AWS DMS are?
Why they ask this: They want to know you understand how DMS is put together before you try to run a migration with it.
How you might answer: I’d explain it as three pieces. There’s a replication instance, or a serverless config, which is the machine that does the work. Then there are two endpoints, one for the source and one for the target.
Finally there’s the replication task, which holds the table mappings and settings and tells DMS what to move and how.
2. You’re setting up a SQL Server source for a full load. What permissions would you ask the DBA to give the DMS login?
Why they ask this: They want to see that you prepare the source properly and ask for only the access DMS really needs.
How you might answer: I’d ask for db_datareader in the database I’m migrating, plus VIEW DATABASE STATE and VIEW DEFINITION. In master I’d ask for VIEW SERVER STATE.
That is enough for a full load, and it keeps the login read-only.
3. A team wants to move their SQL Server Express database with DMS. What would you tell them?
Why they ask this: They want to check that you know the edition limits before you promise anything. It shows you read the support details instead of guessing.
How you might answer: I’d tell them DMS can’t use Express at all, so they need another way, like native backup and restore. DMS works with Enterprise, Standard, Workgroup and Developer as both source and target.
Web edition is a special case, because it only works as a full-load source.
2–5 years experience
4. After a DMS migration the row counts match, but the app starts failing. What do you think went wrong?
Why they ask this: They want to know if you understand that DMS moves data, not the full database. Many people learn this the hard way in real projects.
How you might answer: My first guess is missing objects. DMS doesn’t bring over identity properties, defaults, secondary indexes, foreign keys, triggers or code like stored procedures.
That’s why I create the schema on the target first. After the load I compare objects, not just rows.
5. Walk me through the TargetTablePrepMode options and when you’d pick each one.
Why they ask this: They want to see that you know the task settings well enough to avoid wiping a schema or loading duplicate data by mistake.
How you might answer: DROP_AND_CREATE drops the target tables and builds them again from the source metadata, so any schema you prepared is lost. TRUNCATE_BEFORE_LOAD keeps your schema and just empties the tables, which is what I use when I’ve created the schema first.
DO_NOTHING keeps both the schema and the data already there. That can cause duplicate key errors, so I only use it when I know the tables are empty or I really want to append.
6. You click Test connection on a DMS endpoint and get “Login timeout expired”. What do you check first?
Why they ask this: They want to see if you can read the error and go to the right layer. A timeout points to the network, not the password.
How you might answer: A timeout means DMS can’t reach the server, so I’d check the network path. That means security groups, NACLs, route tables, the VPN, the firewall on the source server, DNS for the server name and the port.
If the credentials were wrong I’d see “Login failed” instead, so I wouldn’t start by resetting the password.
Senior level
7. You need to move a 3 TB SQL Server database to RDS, and the business gives you only one hour of downtime. How would you plan it?
Why they ask this: They want to see how you design a large migration end to end, including testing, validation and a way back if it goes wrong.
How you might answer: I’d start with an assessment using DMS Fleet Advisor or the SCT reports, then create the schema on RDS first. For the data I’d run a full load with parallel loads and the right LOB settings, then keep it in sync with CDC so the one hour is only for cutover.
I’d turn on validation, rehearse the cutover from a written runbook, and agree a rollback plan before the real night.
8. When would you choose native backup and restore over DMS for a SQL Server move?
Why they ask this: They want to see that you pick the tool based on the situation, not habit. It shows you can explain the trade-off to a team or a manager.
How you might answer: I’d pick backup and restore for a SQL Server to SQL Server move when a short outage is fine, when the source is Express or Web, or when I need the full schema and code exactly as they are.
I’d pick DMS when downtime must be very small, when the target is a different engine, or when I only need some of the tables.
Quick quiz
Q1. What does the replication instance do?
- Stores backups
- Reads from the source and writes to the target
- Hosts the target database
- Runs SSMS
Show answer
B — It is the engine that moves the data.
Q2. Which SQL Server edition can’t DMS use?
- Standard
- Enterprise
- Express
- Developer
Show answer
C — Express isn’t supported as source or target.
Q3. What did DMS not create on the target?
- The rows
- The primary key
- The identity property and secondary index
- The table
Show answer
C — DMS moves data, not full schema.
Q4. “Login timeout expired” on test-connection usually means?
- Wrong password
- Network blocked
- Disk full
- Wrong edition
Show answer
B — The server never answered.
Q5. Which prep mode keeps your schema but empties the table?
- DROP_AND_CREATE
- DO_NOTHING
- TRUNCATE_BEFORE_LOAD
- LIMITED_LOB
Show answer
C — It truncates before loading.
Quick recap
- DMS uses a replication instance, two endpoints and a task.
- Express isn’t supported; Web is only a full-load source.
- Full load copies rows and primary keys, not identity, defaults, indexes or code.
- “Login timeout” means network; “Login failed” means credentials.
- Create the schema first and use TRUNCATE_BEFORE_LOAD for real migrations.
Next lesson
Lesson 48: AWS DMS: change data capture → You’ll keep the target in sync with ongoing changes and practise a near-zero downtime cutover.
Further reading: AWS DMS SQL Server migration
Want to go deeper into AWS DMS SQL Server migration? Read Using a Microsoft SQL Server database as a source for AWS DMS and Working with AWS DMS tasks. You can also see the full plan for this course on the AWS Cloud Administration course page.
