Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
These troubleshooting steps apply to Azure Database Migration Service-based migrations created by using either the Azure DocumentDB Migration Extension or the Azure Database Migration Service experience in the Azure portal.
Azure Database Migration Service (DMS) runs the migration and manages the network connection between your source environment and Azure DocumentDB. When a migration job fails, use these steps to trace where the network connectivity broke down. For private migrations, DMS creates a dedicated ephemeral virtual network for each migration job that is purged as soon as the job completes or fails. You can't inspect the DMS virtual network afterward, so you reproduce its network position to isolate the failure.
Validation prerequisites
| Tool | Purpose |
|---|---|
Azure CLI (az) |
Query Azure networking resources |
| PowerShell or Bash | Run validation scripts |
nc (netcat) or Test-NetConnection |
Test TCP connectivity |
| Network Contributor or Reader RBAC roles | Read access on relevant virtual networks |
Public connectivity validation
In public mode, DMS connects to source and target over the public internet by using static IPs that the migration wizard displays.
Validate source firewall
Review the source MongoDB firewall configuration and confirm that it allows every DMS static IP address displayed in the migration wizard.
The following commands test whether the source endpoint is reachable from your machine. They don't verify that the source firewall allows the DMS static IP addresses because the connection originates from your machine's egress address.
# PowerShell
Test-NetConnection -ComputerName <SOURCE_PUBLIC_IP_OR_HOSTNAME> -Port 27017
# Bash / Linux
nc -zv <SOURCE_PUBLIC_IP_OR_HOSTNAME> 27017
Expected: TcpTestSucceeded : True or Connection succeeded.
Validate target firewall
List the Azure DocumentDB firewall rules and confirm that they allow every DMS static IP address displayed in the migration wizard:
az documentdb mongocluster firewall-rule list \
--cluster-name "<CLUSTER_NAME>" \
--resource-group "<RESOURCE_GROUP>" \
--output table
The following command tests whether the target endpoint is reachable from your machine. It doesn't verify connectivity from the DMS static IP addresses.
Test-NetConnection -ComputerName <DOCUMENTDB_HOSTNAME> -Port 10260
Validate DNS resolution
Resolve-DnsName <SOURCE_HOSTNAME>
Resolve-DnsName <DOCUMENTDB_HOSTNAME>
Verify that both hostnames resolve to public IPs. A private IP response indicates a private DNS override.
Public connectivity checklist
| # | Check | Command | Expected |
|---|---|---|---|
| 1 | Source firewall allows DMS IPs | Review the source firewall configuration | Every DMS IP is in the allow list |
| 2 | Target firewall allows DMS IPs | az documentdb mongocluster firewall-rule list |
Every DMS IP is in the allow list |
| 3 | DNS resolves to public IPs | Resolve-DnsName <hostname> |
Public IP returned |
| 4 | Source is reachable from your machine | Test-NetConnection <source> -Port 27017 |
Succeeded |
| 5 | Target is reachable from your machine | Test-NetConnection <target> -Port 10260 |
Succeeded |
Private connectivity validation
Private connectivity validation has four phases: validate infrastructure, build a simulation environment, test connectivity from the simulation, and clean up.
Phase 1: Validate infrastructure
Source in Azure
Validate both source and target private endpoints:
# Check source private endpoints
az network private-endpoint list \
--subscription "<SUBSCRIPTION>" -g "<SOURCE_RG>" \
--query "[].{Name:name,Subnet:subnet.id,Status:privateLinkServiceConnections[0].privateLinkServiceConnectionState.status}" \
-o table
# Check target private endpoints
az network private-endpoint list \
--subscription "<SUBSCRIPTION>" -g "<TARGET_RG>" \
--query "[].{Name:name,Subnet:subnet.id,Status:privateLinkServiceConnections[0].privateLinkServiceConnectionState.status}" \
-o table
Expected: Status = Approved for both.
Check for CIDR overlap between your virtual networks and the DMS CIDR:
echo "=== Source VNet ==="
az network vnet show --subscription "<SUB>" -g "<SOURCE_RG>" -n "<SOURCE_VNET>" \
--query "addressSpace.addressPrefixes" -o tsv
echo "=== Target VNet ==="
az network vnet show --subscription "<SUB>" -g "<TARGET_RG>" -n "<TARGET_VNET>" \
--query "addressSpace.addressPrefixes" -o tsv
Expected: The DMS CIDR you select in the wizard must not overlap with either virtual network.
Identify the NSG associated with each source and target subnet:
az network vnet subnet show \
--subscription "<SUB>" -g "<SOURCE_RG>" \
--vnet-name "<SOURCE_VNET>" -n "<SOURCE_SUBNET>" \
--query "networkSecurityGroup.id" -o tsv
az network vnet subnet show \
--subscription "<SUB>" -g "<TARGET_RG>" \
--vnet-name "<TARGET_VNET>" -n "<TARGET_SUBNET>" \
--query "networkSecurityGroup.id" -o tsv
List the NSGs in both resource groups, and inspect the rules for the NSGs returned by the preceding commands:
az network nsg list \
--subscription "<SUB>" -g "<SOURCE_RG>" \
--query "[].{Name:name,Rules:securityRules[?direction=='Inbound'].{Rule:name,Priority:priority,Access:access,Source:sourceAddressPrefix,Port:destinationPortRange}}" \
-o json
az network nsg list \
--subscription "<SUB>" -g "<TARGET_RG>" \
--query "[].{Name:name,Rules:securityRules[?direction=='Inbound'].{Rule:name,Priority:priority,Access:access,Source:sourceAddressPrefix,Port:destinationPortRange}}" \
-o json
Verify: No Deny rules block the DMS virtual network CIDR on MongoDB ports (default 27017) or DocumentDB ports (default 10260).
Validate private DNS zone configuration:
# Check private DNS zones
az network private-dns zone list \
--subscription "<SUB>" \
--query "[?contains(name,'mongo') || contains(name,'cosmos') || contains(name,'documentdb')].{Zone:name,RecordSets:numberOfRecordSets}" \
-o table
# Verify DNS zone is linked to relevant VNets
az network private-dns link vnet list \
--subscription "<SUB>" -g "<DNS_ZONE_RG>" -z "<ZONE_NAME>" \
--query "[].{Link:name,VNet:virtualNetwork.id,AutoReg:registrationEnabled}" \
-o table
Azure-to-Azure checklist:
| # | Check | Expected |
|---|---|---|
| 1 | Source private endpoint status | Approved |
| 2 | Target private endpoint status | Approved |
| 3 | No CIDR overlap between your virtual networks and the DMS CIDR | Distinct address ranges |
| 4 | NSGs allow DMS CIDR on required ports | No blocking Deny rules |
| 5 | Private DNS zones linked to your virtual networks | Zones linked, records resolve |
Source on-premises or other cloud
Validate VPN/ExpressRoute tunnel:
# For VPN Gateway
az network vpn-connection list \
--subscription "<SUB>" -g "<GATEWAY_RG>" \
--query "[].{Name:name,Status:connectionStatus,IngressBytes:ingressBytesTransferred,EgressBytes:egressBytesTransferred}" \
-o table
Expected: connectionStatus = Connected, bytes increasing over time.
# For ExpressRoute
az network express-route show \
--subscription "<SUB>" -g "<GATEWAY_RG>" -n "<ER_CIRCUIT_NAME>" \
--query "{Name:name,State:serviceProviderProvisioningState,PeeringState:peerings[0].state}" \
-o table
The DMS virtual network CIDR must be advertised to the remote network, validate BGP route advertisement:
az network vnet-gateway list-advertised-routes \
--subscription "<SUB>" -g "<GATEWAY_RG>" -n "<VPN_GATEWAY_NAME>" \
--peer <REMOTE_BGP_PEER_IP> -o table
Verify that the DMS virtual network CIDR appears in the output. If it's missing, set allowGatewayTransit=true on your gateway virtual network peering.
Validate routing depending on your topology:
- Single virtual network (no hub-spoke): Verify
allowGatewayTransit=trueon your virtual network peering. - Hub-direct: Same as single virtual network, but check the hub virtual network peering.
- TCP proxy: If DMS can only peer to a spoke and direct hub peering isn't possible, deploy a MongoDB migration proxy VM in the DMS-peered virtual network. The proxy forwards traffic from DMS to the source MongoDB server through the existing VPN/ExpressRoute path. Configure the DMS migration job to use the proxy VM's private IP and listen port as the source endpoint.
# Check VNet peering configuration
az network vnet peering list \
--subscription "<SUB>" -g "<YOUR_VNET_RG>" --vnet-name "<YOUR_VNET>" \
--query "[].{Name:name,State:peeringState,AllowGatewayTransit:allowGatewayTransit}" \
-o table
Important
Virtual network peering is non-transitive. If DMS peers to a spoke virtual network, it can't automatically reach the hub's VPN gateway. For DMS traffic to reach on-premises/other-cloud sources, you need either direct hub peering (so DMS can use the VPN gateway natively via useRemoteGateways) or a TCP proxy deployed in the DMS-peered virtual network that forwards traffic to the source through your existing network path.
The on-premises or other-cloud network must have return routes for the DMS virtual network CIDR, validate remote-side return routes. For AWS:
aws ec2 describe-route-tables --region <REGION> \
--route-table-ids <ROUTE_TABLE_ID> \
--query "RouteTables[0].Routes[?GatewayId!=null && starts_with(GatewayId,'vgw')].{Destination:DestinationCidrBlock,Gateway:GatewayId,State:State}" \
--output table
For on-premises routers, verify the DMS CIDR appears in the BGP routing table.
The remote side must also allow inbound traffic from the DMS virtual network CIDR on the required ports (default: 27017 for MongoDB).
DMS creates a new virtual network with a dynamic CIDR for each migration job. Use the specific DMS CIDR shown in the migration wizard when configuring remote firewall rules.
On-premises/other cloud checklist:
| # | Check | Expected |
|---|---|---|
| 1 | VPN/ExpressRoute tunnel status | Connected, bytes flowing |
| 2 | Azure advertises DMS CIDR via BGP | DMS CIDR in advertised routes |
| 3 | Virtual network peering allows gateway transit | allowGatewayTransit=true |
| 4 | Remote side has return route for DMS CIDR | Route present in route table |
| 5 | Remote firewall/SG allows DMS CIDR | Inbound rule on required ports |
| 6 | Target private endpoint status | Approved |
Phase 2: Build simulation environment
DMS creates an ephemeral virtual network for each migration job and removes it when the job finishes or fails. To test connectivity from the DMS network position, create a simulation virtual network with the same CIDR, peer it to your networks, and deploy a test VM.
Testing from a VM in your existing gateway or spoke virtual network only confirms that your VPN works. It doesn't prove that traffic originating from the DMS CIDR can route correctly. The simulation virtual network replicates the exact network position of DMS, so connectivity tests from it represent actual migration traffic.
Create a test virtual network
Use the same CIDR you provided (or plan to provide) in the DMS migration wizard:
SUBSCRIPTION="<YOUR_SUBSCRIPTION>"
TEST_RG="dms-network-test-rg"
LOCATION="<SAME_REGION_AS_DMS>"
DMS_CIDR="<DMS_CIDR_FROM_WIZARD>"
TEST_VNET="dms-test-vnet"
TEST_SUBNET="default"
TEST_SUBNET_CIDR="<SUBNET_WITHIN_DMS_CIDR>"
az group create --subscription "$SUBSCRIPTION" -n "$TEST_RG" -l "$LOCATION"
az network vnet create --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" -n "$TEST_VNET" \
--address-prefix "$DMS_CIDR" \
--subnet-name "$TEST_SUBNET" --subnet-prefix "$TEST_SUBNET_CIDR" \
-l "$LOCATION"
Peer the test virtual network
Create peerings that mirror what DMS does. Choose the peering that matches your topology.
For single virtual network or hub-direct (VPN/ExpressRoute scenarios):
HUB_RG="<HUB_OR_SINGLE_VNET_RESOURCE_GROUP>"
HUB_VNET="<HUB_OR_SINGLE_VNET_NAME>"
TEST_VNET_ID=$(az network vnet show --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" -n "$TEST_VNET" --query id -o tsv)
HUB_VNET_ID=$(az network vnet show --subscription "$SUBSCRIPTION" \
-g "$HUB_RG" -n "$HUB_VNET" --query id -o tsv)
# Gateway VNet → Test VNet
az network vnet peering create --subscription "$SUBSCRIPTION" \
-g "$HUB_RG" --vnet-name "$HUB_VNET" \
-n "hub-to-dms-test" --remote-vnet "$TEST_VNET_ID" \
--allow-vnet-access true --allow-forwarded-traffic true --allow-gateway-transit true
# Test VNet → Gateway VNet
az network vnet peering create --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" --vnet-name "$TEST_VNET" \
-n "dms-test-to-hub" --remote-vnet "$HUB_VNET_ID" \
--allow-vnet-access true --allow-forwarded-traffic true --use-remote-gateways true
For source in Azure (no VPN needed):
SOURCE_RG="<SOURCE_RESOURCE_GROUP>"
SOURCE_VNET="<SOURCE_VNET_NAME>"
SOURCE_VNET_ID=$(az network vnet show --subscription "$SUBSCRIPTION" \
-g "$SOURCE_RG" -n "$SOURCE_VNET" --query id -o tsv)
TEST_VNET_ID=$(az network vnet show --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" -n "$TEST_VNET" --query id -o tsv)
az network vnet peering create --subscription "$SUBSCRIPTION" \
-g "$SOURCE_RG" --vnet-name "$SOURCE_VNET" \
-n "source-to-dms-test" --remote-vnet "$TEST_VNET_ID" \
--allow-vnet-access true --allow-forwarded-traffic true
az network vnet peering create --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" --vnet-name "$TEST_VNET" \
-n "dms-test-to-source" --remote-vnet "$SOURCE_VNET_ID" \
--allow-vnet-access true --allow-forwarded-traffic true
Peer with target virtual network when it differs from the source virtual network:
If the source and target use the same virtual network, skip this block. The source peerings already connect the common virtual network to the test virtual network, and Azure doesn't allow a second peering between the same virtual network pair.
TARGET_RG="<TARGET_RESOURCE_GROUP>"
TARGET_VNET="<TARGET_VNET_NAME>"
TARGET_VNET_ID=$(az network vnet show --subscription "$SUBSCRIPTION" \
-g "$TARGET_RG" -n "$TARGET_VNET" --query id -o tsv)
az network vnet peering create --subscription "$SUBSCRIPTION" \
-g "$TARGET_RG" --vnet-name "$TARGET_VNET" \
-n "target-to-dms-test" --remote-vnet "$TEST_VNET_ID" \
--allow-vnet-access true --allow-forwarded-traffic true
az network vnet peering create --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" --vnet-name "$TEST_VNET" \
-n "dms-test-to-target" --remote-vnet "$TARGET_VNET_ID" \
--allow-vnet-access true --allow-forwarded-traffic true
Verify peerings
az network vnet peering list --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" --vnet-name "$TEST_VNET" \
--query "[].{Name:name,State:peeringState,UseRemoteGateways:useRemoteGateways}" \
-o table
Expected: All peerings show peeringState = Connected.
Deploy a test VM
az vm create --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" -n "dms-test-vm" \
--image Ubuntu2204 --size Standard_B2s \
--vnet-name "$TEST_VNET" --subnet "$TEST_SUBNET" \
--admin-username azureuser --generate-ssh-keys \
--public-ip-address "" --no-wait
The VM has no public IP. Access it via Azure Bastion, serial console, or az vm run-command invoke.
Phase 3: Test connectivity from simulation VM
Run the following commands from the test VM using az vm run-command invoke.
Test DNS resolution
az vm run-command invoke --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" -n "dms-test-vm" \
--command-id RunShellScript \
--scripts "nslookup <SOURCE_HOSTNAME> && echo '---' && nslookup <TARGET_DOCUMENTDB_HOSTNAME>"
Expected: Both resolve to private IPs. If either resolves to a public IP, link the private DNS zone to the test virtual network.
Test TCP connectivity
# Source MongoDB
az vm run-command invoke --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" -n "dms-test-vm" \
--command-id RunShellScript \
--scripts "nc -zv -w 10 <SOURCE_PRIVATE_IP_OR_HOSTNAME> 27017 2>&1"
# Target DocumentDB
az vm run-command invoke --subscription "$SUBSCRIPTION" \
-g "$TEST_RG" -n "dms-test-vm" \
--command-id RunShellScript \
--scripts "nc -zv -w 10 <TARGET_DOCUMENTDB_HOSTNAME> 10260 2>&1"
Expected: Connection … succeeded for both.
Test with mongosh
Connect to the test VM interactively through Azure Bastion or another secure administrative connection. Don't pass credentials in an az vm run-command script because Azure can record the script and its parameters in invocation logs.
Install mongosh on the test VM:
sudo apt-get update -qq && sudo apt-get install -y -qq gnupg curl
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-7.0.gpg
echo 'deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse' | \
sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt-get update -qq && sudo apt-get install -y -qq mongosh
Prompt for each connection string so credentials aren't stored in shell history. Use connection strings that enable TLS and validate the server certificate. Don't add tlsAllowInvalidCertificates=true.
read -rsp "Source MongoDB connection string: " SOURCE_URI
echo
mongosh "$SOURCE_URI" --eval 'db.runCommand({ping:1})'
unset SOURCE_URI
read -rsp "Target Azure DocumentDB connection string: " TARGET_URI
echo
mongosh "$TARGET_URI" --eval 'db.runCommand({ping:1})'
unset TARGET_URI
Expected: { ok: 1 } for both. If nc succeeds but mongosh fails, networking is working correctly and the problem is likely related to authentication credentials or TLS configuration.
Interpret results
| Result | Meaning | Action |
|---|---|---|
Peering state = Disconnected |
CIDR overlap or virtual network config issue | Check for overlapping address spaces |
nc to source times out |
Traffic blocked or not routed | Check firewall rules, return routes, NSGs |
nc succeeds, mongosh TLS error |
TLS/certificate mismatch | Verify tls=true and cert settings |
nc succeeds, mongosh auth error |
Wrong credentials | Verify username/password and auth DB |
| DNS resolves to public IP | Private DNS zone not linked | Link DNS zone to test virtual network |
Effective routes show None for source CIDR |
No route to source | Check VPN gateway/peering config |
If all tests pass but the migration job still fails, the problem likely lies within DMS-managed components rather than your infrastructure. Contact Azure support with these test results to expedite troubleshooting.
Phase 4: Clean up
Important
Clean up the simulation virtual network and VM before retrying the migration job. If they remain, DMS fails to create its own virtual network and VM due to IP address conflicts.
# Remove the test resource group (includes the test VNet and VM)
az group delete --subscription "$SUBSCRIPTION" -n "$TEST_RG" --yes
# Remove peerings on your existing VNets
az network vnet peering delete --subscription "$SUBSCRIPTION" \
-g "<HUB_RG>" --vnet-name "<HUB_VNET>" -n "hub-to-dms-test" 2>/dev/null
az network vnet peering delete --subscription "$SUBSCRIPTION" \
-g "<SOURCE_RG>" --vnet-name "<SOURCE_VNET>" -n "source-to-dms-test" 2>/dev/null
az network vnet peering delete --subscription "$SUBSCRIPTION" \
-g "<TARGET_RG>" --vnet-name "<TARGET_VNET>" -n "target-to-dms-test" 2>/dev/null
Common connectivity issues
| Issue | Symptom | Resolution |
|---|---|---|
| DMS CIDR not advertised through BGP | VPN is up but migration reports connectivity errors | Enable allowGatewayTransit=true on gateway virtual network peering |
| Remote firewall blocks DMS CIDR | Other Azure virtual networks reach source, but DMS times out | Add the DMS CIDR from the wizard to remote firewall rules |
| Proxy NSG blocks DMS CIDR | The proxy can reach the source, but DMS requests to the proxy time out | In the NSG associated with the proxy VM's network interface or subnet, add an inbound rule that allows TCP traffic from the DMS CIDR that you provide in the migration wizard to the proxy's listening port |
| Missing return routes | Traffic leaves Azure but never returns; connection times out | Enable route propagation on remote route table or add static route for DMS CIDR |
| Private endpoint not approved | DNS resolves but connection refused | Approve the private endpoint connection in portal or CLI |
| DNS not resolving correctly | Hostname resolves to public IP instead of private | Link private DNS zone to the virtual network used for migration |
| CIDR overlap | Virtual network peering fails or stays Disconnected |
Select a DMS CIDR that doesn't conflict with existing virtual networks |
| Resource lock blocks peering cleanup | A new migration that uses the same CIDR fails because a source-to-DMS or target-to-DMS peering from a previous job still exists | Remove the lock from the source or target virtual network, or either resource group, delete the leftover peering, and retry the migration |
| Single virtual network for multiple private jobs | Second job fails or interferes with the first | Use different virtual networks for each concurrent migration job |