powershelldba.de · Uwe Janke

Azure SQL Managed Instance: Are Logins and SQL Agent Jobs Automatically Synchronized?

Managed Instance is sold as "SQL Server without the infrastructure", with high availability built in and geo-replication one click away. So it is a fair assumption that logins and Agent jobs simply follow the databases. Within one instance they do. Between instances they do not, and the first time many teams notice is during their first real failover, when the databases are online and the application still cannot log in.

Basis: Microsoft documentation for failover groups and the Managed Instance link as of October 2026. The login and job scripts in this article were tested on SQL Server 2025, which uses the same T-SQL syntax as Managed Instance. Test them after your first planned failover before you rely on them.

The Short Answer

ScenarioLoginsAgent jobs
Built-in HA within one instance (General Purpose or Business Critical, including zone redundancy)YesYes
Failover group between two instancesNoNo
Managed Instance link (SQL Server to MI and back)NoNo
Restore or copy a database to another instanceNoNo

The rule behind the table is simple: logins live in master, jobs live in msdb, and system databases never leave their instance. Everything that replicates between instances replicates user databases only.

Within One Instance: Nothing to Synchronize

The built-in high availability of Managed Instance protects the whole instance, system databases included. In General Purpose, a failed compute node is replaced and the instance starts again on the same remote storage. In Business Critical, the instance runs on four replicas with local storage, kept in sync with Always On technology under the hood. In both tiers, a failover or a maintenance event brings back the same instance with the same master and msdb. Logins, jobs, linked servers and Database Mail are simply there.

Microsoft does not leave anything to sync here, and that is correct. It is also why the question rarely comes up during the first months on Managed Instance: local failovers happen regularly during maintenance, and nobody notices anything.

Failover Groups: The Databases Move, the Instance Does Not

A failover group replicates user databases from a primary instance to a secondary instance in another region. The documentation is explicit: system databases are not replicated to the secondary instance. Scenarios that depend on objects in the system databases, such as server logins and Agent jobs, require the objects to be created manually on the secondary and kept in sync with the primary.

There is exactly one exception: the service master key is copied once, when the failover group is created. Later changes to it on the primary are not replicated. Instance settings such as backup storage redundancy or long-term retention policies are not replicated either.

What exists only on the instance where you created it:

What does replicate: everything inside the user databases. That includes database users, roles, permissions, schemas, procedures, and the data. This is the source of the classic symptom after a failover: the database users are there, but the logins they belong to are missing, or exist with a different SID.

What You See After an Unprepared Failover

Synchronizing Logins

The requirement is not just "the same logins", but the same SID. Database users are mapped to logins by SID, not by name. A SQL login created on the secondary with CREATE LOGIN app WITH PASSWORD = ... gets a new SID, and every database user that belongs to app is orphaned after failover.

For SQL logins, create them on the secondary with the SID and the password hash from the primary. For Microsoft Entra logins the SID is derived from the Entra object ID, so CREATE LOGIN ... FROM EXTERNAL PROVIDER on both instances produces the same SID automatically. The following script runs on the primary and generates the statements for the secondary, including server role memberships and disabled logins. Every CREATE is guarded, so the output can be applied repeatedly:

-- Run on the PRIMARY. Output: script to run on the SECONDARY.
SET NOCOUNT ON;

SELECT N'IF SUSER_SID(N' + QUOTENAME(sl.name, '''') + N') IS NULL CREATE LOGIN ' + QUOTENAME(sl.name)
     + N' WITH PASSWORD = ' + CONVERT(nvarchar(max), CAST(LOGINPROPERTY(sl.name, 'PasswordHash') AS varbinary(256)), 1)
     + N' HASHED, SID = ' + CONVERT(nvarchar(max), sl.sid, 1)
     + N', DEFAULT_DATABASE = ' + QUOTENAME(sl.default_database_name)
     + N', CHECK_POLICY = '     + CASE sl.is_policy_checked     WHEN 1 THEN N'ON' ELSE N'OFF' END
     + N', CHECK_EXPIRATION = ' + CASE sl.is_expiration_checked WHEN 1 THEN N'ON' ELSE N'OFF' END + N';' AS cmd
FROM   sys.sql_logins sl
WHERE  sl.name NOT LIKE N'##%'
  AND  sl.principal_id > 1            -- skip the instance admin login, each instance has its own
UNION ALL
SELECT N'IF SUSER_SID(N' + QUOTENAME(sp.name, '''') + N') IS NULL CREATE LOGIN ' + QUOTENAME(sp.name) + N' FROM EXTERNAL PROVIDER;'
FROM   sys.server_principals sp
WHERE  sp.type IN ('E', 'X')          -- Microsoft Entra user and group logins
UNION ALL
SELECT N'ALTER SERVER ROLE ' + QUOTENAME(r.name) + N' ADD MEMBER ' + QUOTENAME(m.name) + N';'
FROM   sys.server_role_members rm
JOIN   sys.server_principals r ON r.principal_id = rm.role_principal_id
JOIN   sys.server_principals m ON m.principal_id = rm.member_principal_id
WHERE  m.type IN ('S', 'E', 'X')
  AND  m.name NOT LIKE N'##%'
  AND  m.principal_id > 1
UNION ALL
SELECT N'ALTER LOGIN ' + QUOTENAME(sp.name) + N' DISABLE;'
FROM   sys.server_principals sp
WHERE  sp.is_disabled = 1
  AND  sp.type IN ('S', 'E', 'X')
  AND  sp.name NOT LIKE N'##%'
  AND  sp.principal_id > 1;

In the test, three SQL logins (one with a server role, one disabled) were scripted, dropped and recreated from the output. SID, password hash, role membership and disabled state were identical afterwards, the original password worked, and running the script a second time changed nothing.

Handle the output like a password file. The script contains password hashes. Do not store it in a ticket or a repository, and do not leave it in a job step. If you schedule the sync, run it from a place where the output never touches disk, for example a PowerShell job that reads from the primary and executes directly on the secondary.

Two things the script deliberately does not do: it does not drop logins on the secondary that no longer exist on the primary, and it does not change passwords of logins that already exist. Both are decisions with consequences, and an automated sync that removes logins should at least log what it removed. For password changes, a simple approach is to compare the password hash on both sides and recreate the login when it differs. The same idea for on-premises availability groups, as a PowerShell solution, is described in Synchronizing AlwaysOn Logins with PowerShell.

Synchronizing Agent Jobs

Jobs are harder than logins, because a job must exist on both instances but may only do its work on the current primary. Two parts are needed: getting the job definitions to the secondary, and making every job aware of its role.

Make Every Job Role-Aware

A job on the geo-secondary runs on schedule like any other job. The databases are readable but not writable, so anything that changes data fails. The robust pattern is to check the database itself in every step and do nothing if it is not writable:

IF DATABASEPROPERTYEX(N'SalesDb', 'Updateability') = N'READ_WRITE'
    EXEC SalesDb.dbo.usp_NightlyAggregation;

Name the database explicitly. A job step runs in the context of its configured database, often master or msdb, and those are always writable. Databases on the geo-secondary are read-only, so the check does not return READ_WRITE there, and the job step succeeds without doing anything, so the secondary does not produce a stream of failure alerts. Verify the behavior once after your first planned failover.

This pattern has a second benefit. The job definition in msdb does not replicate, but the stored procedure in the user database does. Keep jobs thin: a job step that only calls a procedure in the user database is trivial to recreate, and the actual logic fails over with the data. Note that Agent on Managed Instance supports T-SQL steps, but not CmdExec or PowerShell steps, so the logic belongs in T-SQL anyway.

Getting the Definitions to the Secondary

There are three practical options:

  1. Jobs as code (recommended). Keep every job as a T-SQL deployment script in source control and deploy it to both instances in the same release. With the role check above, the job can be active on both sides. This is the only option where you know exactly which version of a job runs where.
  2. Scheduled copy from the primary. A PowerShell job scripts the jobs on the primary and creates or updates them on the secondary, for example with dbatools (Get-DbaAgentJob and Export-DbaScript, or Copy-DbaAgentJob). Works well for environments where jobs are still created by hand, as long as somebody owns the sync job and its errors.
  3. Microsoft's sample solution. Microsoft publishes scripts ("Azure SQL Managed Instance: Sync Agent Jobs and Logins in Failover Group", version 2.0, July 2024) that store job definitions in a separate database and recreate them on the new primary after a failover. Read the limitations before you use it: a job that is running during the failover loses its progress and starts again at step 1, job history is lost because jobs are deleted and recreated, and jobs that should run only on the primary or only on the secondary are not supported without changes.

The Rest of the Instance: A Checklist

Logins and jobs are the most visible gaps, but not the only ones. Before you call a failover group production-ready, check each of these on the secondary:

ObjectReplicated?What to do
Database users, roles, permissionsYesNothing, but they need matching login SIDs
SQL and Entra logins, server rolesNoScript above, same SID
Agent jobs, schedules, operators, alertsNoJobs as code, role check in every step
Linked servers, server credentialsNoCreate on both; check whether the target is reachable from the secondary region
Database MailNoCreate profile and account on both, same profile name
Server-level certificates and auditsNoCreate on both
Service master keyOnce, at creationLater changes must be repeated manually
Instance settings (backup redundancy, LTR policies)NoConfigure on both

For applications, connect only to the failover group listener (<fog-name>.<zone>.database.windows.net), never to an instance name. Otherwise logins and jobs may be perfect and the application still writes to the old primary. Connection string details are in SQL Server Connection Strings.

Test It Before You Need It

A failover group that has never failed over is a hypothesis. A planned failover takes minutes and is reversible:

# Make the secondary instance the primary (run in the secondary's resource group/region)
Switch-AzSqlDatabaseInstanceFailoverGroup -ResourceGroupName 'rg-sql-weu' `
    -Location 'westeurope' -Name 'fog-sales'

After the switch, check three things: can every application log in through the listener, did the first scheduled run of each job succeed on the new primary, and did the jobs on the new secondary skip their work instead of failing. Then fail back and check the same in the other direction. Repeat the test after every larger change to logins or jobs, or at least twice a year.

The Bottom Line

Within one Managed Instance, logins and Agent jobs are part of the instance and survive every local failover. As soon as a second instance is involved, through a failover group, the Managed Instance link, or a restore, only user databases travel. Logins have to be created on the secondary with the same SID, jobs have to exist on both sides and check whether their database is writable, and linked servers, Database Mail and certificates need the same care. None of this is difficult. It just has to be done before the failover, because during a regional outage is the worst moment to find out what master and msdb contained.

Related reading: Logins, Users, Roles and Permissions, Distributed AG from on-premises to Azure and SQL Server Failover Cluster or Always On Availability Groups?

← Back to Blog