Back to Blog
3 min read

Link Feature for Azure SQL Managed Instance

The Link feature for Azure SQL Managed Instance enables near real-time data replication from SQL Server to Azure SQL MI, providing a seamless path for cloud migration and hybrid scenarios.

The Link creates a distributed availability group between your on-premises SQL Server and Azure SQL MI, enabling:

  • Real-time data synchronization
  • Read-scale workloads on Azure
  • Disaster recovery to the cloud
  • Staged migration approach

Prerequisites

-- Check SQL Server version (2019 or later required)
SELECT @@VERSION;

-- Enable availability groups
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'hadr enabled', 1;
RECONFIGURE;

-- Verify certificate for endpoint authentication
SELECT name, certificate_id, pvt_key_encryption_type_desc
FROM sys.certificates
WHERE name LIKE 'AG%';

Step 1: Prepare SQL Server

-- Create database mirroring endpoint
CREATE ENDPOINT Hadr_endpoint
    STATE = STARTED
    AS TCP (LISTENER_PORT = 5022)
    FOR DATABASE_MIRRORING (
        ROLE = ALL,
        AUTHENTICATION = CERTIFICATE AG_Cert,
        ENCRYPTION = REQUIRED ALGORITHM AES
    );

-- Create the database for replication
CREATE DATABASE SalesDB;
ALTER DATABASE SalesDB SET RECOVERY FULL;
BACKUP DATABASE SalesDB TO DISK = 'C:\Backups\SalesDB.bak';
BACKUP LOG SalesDB TO DISK = 'C:\Backups\SalesDB.trn';

Step 2: Configure Azure SQL MI

# Get SQL MI endpoint info
az sql mi show \
    --name mysqlmi \
    --resource-group myResourceGroup \
    --query "{endpoint:fullyQualifiedDomainName,state:state}"

Step 3: Create the Distributed AG

-- On SQL Server
CREATE AVAILABILITY GROUP [DAG_Link]
    WITH (DISTRIBUTED)
    AVAILABILITY GROUP ON
        'OnPremAG' WITH (
            LISTENER_URL = 'TCP://sqlserver.domain.com:5022',
            AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
            FAILOVER_MODE = MANUAL,
            SEEDING_MODE = AUTOMATIC
        ),
        'MI_AG' WITH (
            LISTENER_URL = 'TCP://mysqlmi.12345.database.windows.net:5022',
            AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
            FAILOVER_MODE = MANUAL,
            SEEDING_MODE = AUTOMATIC
        );
# Install the SQL MI Link module
Install-Module -Name Az.Sql -Force

# Connect to Azure
Connect-AzAccount

# Create the link
$linkParams = @{
    ResourceGroupName = "myResourceGroup"
    InstanceName = "mysqlmi"
    Name = "SalesDBLink"
    PrimaryAvailabilityGroupName = "OnPremAG"
    SecondaryAvailabilityGroupName = "MI_AG"
    TargetDatabase = "SalesDB"
    SourceEndpoint = "TCP://sqlserver.domain.com:5022"
}

New-AzSqlInstanceLink @linkParams
-- On SQL Server: Check distributed AG status
SELECT
    ag.name AS distributed_ag_name,
    ar.replica_server_name,
    ars.role_desc,
    ars.synchronization_health_desc,
    ars.connected_state_desc
FROM sys.availability_groups ag
JOIN sys.availability_replicas ar ON ag.group_id = ar.group_id
JOIN sys.dm_hadr_availability_replica_states ars ON ar.replica_id = ars.replica_id
WHERE ag.is_distributed = 1;

-- Check database synchronization
SELECT
    db_name(database_id) AS database_name,
    synchronization_state_desc,
    synchronization_health_desc,
    log_send_queue_size,
    redo_queue_size
FROM sys.dm_hadr_database_replica_states;

Python Application for Read-Scale

import pyodbc

class HybridSQLConnection:
    def __init__(self):
        self.write_conn_str = (
            "Driver={ODBC Driver 18 for SQL Server};"
            "Server=sqlserver.domain.com;"
            "Database=SalesDB;"
            "Trusted_Connection=yes;"
        )

        self.read_conn_str = (
            "Driver={ODBC Driver 18 for SQL Server};"
            "Server=mysqlmi.database.windows.net;"
            "Database=SalesDB;"
            "Authentication=ActiveDirectoryInteractive;"
        )

    def execute_write(self, query, params=None):
        """Execute write operations on-premises"""
        with pyodbc.connect(self.write_conn_str) as conn:
            cursor = conn.cursor()
            cursor.execute(query, params or [])
            conn.commit()
            return cursor.rowcount

    def execute_read(self, query, params=None):
        """Execute read operations on Azure SQL MI"""
        with pyodbc.connect(self.read_conn_str) as conn:
            cursor = conn.cursor()
            cursor.execute(query, params or [])
            return cursor.fetchall()

# Usage
db = HybridSQLConnection()

# Writes go to on-premises
db.execute_write("INSERT INTO Orders (CustomerID, Total) VALUES (?, ?)", [1, 99.99])

# Reads go to Azure SQL MI
orders = db.execute_read("SELECT * FROM Orders WHERE CustomerID = ?", [1])

Failover to Azure SQL MI

-- Break the link and promote Azure SQL MI
ALTER AVAILABILITY GROUP [DAG_Link]
    FAILOVER;

-- After failover, MI becomes read-write
-- Update connection strings in your applications

The Link feature provides a low-risk path to cloud migration while enabling hybrid scenarios during the transition.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.