With the plan and landing zone ready (previous post), the execution is a sequence of repeatable steps: connect the networks, replicate servers and data continuously, test, then cut over. This post covers each step with the checks that prevent surprises.
flowchart TB
subgraph AZ[Azure source]
VM[Virtual machines<br/>with replication agent]
SQL[Databases]
BLOB[Blob Storage]
VPNG[Azure VPN Gateway]
end
subgraph AWS[AWS target in ap-south-1]
TGW[Virtual Private Gateway<br/>or Transit Gateway]
STG[Staging subnet<br/>replication servers and EBS]
DMS[DMS replication instance]
DS[DataSync agent]
TEST[Test and cutover instances]
RDS[RDS targets]
S3[S3 buckets]
end
VPNG <-->|IPsec with BGP| TGW
VM -->|TCP 443 and 1500| STG
STG --> TEST
SQL --> DMS --> RDS
BLOB --> DS --> S3
R53[Route 53<br/>low TTL records] --> TEST
1. Connect the networks
A Site-to-Site VPN between an Azure VPN Gateway and an AWS Virtual Private Gateway (or a Transit Gateway when several VPCs need access) gives private connectivity for the whole migration.
- Use two tunnels and BGP so a single tunnel failure does not stop replication.
- Confirm that CIDR ranges do not overlap, and open the needed ports in both Azure network security groups and AWS security groups.
- A VPN is bandwidth-limited. If data volumes are large, estimate copy time first (for example 5 TB at a sustained 500 Mbps takes roughly 22 hours) and consider AWS Direct Connect or Azure ExpressRoute.
2. Replicate servers with AWS Application Migration Service
AWS Application Migration Service (MGN) treats an Azure VM as a source server. An agent installed on the VM copies disks continuously, at block level, to low-cost staging resources in AWS; a launch creates a test or cutover instance from the replicated data. Replication servers in the staging subnet receive traffic on TCP 1500, and the agent talks to AWS service endpoints on TCP 443. AWS offers the service free for the first 90 days per replicating server; check current pricing for anything slower than that.
wget -O ./aws-replication-installer-init https://aws-application-migration-service-ap-south-1.s3.ap-south-1.amazonaws.com/latest/linux/aws-replication-installer-init
sudo chmod +x aws-replication-installer-init
sudo ./aws-replication-installer-init --region ap-south-1
The installer asks for an access key and secret, so create a dedicated IAM user (or temporary credentials) with the AWSApplicationMigrationAgentInstallationPolicy managed policy, and delete it after installation. Then:
- Wait until the source server shows Healthy and the data replication status reaches Continuous, which means the initial sync is finished.
- Set the launch template: instance type (right-sized from measured use), subnet, security groups and tags.
- Launch a test instance, check the application, run a smoke test, and mark the server Ready for cutover once it passes. Repeat tests until they are boring.
- Use post-launch actions (through Systems Manager) to install the SSM agent, apply configuration and register the instance with monitoring.
3. Migrate databases
The right tool depends on the source.
| Source | Approach | Downtime |
|---|---|---|
| Azure Database for PostgreSQL or MySQL | AWS DMS full load plus change data capture, using logical replication (PostgreSQL) or the binary log (MySQL) | Minutes |
| Azure SQL Managed Instance | DMS with change data capture, or native backup and restore to RDS for SQL Server for a one-time copy | Minutes with DMS, longer with backup and restore |
| Azure SQL Database | DMS supports it for full load only; the DMS documentation states that change data capture is not supported for Azure SQL Database | Plan a write freeze for the copy, or use BACPAC export and import for small databases |
Check the current DMS source documentation for your exact engine and version before committing to a downtime estimate. A typical DMS task for the supported cases:
aws dms create-replication-task \
--replication-task-identifier shop-pg-cdc \
--source-endpoint-arn $SRC_ARN --target-endpoint-arn $TGT_ARN \
--replication-instance-arn $REPL_ARN \
--migration-type full-load-and-cdc \
--table-mappings file://mappings.json
Place the replication instance in a private subnet that can reach Azure over the VPN. Validate with DMS data validation, compare row counts and checksums on critical tables, and test the application against the target before cutover. Convert schema differences first (the AWS Schema Conversion Tool or DMS Schema Conversion helps when the engine changes).
4. Copy files
AWS DataSync supports Azure Blob Storage as a source location and transfers to S3 with scheduling, verification and bandwidth limits, using a DataSync agent. For small data sets, azcopy and aws s3 sync are enough:
aws s3 sync ./export s3://acme-assets-prod --storage-class STANDARD_IA --sse AES256
Run an initial copy, then incremental passes close to cutover. Check egress charges from Azure first.
5. Cutover runbook
| When | Action |
|---|---|
| T minus 7 days | Lower DNS TTLs to 60 seconds; freeze the change calendar; confirm test launches passed |
| T minus 1 day | Final test launch; confirm replication is continuous and DMS lag is near zero; confirm rollback steps |
| T (window opens) | Announce the freeze; stop writes (maintenance page); wait for replication lag to reach zero |
| T plus 30 min | Launch cutover instances; point the application at the RDS target; run smoke tests |
| T plus 1 hour | Switch DNS (a Route 53 weighted record allows a gradual shift); watch error rate and latency |
| T plus 1 to 14 days | Keep Azure running read-only; compare data; then stop replication and decommission |
Decide in advance the point of no return, the moment after which new writes exist only in AWS. Before it, rollback is a DNS change back to Azure. After it, rollback needs reverse replication, which must be planned and tested, not improvised.
Finally, finalize cutover in MGN (this stops replication and removes staging resources), delete the temporary IAM users, update backups, monitoring and cost allocation tags, and schedule the Azure decommission so that nothing keeps billing.