Home / Databases / SQL Server

DatabasesRunbookIntermediate

Switching off every SQL Agent job for maintenance, then turning back on only the ones that were on

Before patching, a migration cut-over or a DR test you often need all jobs stopped. Save the current enabled state, disable everything, and afterwards restore exactly that state, so jobs that were already disabled stay disabled.

Deepak Bhardwaj3 min read

Applies toSQL Server 2016–2025, Azure SQL Managed Instance

The trap

"Disable all jobs" is easy. "Enable all jobs" afterwards also switches on the jobs that were deliberately disabled: the old archive job that deletes data, the job for a decommissioned app. Always save the state first.

1. Save the current state

USE DBA;   -- your utility database
IF OBJECT_ID('dbo.JobStateSnapshot') IS NULL
  CREATE TABLE dbo.JobStateSnapshot (
    snapshot_name sysname, job_id uniqueidentifier, job_name sysname,
    enabled tinyint, taken_at datetime2 DEFAULT SYSDATETIME(),
    PRIMARY KEY (snapshot_name, job_id));

DECLARE @snap sysname = N'Patch-2026-10-25';

INSERT dbo.JobStateSnapshot (snapshot_name, job_id, job_name, enabled)
SELECT @snap, job_id, name, enabled FROM msdb.dbo.sysjobs;

SELECT enabled, COUNT(*) FROM dbo.JobStateSnapshot WHERE snapshot_name = @snap GROUP BY enabled;

2. Disable everything (except what must keep running)

DECLARE @snap sysname = N'Patch-2026-10-25', @job_id uniqueidentifier;
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
  SELECT job_id FROM dbo.JobStateSnapshot
  WHERE snapshot_name = @snap AND enabled = 1
    AND job_name NOT IN (N'DBA - Log Backups');     -- keep log backups during long work
OPEN c; FETCH NEXT FROM c INTO @job_id;
WHILE @@FETCH_STATUS = 0
BEGIN
  EXEC msdb.dbo.sp_update_job @job_id = @job_id, @enabled = 0;
  FETCH NEXT FROM c INTO @job_id;
END
CLOSE c; DEALLOCATE c;

Use sp_update_job, not UPDATE msdb.dbo.sysjobs. The procedure tells Agent about the change straight away.

3. Stop jobs that are already running

Disabling doesn't stop a running job:

SELECT j.name, a.start_execution_date
FROM msdb.dbo.sysjobactivity a JOIN msdb.dbo.sysjobs j ON j.job_id = a.job_id
WHERE a.session_id = (SELECT MAX(session_id) FROM msdb.dbo.syssessions)
  AND a.start_execution_date IS NOT NULL AND a.stop_execution_date IS NULL;

-- Decide per job: wait for it, or stop it
EXEC msdb.dbo.sp_stop_job @job_name = N'ETL - Load Sales';

4. Restore exactly the saved state

DECLARE @snap sysname = N'Patch-2026-10-25', @job_id uniqueidentifier, @en tinyint;
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
  SELECT s.job_id, s.enabled
  FROM dbo.JobStateSnapshot s
  JOIN msdb.dbo.sysjobs j ON j.job_id = s.job_id         -- skip jobs deleted meanwhile
  WHERE s.snapshot_name = @snap AND j.enabled <> s.enabled;
OPEN c; FETCH NEXT FROM c INTO @job_id, @en;
WHILE @@FETCH_STATUS = 0
BEGIN
  EXEC msdb.dbo.sp_update_job @job_id = @job_id, @enabled = @en;
  FETCH NEXT FROM c INTO @job_id, @en;
END
CLOSE c; DEALLOCATE c;

5. Verify

SELECT s.job_name, s.enabled AS was, j.enabled AS now
FROM dbo.JobStateSnapshot s JOIN msdb.dbo.sysjobs j ON j.job_id = s.job_id
WHERE s.snapshot_name = N'Patch-2026-10-25' AND s.enabled <> j.enabled;

An empty result means every job is back as it was.

Alternatives

Option Effect Use when
Stop the SQL Server Agent service Nothing runs, including alerts Short outages; remember it may restart with the server
Disable jobs (this runbook) Agent and alerts keep working Most maintenance
dbatools Get-DbaAgentJob -SqlInstance SQL01 | Where-Object IsEnabled | Export-Csv then Set-DbaAgentJob -Disabled / -Enabled Many servers at once

Warning

After a migration cut-over, run the restore on the new server and keep jobs disabled on the old one, or both servers will run the same ETL against the same targets.

Checklist

  • Snapshot saved with a clear name
  • Jobs disabled; exceptions documented
  • Running jobs handled
  • State restored and verified