Troubleshoot Azure DocumentDB migration connectivity

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=true on 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