Azure SQL Database → AWS RDS for SQL Server PRODUCTION

Version 1.2.0 Updated 2026-07-05 Engines: SQL Server 2019 SE/EE (both ends) Author: Acme Corp DBA Team

Overview & Scope

This runbook covers a live schema-plus-data migration from Azure SQL Database (PaaS) to AWS RDS for SQL Server using MigrationBridge. The same connection profile supports the reverse direction (RDS → Azure SQL) by swapping the Source and Target fields in Step 1.

Cross-Cloud Firewall (TCP 1433) Both endpoints must permit inbound TCP 1433 from the MigrationBridge host before any connection test will succeed. Azure SQL: add a Server-level firewall rule or use a Private Endpoint. RDS: update the VPC Security Group inbound rule. See § Prereq Source and § Prereq Target for exact commands.

What This SOP Covers

What This SOP Does NOT Cover

Estimated Timelines

Database SizeFull-Copy DurationCT Catch-UpTotal Window
< 10 GB15 – 45 min< 5 min~1 h
10 – 100 GB1 – 4 h5 – 30 min~5 h
100 GB – 1 TB4 – 18 h30 min – 2 h~20 h
> 1 TBContact supportVariablePlan cutover carefully
Bidirectional Support MigrationBridge treats Azure SQL and RDS symmetrically. To migrate in the reverse direction (RDS → Azure SQL), open Step 1 and place the RDS instance in the Source card and Azure SQL in the Target card. All assessment, CT, and verification logic is identical.

Prerequisites — Source (Azure SQL Database)

1. Create a Contained Migration User

Run the following against the source database (not master). Contained users are scoped to the database and do not require a server-level login — the preferred pattern on Azure SQL PaaS.

-- ─────────────────────────────────────────────────────────────────────────────
-- Azure SQL: create contained migration user with minimum required permissions
-- Run in: source database context  (NOT master)
-- ─────────────────────────────────────────────────────────────────────────────
USE [YourSourceDatabase];
GO

-- Create contained user mapped to Azure AD or SQL auth
CREATE USER [mb_migration] WITH PASSWORD = N'Str0ng!Pass#2026';
GO

-- Minimum read permissions
ALTER ROLE [db_datareader] ADD MEMBER [mb_migration];
GO

-- Required for assessment queries (blocking, sessions, query stats)
GRANT VIEW DATABASE STATE TO [mb_migration];
GO

-- Required for Change Tracking (live migration mode only)
GRANT VIEW CHANGE TRACKING TO [mb_migration];
GO

-- Verify
SELECT dp.name, dp.type_desc, drm.role_principal_id
FROM sys.database_principals dp
LEFT JOIN sys.database_role_members drm
    ON dp.principal_id = drm.member_principal_id
WHERE dp.name = N'mb_migration';

2. Azure SQL Firewall — Allow MigrationBridge Host

In the Azure portal or via Azure CLI, add the public IP of the MigrationBridge host.

# Azure CLI — add server-level firewall rule
az sql server firewall-rule create \
  --resource-group <your-rg> \
  --server <azure-sql-server-name> \
  --name MigrationBridgeHost \
  --start-ip-address <mb-host-public-ip> \
  --end-ip-address   <mb-host-public-ip>

3. Readiness Verification Queries

Run all three checks. All must return expected values before proceeding.

-- ① Connectivity & edition check
SELECT
    SERVERPROPERTY('Edition')        AS Edition,
    SERVERPROPERTY('EngineEdition')  AS EngineEdition,   -- 5 = Azure SQL DB
    SERVERPROPERTY('ProductVersion') AS ProductVersion,
    DB_NAME()                          AS CurrentDatabase;

-- ② Confirm user has required permissions
SELECT
    HAS_PERMS_BY_NAME(DB_NAME(), 'DATABASE', 'VIEW DATABASE STATE') AS has_view_db_state,
    IS_ROLEMEMBER('db_datareader')                                  AS is_datareader;

-- ③ Count user tables eligible for migration
SELECT
    COUNT(*) AS total_user_tables,
    SUM(p.rows) AS total_rows_approx
FROM sys.tables t
JOIN sys.partitions p
    ON t.object_id = p.object_id AND p.index_id IN (0,1)
WHERE t.is_ms_shipped = 0;

-- ④ Check Change Tracking status (required for live mode)
SELECT
    is_change_tracking_on,
    retention_period,
    retention_period_units_desc
FROM sys.change_tracking_databases
WHERE database_id = DB_ID();
Expected Results EngineEdition = 5 (Azure SQL Database PaaS). has_view_db_state = 1. is_datareader = 1. If Change Tracking is not yet enabled, see Change Tracking Setup.

Prerequisites — Target (AWS RDS for SQL Server)

1. Open VPC Security Group — TCP 1433

The RDS instance's Security Group must allow inbound TCP 1433 from the MigrationBridge host IP.

# AWS CLI — authorize inbound TCP 1433 from MigrationBridge host
aws ec2 authorize-security-group-ingress \
  --group-id <rds-sg-id> \
  --protocol tcp \
  --port 1433 \
  --cidr <mb-host-public-ip>/32

# Verify rule was added
aws ec2 describe-security-groups \
  --group-ids <rds-sg-id> \
  --query 'SecurityGroups[0].IpPermissions'
NACLs May Also Block Traffic If connectivity still fails after updating the Security Group, check the subnet Network ACL for an explicit DENY on port 1433 in either direction. NACLs are stateless — inbound AND outbound rules must both allow port 1433 and the ephemeral port range (1024–65535).

2. Create SQL Login on RDS

RDS for SQL Server does not support contained users in the same way as Azure SQL PaaS. Create a standard server-level login, then map it to a database user.

-- ─────────────────────────────────────────────────────────────────────────────
-- RDS: create server login + database user (NOT a contained user)
-- Run as: master admin login  (the one created during RDS provisioning)
-- ─────────────────────────────────────────────────────────────────────────────

-- Step A: create login at server scope
USE [master];
GO
CREATE LOGIN [mb_migration] WITH PASSWORD = N'Str0ng!Pass#2026',
    CHECK_POLICY = ON, CHECK_EXPIRATION = OFF;
GO

-- Step B: map to target database
USE [YourTargetDatabase];
GO
CREATE USER [mb_migration] FOR LOGIN [mb_migration];
GO

-- Step C: grant write + schema access for migration inserts
ALTER ROLE [db_datareader]  ADD MEMBER [mb_migration];
ALTER ROLE [db_datawriter]  ADD MEMBER [mb_migration];
ALTER ROLE [db_ddladmin]    ADD MEMBER [mb_migration];
GRANT VIEW DATABASE STATE TO [mb_migration];
GO

-- Verify
SELECT dp.name, rp.name AS role_name
FROM sys.database_role_members drm
JOIN sys.database_principals dp ON drm.member_principal_id = dp.principal_id
JOIN sys.database_principals rp ON drm.role_principal_id   = rp.principal_id
WHERE dp.name = N'mb_migration';

3. Test Connectivity from MigrationBridge Host

# PowerShell — test TCP 1433 reachability to RDS endpoint
Test-NetConnection -ComputerName <rds-endpoint>.rds.amazonaws.com `
                   -Port 1433 -InformationLevel Detailed

# Also test Azure SQL endpoint
Test-NetConnection -ComputerName <server>.database.windows.net `
                   -Port 1433 -InformationLevel Detailed
Option Group Note RDS for SQL Server does not require a custom Option Group for this migration. The default Option Group is sufficient. A custom Parameter Group is recommended for tuning after migration — see Step 6.

4. RDS Instance Checklist

ItemRequirementStatus
SQL Server EditionSE or EE (Express does not support CT)Verify
SQL Server Version2017 or later recommendedVerify
Storage (gp3)≥ 1.2× source database sizeCheck
Multi-AZRecommended for prod; enable after migrationPost-Migration
Backup RetentionMinimum 7 days; 35 days for prodPost-Migration
VPC Security GroupTCP 1433 open from MB hostAction Required
Parameter GroupCustom group for tuning (post-migration)Post-Migration
1

Connect to Source and Target

Enter connection details for both databases. MigrationBridge tests both connections before allowing you to proceed to assessment. All credentials are encrypted in memory and never written to disk.

Az
Source — Azure SQL Database
PaaS · Contained User
acmecorp-prod.database.windows.net
1433
EndriasBridgeWH
SQL Authentication (Contained User)
mb_migration
••••••••••••••
Required (Azure SQL enforces)
Connection OK  RTT 18 ms · SQL Server 2022 (16.x)
RDS
Target — AWS RDS for SQL Server
SE2019 · Standard Login
acmecorp-rds.c9abc1def.us-east-1.rds.amazonaws.com
1433
EndriasBridgeWH
SQL Authentication (Server Login)
mb_migration
••••••••••••••
Optional (TrustServerCertificate=True)
Connection OK  RTT 32 ms · SQL Server 2019 (15.x)
Version Mismatch Warning Source is SQL Server 2022 (16.x); target is SQL Server 2019 (15.x). Features introduced in SQL 2022 (e.g., JSON improvements, ledger tables, IS DISTINCT FROM syntax) may not transfer correctly. The assessment in Step 2 will flag specific incompatibilities.
2

Schema & Compatibility Assessment

MigrationBridge interrogates both engines and flags objects that require manual remediation before or after migration. Resolve all Blocker items before proceeding to Step 3.

Category Finding Severity Action
Cross-Database References 4 views reference OtherDB.dbo.* (3-part names) Blocker Recreate as local views or use synonyms on RDS after DB is in place
Linked Servers 1 linked server definition (AZURESQL_REPORTING) Blocker Linked servers cannot be migrated; reconfigure manually on RDS
SQL Agent Jobs 7 Agent jobs detected in msdb Warning Azure SQL does not expose Agent; export from on-prem or script manually
FILESTREAM / FileTable Not detected Pass No action required
CLR Assemblies 2 SAFE assemblies detected (StringUtils, GeoHelpers) Warning RDS supports CLR with clr enabled; deploy DLL to RDS and re-register
Temporal Tables (SysEnd) 3 temporal tables with GENERATED ALWAYS AS ROW END columns Warning Temporal tables must be versioned-off before bulk insert; re-enable after. See Known Issues.
Change Tracking Readiness CT not yet enabled on source database Warning Enable CT before starting migration if live mode is desired. See CT Setup.
Full-Text Catalogs 1 full-text catalog (FT_EndriasBridgeWH) with 2 indexes Warning Full-text catalogs must be created and populated manually on RDS after migration
Deprecated Data Types text (6 cols), ntext (2 cols), image (1 col), timestamp (3 cols) Advisory MigrationBridge will auto-upgrade to varchar(max) / varbinary(max) / rowversion. Confirm in Type Conversions.
Encrypted Columns (AE) Not detected Pass No action required
Ledger Tables Not detected Pass No action required
Row-Level Security 2 security policies detected Advisory RLS policies will be scripted and applied on target; verify predicate functions exist
Schema Count 6 user schemas Pass All schemas will be created on target before table migration
2 Blocker Items Require Manual Resolution Before Proceeding Cross-database references and linked server definitions cannot be automatically migrated. Document the affected objects, drop or disable them on the source, complete the migration, then recreate them on the RDS target.

Assessment SQL — Run Manually for Cross-DB References

-- Find views and procedures with 3-part (cross-database) object references
SELECT
    o.type_desc        AS ObjectType,
    o.name             AS ObjectName,
    sm.definition
FROM sys.sql_modules sm
JOIN sys.objects     o ON sm.object_id = o.object_id
WHERE sm.definition LIKE '%[DatabaseName]%.%'   -- replace [DatabaseName]
   OR sm.definition LIKE '%].dbo.[%'
ORDER BY o.type_desc, o.name;
3

Select Tables & Migration Mode

Choose which tables to include and select the migration mode. Full Copy performs a complete bulk-insert of all rows. Incremental (CT) applies only changes since the last sync version (requires Change Tracking). Schema Only creates tables without migrating data.

Migration Mode

Full Copy Selected All selected tables will be truncated on the target (if they exist) and re-populated from source. Existing target data will be overwritten. Confirm this is the desired behavior before starting.

Table Selection (9 of 47 shown)

Schema.Table Row Count Data Size Has CT Temporal Status
dbo.BgClaim 4,812,330 2.1 GB Yes No Ready
dbo.BgMember 1,203,450 620 MB Yes No Ready
dbo.BgProvider 89,200 48 MB Yes No Ready
hist.BgClaimHistory 22,471,000 9.4 GB No Yes Needs Prep
ref.DrugFormulary 342,000 88 MB Yes No Ready
ref.ICD10Codes 94,500 24 MB No No Ready
audit.AccessLog 67,200,000 18.2 GB No No Ready
dbo.BgAuthorization 5,620,800 3.3 GB Yes No Ready
cfg.SystemConfig 1,240 0.8 MB No No Ready

47 total tables · 47 selected · 1 requires temporal prep · Estimated total: 33.8 GB

Temporal Table Preparation Required hist.BgClaimHistory (and 2 other temporal tables) must have system-versioning disabled before the bulk insert, then re-enabled after. MigrationBridge will do this automatically when Full Copy mode is selected — confirm the checkbox in the options panel before starting.
4

Run Migration & Monitor Progress

Click Start Migration to begin the full-copy transfer. MigrationBridge streams rows in configurable batches (default 10,000) using SqlBulkCopy with table-lock hints for maximum throughput. Do not close the application during migration.

47
Tables Total
31
Completed
1
In Progress
15
Pending
22.1 GB
Transferred
audit.AccessLog — Row 41,200,000 / 67,200,000 61% · ~22 min remaining
Throughput: 52,300 rows/sec · Batch size: 10,000 · Threads: 4

Live Log Panel

[2026-07-05 14:02:11] INFO Migration started — 47 tables, Full Copy mode
[2026-07-05 14:02:12] INFO Schema creation complete on target (EndriasBridgeWH)
[2026-07-05 14:02:45] OK cfg.SystemConfig — 1,240 rows in 0.3 s
[2026-07-05 14:03:18] OK ref.ICD10Codes — 94,500 rows in 1.2 s
[2026-07-05 14:04:02] OK dbo.BgProvider — 89,200 rows in 1.8 s
[2026-07-05 14:05:30] OK ref.DrugFormulary — 342,000 rows in 3.1 s
[2026-07-05 14:05:31] INFO Disabling system-versioning on hist.BgClaimHistory
[2026-07-05 14:05:32] OK Temporal versioning OFF for hist.BgClaimHistory
[2026-07-05 14:18:14] OK hist.BgClaimHistory — 22,471,000 rows in 772.4 s
[2026-07-05 14:18:15] INFO Re-enabling system-versioning on hist.BgClaimHistory
[2026-07-05 14:18:19] OK Temporal versioning ON for hist.BgClaimHistory
[2026-07-05 14:31:44] OK dbo.BgMember — 1,203,450 rows in 81.2 s
[2026-07-05 14:56:02] OK dbo.BgClaim — 4,812,330 rows in 254.3 s
[2026-07-05 15:10:21] OK dbo.BgAuthorization — 5,620,800 rows in 339.1 s
[2026-07-05 15:10:22] INFO Starting audit.AccessLog (67,200,000 rows — largest table)
[2026-07-05 15:10:22] INFO Batch progress: 41,200,000 / 67,200,000 (61%) — 22 min est.

Monitor from CLI (Headless)

# PowerShell — tail the MigrationBridge log file in real time
Get-Content -Path "C:\MigrationT\Migrationtool\Output\migration_$(Get-Date -f yyyyMMdd).log" `
            -Wait -Tail 20
Do Not Close MigrationBridge During Transfer If the UI is closed, the current table transfer will be interrupted. The migration can be resumed from the last fully-completed table — partially-completed tables will be restarted from row 0. Use UI Busy / Unresponsive if the application appears frozen.
5

Verify Data Integrity

After migration completes, run the row-count spot-check and optional checksum verification to confirm data fidelity. Do not perform cutover until all critical tables pass verification.

Row Count Spot-Check — Source (Azure SQL)

-- Run on SOURCE (Azure SQL Database)
SELECT
    QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) AS TableName,
    SUM(p.rows)                                                       AS RowCount_Source,
    SUM(a.total_pages) * 8 / 1024.0                                  AS SizeMB_Source
FROM sys.tables t
JOIN sys.partitions      p ON t.object_id = p.object_id AND p.index_id IN (0,1)
JOIN sys.allocation_units a ON p.partition_id = a.container_id
WHERE t.is_ms_shipped = 0
GROUP BY t.schema_id, t.name
ORDER BY SUM(p.rows) DESC;

Row Count Spot-Check — Target (RDS)

-- Run on TARGET (AWS RDS for SQL Server) — identical query
SELECT
    QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) AS TableName,
    SUM(p.rows)                                                       AS RowCount_Target,
    SUM(a.total_pages) * 8 / 1024.0                                  AS SizeMB_Target
FROM sys.tables t
JOIN sys.partitions      p ON t.object_id = p.object_id AND p.index_id IN (0,1)
JOIN sys.allocation_units a ON p.partition_id = a.container_id
WHERE t.is_ms_shipped = 0
GROUP BY t.schema_id, t.name
ORDER BY SUM(p.rows) DESC;

Checksum Verification (Critical Tables)

-- Compute checksum for a critical table — run on BOTH source and target
-- Results must match exactly
SELECT
    COUNT(*)                      AS RowCount,
    CHECKSUM_AGG(BINARY_CHECKSUM(*)) AS TableChecksum
FROM dbo.BgClaim;

-- For large tables: checksum a representative sample
SELECT
    CHECKSUM_AGG(BINARY_CHECKSUM(ClaimId, MemberId, ClaimDate, TotalAmount, StatusCode))
        AS SampleChecksum
FROM (
    SELECT TOP 100000 ClaimId, MemberId, ClaimDate, TotalAmount, StatusCode
    FROM dbo.BgClaim
    ORDER BY ClaimId
) s;

Verification Results

TableSource RowsTarget RowsMatchChecksum
dbo.BgClaim 4,812,330 4,812,330 Pass Match
dbo.BgMember 1,203,450 1,203,450 Pass Match
dbo.BgProvider 89,200 89,200 Pass Match
hist.BgClaimHistory 22,471,000 22,471,000 Pass Match
audit.AccessLog 67,200,000 67,200,000 Pass Match
dbo.BgAuthorization 5,620,800 5,620,800 Pass Match
ref.DrugFormulary 342,000 342,000 Pass Match
Verify Data Integrity  All 47 tables passed row-count check · 7 checksums verified
Verification Passed — All Tables Match Source and target row counts are identical for all 47 tables. Checksum verification passed for 7 critical tables. It is safe to proceed to Step 6 (Post-Migration Hardening) and plan application cutover.
6

Post-Migration RDS Hardening

After successful verification, harden the RDS instance for production: extend backup retention, tune the parameter group, enable Multi-AZ, and configure CloudWatch alarms.

1. Extend Automated Backup Retention to 35 Days

# AWS CLI — extend backup retention for PITR coverage
aws rds modify-db-instance \
  --db-instance-identifier acmecorp-rds \
  --backup-retention-period 35 \
  --apply-immediately

# Verify new retention setting
aws rds describe-db-instances \
  --db-instance-identifier acmecorp-rds \
  --query 'DBInstances[0].BackupRetentionPeriod'

2. Custom Parameter Group — SQL Server Tuning

# Step A: create custom parameter group
aws rds create-db-parameter-group \
  --db-parameter-group-name acmecorp-sqlserver-2019-pg \
  --db-parameter-group-family sqlserver-se-15.0 \
  --description "Acme Corp production tuning — SQL Server 2019 SE"

# Step B: set cost threshold for parallelism (default 5 is too low for OLAP)
aws rds modify-db-parameter-group \
  --db-parameter-group-name acmecorp-sqlserver-2019-pg \
  --parameters \
    ParameterName="cost threshold for parallelism",ParameterValue="50",ApplyMethod="immediate"

# Step C: cap max degree of parallelism (8 vCPU instance)
aws rds modify-db-parameter-group \
  --db-parameter-group-name acmecorp-sqlserver-2019-pg \
  --parameters \
    ParameterName="max degree of parallelism",ParameterValue="4",ApplyMethod="immediate"

# Step D: attach parameter group to instance
aws rds modify-db-instance \
  --db-instance-identifier acmecorp-rds \
  --db-parameter-group-name acmecorp-sqlserver-2019-pg \
  --apply-immediately

# Step E: verify parameters applied
aws rds describe-db-parameters \
  --db-parameter-group-name acmecorp-sqlserver-2019-pg \
  --query "Parameters[?ParameterName=='cost threshold for parallelism' || ParameterName=='max degree of parallelism']"

3. Enable Multi-AZ Standby

# Enable Multi-AZ (synchronous standby in secondary AZ)
# NOTE: This triggers a brief failover (~60s) during the conversion window
aws rds modify-db-instance \
  --db-instance-identifier acmecorp-rds \
  --multi-az \
  --apply-immediately

# Monitor status until "available"
aws rds describe-db-instances \
  --db-instance-identifier acmecorp-rds \
  --query 'DBInstances[0].{Status:DBInstanceStatus,MultiAZ:MultiAZ}'
Multi-AZ Conversion Downtime Enabling Multi-AZ on an existing Single-AZ instance requires a failover of approximately 60–120 seconds. Schedule this during a maintenance window. For zero-downtime enablement, create a new Multi-AZ instance and restore from snapshot.

4. CloudWatch Alarms — Three Critical Metrics

# Alarm 1: High CPU Utilization (> 80% for 5 consecutive minutes)
aws cloudwatch put-metric-alarm \
  --alarm-name "acmecorp-rds-cpu-high" \
  --alarm-description "RDS SQL Server CPU > 80% for 5 min" \
  --metric-name CPUUtilization \
  --namespace AWS/RDS \
  --statistic Average \
  --period 60 \
  --evaluation-periods 5 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --dimensions Name=DBInstanceIdentifier,Value=acmecorp-rds \
  --alarm-actions arn:aws:sns:us-east-1:<account-id>:dba-alerts \
  --ok-actions     arn:aws:sns:us-east-1:<account-id>:dba-alerts

# Alarm 2: Low Free Storage (< 20 GB)
aws cloudwatch put-metric-alarm \
  --alarm-name "acmecorp-rds-storage-low" \
  --alarm-description "RDS SQL Server free storage < 20 GB" \
  --metric-name FreeStorageSpace \
  --namespace AWS/RDS \
  --statistic Average \
  --period 300 \
  --evaluation-periods 3 \
  --threshold 21474836480 \
  --comparison-operator LessThanThreshold \
  --dimensions Name=DBInstanceIdentifier,Value=acmecorp-rds \
  --alarm-actions arn:aws:sns:us-east-1:<account-id>:dba-alerts

# Alarm 3: High Connection Count (> 450 connections)
aws cloudwatch put-metric-alarm \
  --alarm-name "acmecorp-rds-connections-high" \
  --alarm-description "RDS SQL Server connection count > 450" \
  --metric-name DatabaseConnections \
  --namespace AWS/RDS \
  --statistic Average \
  --period 60 \
  --evaluation-periods 3 \
  --threshold 450 \
  --comparison-operator GreaterThanThreshold \
  --dimensions Name=DBInstanceIdentifier,Value=acmecorp-rds \
  --alarm-actions arn:aws:sns:us-east-1:<account-id>:dba-alerts

# Verify all three alarms
aws cloudwatch describe-alarms \
  --alarm-name-prefix "acmecorp-rds-" \
  --query 'MetricAlarms[].{Name:AlarmName,State:StateValue}'
Post-Migration Hardening Checklist
  • Backup retention extended to 35 days
  • Custom parameter group applied (CTFP=50, MAXDOP=4)
  • Multi-AZ enabled (or scheduled)
  • Three CloudWatch alarms active (CPU, Storage, Connections)
  • Security group reviewed — remove MB host IP if no longer needed
  • Application connection strings updated to RDS endpoint

Change Tracking Setup (Live Migration)

Change Tracking (CT) enables MigrationBridge to apply row-level changes (INSERT / UPDATE / DELETE) that occurred on the source during the full-copy phase, minimizing the cutover window. The sequence is: enable CT → start full copy → after full copy, apply CT delta → cutover.

Full-Load-First Sequence MigrationBridge records the CT sync version at the start of the full copy. After the full copy completes, it applies all changes with a sync version greater than the recorded baseline. This ensures no changes are lost even if the full copy takes many hours.

Step A — Enable CT on the Source Database

-- Run on SOURCE (Azure SQL Database) — in source database context
USE [EndriasBridgeWH];
GO

ALTER DATABASE [EndriasBridgeWH]
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 30 DAYS, AUTO_CLEANUP = ON);
GO

Step B — Enable CT on Each Table

-- Enable CT on all user tables (run once per table; adjust schema/name)
ALTER TABLE dbo.BgClaim         ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON);
ALTER TABLE dbo.BgMember        ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON);
ALTER TABLE dbo.BgProvider      ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON);
ALTER TABLE dbo.BgAuthorization ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON);
ALTER TABLE ref.DrugFormulary   ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON);
-- Repeat for all tables requiring live sync (not needed for static reference tables)

Step C — Grant VIEW CHANGE TRACKING to Migration User

-- Grant the migration user access to read change tracking data
GRANT VIEW CHANGE TRACKING ON SCHEMA::dbo   TO [mb_migration];
GRANT VIEW CHANGE TRACKING ON SCHEMA::ref   TO [mb_migration];
GRANT VIEW CHANGE TRACKING ON SCHEMA::hist  TO [mb_migration];
GRANT VIEW CHANGE TRACKING ON SCHEMA::audit TO [mb_migration];
GRANT VIEW CHANGE TRACKING ON SCHEMA::cfg   TO [mb_migration];

Step D — Validate CT State

-- Confirm CT is active on database
SELECT
    d.name                        AS DatabaseName,
    ct.is_change_tracking_on,
    ct.retention_period,
    ct.retention_period_units_desc,
    ct.is_auto_cleanup_on,
    CHANGE_TRACKING_CURRENT_VERSION() AS CurrentVersion
FROM sys.databases d
JOIN sys.change_tracking_databases ct ON d.database_id = ct.database_id
WHERE d.name = DB_NAME();

-- Confirm CT is enabled on specific tables
SELECT
    SCHEMA_NAME(t.schema_id) + '.' + t.name AS TableName,
    ct.is_track_columns_updated_on,
    ct.min_valid_version
FROM sys.tables t
JOIN sys.change_tracking_tables ct ON t.object_id = ct.object_id
ORDER BY t.name;
CT Retention Risk If the full copy takes longer than the CT retention window (30 days here), old change records will be auto-cleaned and MigrationBridge will not be able to apply the CT delta. For very large databases, increase retention to 60–90 days before starting the migration, or plan a maintenance window that fits within retention.

UI Busy / Unresponsive During Migration

During large table transfers (tens of millions of rows), the MigrationBridge UI may become temporarily unresponsive as the worker threads are CPU- and I/O-bound. This is expected behavior and does not indicate a crash or hang.

Symptoms

Recommended Actions

  1. Wait at least 5 minutes before concluding the application is hung
  2. Check the log file directly — if it is still being written, migration is running normally
  3. Check Task Manager: if migrationbridge.exe CPU is above 20%, transfer is active
  4. Do NOT close or force-terminate the process — this will interrupt the current table transfer
  5. If the log file has not changed for > 15 minutes AND CPU is at 0%, the process may be deadlocked — see below

Headless CLI Tip — Run Without the UI

For very large migrations, run MigrationBridge in headless mode from a terminal to avoid UI overhead entirely. All progress is written to the log file and can be monitored with PowerShell.

# Launch MigrationBridge in headless mode using a saved profile
python "C:\MigrationT\Migrationtool\launch_gui.pyw" --headless --profile EndriasBridgeWH_AzureToRDS.json

# Monitor progress in real time
Get-Content -Path "C:\MigrationT\Migrationtool\Output\migration_$(Get-Date -f yyyyMMdd).log" `
            -Wait -Tail 20

# Check for errors only
Select-String -Path "C:\MigrationT\Migrationtool\Output\*.log" -Pattern "ERROR|FAIL"

If the Process Is Truly Hung

# PowerShell — check if MigrationBridge has an open SQL connection
Get-Process migrationbridge | Select-Object Name, CPU, WorkingSet64, Responding
-- T-SQL on target RDS — check for blocking sessions
SELECT
    r.session_id,
    r.blocking_session_id,
    r.command,
    r.status,
    r.wait_type,
    r.wait_time / 1000.0 AS wait_sec,
    SUBSTRING(st.text, r.statement_start_offset / 2 + 1,
        (CASE r.statement_end_offset WHEN -1 THEN LEN(CONVERT(nvarchar(max), st.text)) * 2
             ELSE r.statement_end_offset END - r.statement_start_offset) / 2
    ) AS CurrentStatement
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
WHERE r.session_id > 50  -- exclude system sessions
ORDER BY r.blocking_session_id DESC;

Known Issues & Limitations

Issue Affected Feature Severity Workaround
Temporal table SysEnd column incompatibility System-versioned temporal tables Warning MigrationBridge automatically disables system-versioning before bulk insert and re-enables after. Verify via sys.tables WHERE temporal_type = 2 on target post-migration.
Cross-database object references fail Views, procedures with 3-part names Blocker Drop or rewrite affected objects to use local names. After all databases are on RDS, recreate cross-DB references using linked server or synonyms.
SQL Agent Jobs not migrated msdb SQL Agent jobs Warning Script jobs from source using SSMS → SQL Server Agent → Jobs → Script. Apply on RDS target after migration. Adjust job step connection strings.
Linked server definitions dropped sys.servers linked server rows Blocker Recreate linked servers on RDS manually using sp_addlinkedserver after all target databases are live. Update all dependent objects.
CLR assemblies require manual re-registration CLR stored procedures / functions Warning Enable CLR on RDS: set parameter clr enabled = 1 in parameter group. Upload DLL to S3, download to EC2 in same VPC, then run CREATE ASSEMBLY from local path.
TCP 10054 / connection reset mid-transfer Large table bulk inserts over cross-cloud TCP Warning Azure SQL and RDS have idle connection timeouts (~30 min). MigrationBridge reconnects automatically and resumes from the last committed batch. If persistent, reduce batch size to 1,000 and enable keep-alive in the connection string.
Multi-AZ failover interrupts in-flight transfer RDS Multi-AZ automatic failover Warning If an unplanned Multi-AZ failover occurs during migration, MigrationBridge will reconnect and resume from the last committed batch. Monitor the log for reconnect events. Re-run verification after migration completes.
Full-text catalogs not migrated Full-text search indexes and catalogs Advisory MigrationBridge migrates table data only — not full-text catalogs. After migration, run CREATE FULLTEXT CATALOG, CREATE FULLTEXT INDEX, and allow population to complete before enabling full-text queries.

Data Type Conversions — Azure SQL → RDS

Azure SQL Database and AWS RDS for SQL Server are both SQL Server engines; the vast majority of data types are identical. The table below documents 1:1 pass-throughs and the auto-upgrades applied by MigrationBridge for deprecated types.

Source Type (Azure SQL) Target Type (RDS) Conversion Notes
intint1:1No change
bigintbigint1:1No change
smallintsmallint1:1No change
tinyinttinyint1:1No change
decimal(p,s)decimal(p,s)1:1Precision and scale preserved exactly
numeric(p,s)numeric(p,s)1:1No change
floatfloat1:1No change; IEEE 754 double precision
realreal1:1No change; IEEE 754 single precision
moneymoney1:1No change
smallmoneysmallmoney1:1No change
bitbit1:1No change
char(n)char(n)1:1No change; collation inherited from DB default
nchar(n)nchar(n)1:1No change; Unicode fixed-width
varchar(n)varchar(n)1:1No change
varchar(max)varchar(max)1:1No change
nvarchar(n)nvarchar(n)1:1No change
nvarchar(max)nvarchar(max)1:1No change
binary(n)binary(n)1:1No change
varbinary(n)varbinary(n)1:1No change
varbinary(max)varbinary(max)1:1No change
datedate1:1No change
time(n)time(n)1:1No change; fractional precision preserved
datetimedatetime1:1No change; legacy 3.33 ms precision
datetime2(n)datetime2(n)1:1No change; 100 ns precision
smalldatetimesmalldatetime1:1No change
datetimeoffset(n)datetimeoffset(n)1:1No change; time zone offset preserved
uniqueidentifieruniqueidentifier1:1No change; GUID/UUID
xmlxml1:1XML schema collections migrated separately
hierarchyidhierarchyid1:1No change; CLR type supported on RDS
geographygeography1:1Spatial CLR types supported on RDS SE/EE
geometrygeometry1:1Spatial CLR types supported on RDS SE/EE
sql_variantsql_variant1:1No change; verify consuming application handles variant correctly
timestamp Deprecated rowversion Auto-Upgrade SQL Server timestamp is a synonym for rowversion; values are 8-byte binary counters, not datetime values. Upgraded to canonical name.
ntext Deprecated nvarchar(max) Auto-Upgrade Unicode LOB storage. No data loss. Update any application code using UPDATETEXT, WRITETEXT, READTEXT — these do not work with nvarchar(max).
text Deprecated varchar(max) Auto-Upgrade ANSI/DBCS LOB storage. No data loss. Same application code caveat as ntext.
image Deprecated varbinary(max) Auto-Upgrade Binary LOB storage. No data loss. Application code using UPDATETEXT / READTEXT must be updated.
Collation Note Azure SQL Database defaults to SQL_Latin1_General_CP1_CI_AS unless a custom collation was specified during database creation. AWS RDS defaults to the collation of the SQL Server installation (typically SQL_Latin1_General_CP1_CI_AS on US-region instances). MigrationBridge preserves column-level collation from source. Verify database-level collation matches if you use case-sensitive collation-dependent queries.
FILESTREAM / FileTable Not Supported AWS RDS for SQL Server does not support FILESTREAM or FileTable. If source tables contain FILESTREAM columns, MigrationBridge will skip those columns and log a warning. Migrate FILESTREAM data to S3 via a separate process.

MigrationBridge v1.2.0 · SOP-AzureSQL-to-RDS · Acme Corp DBA Team Last updated: 2026-07-05 · Classification: Internal Use Only