Skip to content

Commit 0cc01d9

Browse files
BrentOzarclaude
andcommitted
sp_Blitz: add Azure SQL Database support
Azure SQL Database (EngineEdition = 5) was permitted past the version gate but had no dedicated skip block, so ~40 checks that rely on msdb, SQL Agent, xp_* procs, sys.master_files, sp_validatelogins, linked servers, or cross-database metadata would fail or throw. Following the existing Managed Instance pattern: - Adds @IsAzureSQLDB flag, initialized from SERVERPROPERTY('EngineEdition'). - Adds an Azure SQL DB skip block after the MI block that adds the incompatible CheckIDs (backup history, SQL Agent, cross-DB, file layout, OS/services, mirroring) to #SkipChecks and emits the CheckID 223 informational summary row. - Fixes CheckID 232 (Data Size) to detect Azure SQL DB via EngineEdition = 5 instead of the legacy Edition = 'SQL Azure' string. - Guards msdb.dbo.sp_send_dbmail email output so @EmailRecipients on Azure SQL DB prints a not-supported message instead of erroring. - Guards the @OutputServerName linked-server output path for the same reason - Azure SQL DB has no sys.servers linked servers. No changes to MI, RDS, Express, or on-prem paths. Scope is skip + drop-in DMV swaps only; backup history, Agent, config, and xp_* checks remain skipped rather than rewritten. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ed578e4 commit 0cc01d9

1 file changed

Lines changed: 146 additions & 41 deletions

File tree

sp_Blitz.sql

Lines changed: 146 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ BEGIN
190190
,@TraceFileIssue bit
191191
-- Flag for Windows OS to help with Linux support
192192
,@IsWindowsOperatingSystem BIT
193+
-- Flag for Azure SQL Database (EngineEdition 5) to skip checks that do not apply
194+
,@IsAzureSQLDB BIT
193195
,@DaysUptime NUMERIC(23,2)
194196
/* For First Responder Kit consistency check:*/
195197
,@spBlitzFullName VARCHAR(1024)
@@ -746,6 +748,9 @@ BEGIN
746748
SELECT @IsWindowsOperatingSystem = 1 ;
747749
END;
748750

751+
-- Flag for Azure SQL Database - used to guard email and linked server output paths
752+
SELECT @IsAzureSQLDB = CASE WHEN CONVERT(INT, SERVERPROPERTY('EngineEdition')) = 5 THEN 1 ELSE 0 END;
753+
749754

750755
IF NOT EXISTS ( SELECT 1
751756
FROM #SkipChecks
@@ -922,6 +927,90 @@ BEGIN
922927
'Managed Instance detected, so we skipped some checks that are not currently possible, relevant, or practical there.' AS Details;
923928
END; /* Azure Managed Instance skipped checks */
924929

930+
/* If the server is Azure SQL Database, skip checks that it doesn't allow */
931+
IF SERVERPROPERTY('EngineEdition') = 5
932+
BEGIN
933+
/* Backup / restore history - msdb does not exist on Azure SQL DB */
934+
INSERT INTO #SkipChecks (CheckID) VALUES (1); /* Full backups */
935+
INSERT INTO #SkipChecks (CheckID) VALUES (2); /* Log backups */
936+
INSERT INTO #SkipChecks (CheckID) VALUES (4); /* Full backup of user DB */
937+
INSERT INTO #SkipChecks (CheckID) VALUES (5); /* Log backup of user DB */
938+
INSERT INTO #SkipChecks (CheckID) VALUES (18); /* Backup to same drive */
939+
INSERT INTO #SkipChecks (CheckID) VALUES (177); /* Disabled Internal Monitoring Features - requires dm_server_registry access */
940+
INSERT INTO #SkipChecks (CheckID) VALUES (186); /* MSDB Backup History Purged Too Frequently */
941+
942+
/* SQL Agent - no Agent on Azure SQL DB */
943+
INSERT INTO #SkipChecks (CheckID) VALUES (6); /* Jobs Owned By Users */
944+
INSERT INTO #SkipChecks (CheckID) VALUES (30); /* Alerts not configured */
945+
INSERT INTO #SkipChecks (CheckID) VALUES (31); /* No enabled operators */
946+
INSERT INTO #SkipChecks (CheckID) VALUES (57); /* Agent Job Runs at Startup */
947+
INSERT INTO #SkipChecks (CheckID) VALUES (59); /* Alerts Configured without Follow Up */
948+
INSERT INTO #SkipChecks (CheckID) VALUES (61); /* Agent alerts for severity 19-25 */
949+
INSERT INTO #SkipChecks (CheckID) VALUES (73); /* No Failsafe Operator Configured */
950+
INSERT INTO #SkipChecks (CheckID) VALUES (79); /* Shrink Database Job */
951+
INSERT INTO #SkipChecks (CheckID) VALUES (94); /* Job failure without operator notification */
952+
INSERT INTO #SkipChecks (CheckID) VALUES (96); /* Agent alerts for corruption */
953+
INSERT INTO #SkipChecks (CheckID) VALUES (98); /* Disabled alerts */
954+
INSERT INTO #SkipChecks (CheckID) VALUES (123); /* Agent Jobs Starting Simultaneously */
955+
INSERT INTO #SkipChecks (CheckID) VALUES (180); /* Maintenance plans */
956+
INSERT INTO #SkipChecks (CheckID) VALUES (181); /* Repetitive maintenance tasks */
957+
INSERT INTO #SkipChecks (CheckID) VALUES (219); /* Alerts without event descriptions */
958+
959+
/* Cross-DB / system-DB access not permitted on Azure SQL DB */
960+
INSERT INTO #SkipChecks (CheckID) VALUES (29); /* Tables in model database */
961+
INSERT INTO #SkipChecks (CheckID) VALUES (55); /* Database owner <> sa - queries master.sys */
962+
INSERT INTO #SkipChecks (CheckID) VALUES (68); /* Last good DBCC CHECKDB - DBCC DBINFO cross-DB */
963+
INSERT INTO #SkipChecks (CheckID) VALUES (69); /* High VLF count - DBCC LOGINFO cross-DB */
964+
INSERT INTO #SkipChecks (CheckID) VALUES (71); /* sysadmin in master.sys.syslogins */
965+
INSERT INTO #SkipChecks (CheckID) VALUES (74); /* Trace flags - DBCC TRACESTATUS */
966+
INSERT INTO #SkipChecks (CheckID) VALUES (97); /* Unusual SQL Server Edition */
967+
INSERT INTO #SkipChecks (CheckID) VALUES (2301); /* sp_validatelogins */
968+
969+
/* File layout / tempdb - cannot read tempdb or system DBs cross-DB from a user DB */
970+
INSERT INTO #SkipChecks (CheckID) VALUES (21); /* Database encrypted - always true on Azure SQL DB */
971+
INSERT INTO #SkipChecks (CheckID) VALUES (24); /* System DB on C drive */
972+
INSERT INTO #SkipChecks (CheckID) VALUES (40); /* TempDB only one data file */
973+
INSERT INTO #SkipChecks (CheckID) VALUES (41); /* TempDB file size/growth mismatch */
974+
INSERT INTO #SkipChecks (CheckID, DatabaseName) VALUES (80, 'master'); /* Max file size set */
975+
INSERT INTO #SkipChecks (CheckID, DatabaseName) VALUES (80, 'model'); /* Max file size set */
976+
INSERT INTO #SkipChecks (CheckID, DatabaseName) VALUES (80, 'msdb'); /* Max file size set */
977+
INSERT INTO #SkipChecks (CheckID, DatabaseName) VALUES (80, 'tempdb'); /* Max file size set */
978+
INSERT INTO #SkipChecks (CheckID) VALUES (172); /* TempDB files on C drive */
979+
980+
/* Server / OS / services - not addressable on Azure SQL DB */
981+
INSERT INTO #SkipChecks (CheckID) VALUES (50); /* Max Server Memory - not user-configurable */
982+
INSERT INTO #SkipChecks (CheckID) VALUES (92); /* Drive space - xp_fixeddrives */
983+
INSERT INTO #SkipChecks (CheckID) VALUES (100); /* Remote DAC */
984+
INSERT INTO #SkipChecks (CheckID) VALUES (192); /* IFI - not applicable */
985+
INSERT INTO #SkipChecks (CheckID) VALUES (193); /* xp_readerrorlog for IFI */
986+
INSERT INTO #SkipChecks (CheckID) VALUES (199); /* Default trace */
987+
INSERT INTO #SkipChecks (CheckID) VALUES (211); /* Power plan - xp_regread */
988+
INSERT INTO #SkipChecks (CheckID) VALUES (212); /* Additional instances - xp_regread */
989+
INSERT INTO #SkipChecks (CheckID) VALUES (224); /* SSRS/SSAS/SSIS Installed */
990+
INSERT INTO #SkipChecks (CheckID) VALUES (258); /* SQL Server service running as LocalSystem */
991+
INSERT INTO #SkipChecks (CheckID) VALUES (259); /* Agent service running as LocalSystem */
992+
INSERT INTO #SkipChecks (CheckID) VALUES (260); /* SQL Server service account in Administrators */
993+
INSERT INTO #SkipChecks (CheckID) VALUES (261); /* Agent service account in Administrators */
994+
995+
/* Replication / mirroring / AGs - not applicable on Azure SQL DB */
996+
INSERT INTO #SkipChecks (CheckID) VALUES (227); /* Database Mirroring */
997+
998+
INSERT INTO #BlitzResults
999+
( CheckID ,
1000+
Priority ,
1001+
FindingsGroup ,
1002+
Finding ,
1003+
URL ,
1004+
Details
1005+
)
1006+
SELECT 223 AS CheckID ,
1007+
0 AS Priority ,
1008+
'Informational' AS FindingsGroup ,
1009+
'Some Checks Skipped' AS Finding ,
1010+
'https://learn.microsoft.com/en-us/azure/azure-sql/database/' AS URL ,
1011+
'Azure SQL Database detected, so we skipped some checks that are not currently possible, relevant, or practical there.' AS Details;
1012+
END; /* Azure SQL Database skipped checks */
1013+
9251014
/*
9261015
That's the end of the SkipChecks stuff.
9271016
The next several tables are used by various checks later.
@@ -9844,7 +9933,7 @@ IF NOT EXISTS ( SELECT 1
98449933
DROP TABLE IF EXISTS #MasterFiles;
98459934
CREATE TABLE #MasterFiles (database_id INT, file_id INT, type_desc NVARCHAR(50), name NVARCHAR(255), physical_name NVARCHAR(255), size BIGINT);
98469935
/* Azure SQL Database doesn't have sys.master_files, so we have to build our own. */
9847-
IF ((SERVERPROPERTY('Edition')) = 'SQL Azure'
9936+
IF (CONVERT(INT, SERVERPROPERTY('EngineEdition')) = 5
98489937
AND (OBJECT_ID('sys.master_files') IS NULL))
98499938
SET @StringToExecute = 'INSERT INTO #MasterFiles (database_id, file_id, type_desc, name, physical_name, size) SELECT DB_ID(), file_id, type_desc, name, physical_name, size FROM sys.database_files;';
98509939
ELSE
@@ -10134,44 +10223,53 @@ IF NOT EXISTS ( SELECT 1
1013410223

1013510224
IF @EmailRecipients IS NOT NULL
1013610225
BEGIN
10137-
10138-
IF @Debug IN (1, 2) RAISERROR('Sending an email.', 0, 1) WITH NOWAIT;
10139-
10140-
/* Database mail won't work off a local temp table. I'm not happy about this hacky workaround either. */
10141-
IF (OBJECT_ID('tempdb..##BlitzResults', 'U') IS NOT NULL) DROP TABLE ##BlitzResults;
10142-
SELECT * INTO ##BlitzResults FROM #BlitzResults;
10143-
SET @query_result_separator = char(9);
10144-
SET @StringToExecute = 'SET NOCOUNT ON;SELECT [Priority] , [FindingsGroup] , [Finding] , [DatabaseName] , [URL] , [Details] , CheckID FROM ##BlitzResults ORDER BY Priority , FindingsGroup , Finding , DatabaseName , Details; SET NOCOUNT OFF;';
10145-
SET @EmailSubject = 'sp_Blitz Results for ' + @@SERVERNAME;
10146-
SET @EmailBody = 'sp_Blitz ' + CAST(CONVERT(DATETIME, @VersionDate, 102) AS VARCHAR(100)) + '. http://FirstResponderKit.org';
10147-
IF @EmailProfile IS NULL
10148-
EXEC msdb.dbo.sp_send_dbmail
10149-
@recipients = @EmailRecipients,
10150-
@subject = @EmailSubject,
10151-
@body = @EmailBody,
10152-
@query_attachment_filename = 'sp_Blitz-Results.csv',
10153-
@attach_query_result_as_file = 1,
10154-
@query_result_header = 1,
10155-
@query_result_width = 32767,
10156-
@append_query_error = 1,
10157-
@query_result_no_padding = 1,
10158-
@query_result_separator = @query_result_separator,
10159-
@query = @StringToExecute;
10226+
10227+
IF @IsAzureSQLDB = 1
10228+
BEGIN
10229+
IF @Debug IN (1, 2) RAISERROR('Skipping email - Database Mail is not available on Azure SQL Database.', 0, 1) WITH NOWAIT;
10230+
PRINT 'Email output is not supported on Azure SQL Database (Database Mail / msdb.dbo.sp_send_dbmail is unavailable). Skipping email send.';
10231+
END;
1016010232
ELSE
10161-
EXEC msdb.dbo.sp_send_dbmail
10162-
@profile_name = @EmailProfile,
10163-
@recipients = @EmailRecipients,
10164-
@subject = @EmailSubject,
10165-
@body = @EmailBody,
10166-
@query_attachment_filename = 'sp_Blitz-Results.csv',
10167-
@attach_query_result_as_file = 1,
10168-
@query_result_header = 1,
10169-
@query_result_width = 32767,
10170-
@append_query_error = 1,
10171-
@query_result_no_padding = 1,
10172-
@query_result_separator = @query_result_separator,
10173-
@query = @StringToExecute;
10174-
IF (OBJECT_ID('tempdb..##BlitzResults', 'U') IS NOT NULL) DROP TABLE ##BlitzResults;
10233+
BEGIN
10234+
10235+
IF @Debug IN (1, 2) RAISERROR('Sending an email.', 0, 1) WITH NOWAIT;
10236+
10237+
/* Database mail won't work off a local temp table. I'm not happy about this hacky workaround either. */
10238+
IF (OBJECT_ID('tempdb..##BlitzResults', 'U') IS NOT NULL) DROP TABLE ##BlitzResults;
10239+
SELECT * INTO ##BlitzResults FROM #BlitzResults;
10240+
SET @query_result_separator = char(9);
10241+
SET @StringToExecute = 'SET NOCOUNT ON;SELECT [Priority] , [FindingsGroup] , [Finding] , [DatabaseName] , [URL] , [Details] , CheckID FROM ##BlitzResults ORDER BY Priority , FindingsGroup , Finding , DatabaseName , Details; SET NOCOUNT OFF;';
10242+
SET @EmailSubject = 'sp_Blitz Results for ' + @@SERVERNAME;
10243+
SET @EmailBody = 'sp_Blitz ' + CAST(CONVERT(DATETIME, @VersionDate, 102) AS VARCHAR(100)) + '. http://FirstResponderKit.org';
10244+
IF @EmailProfile IS NULL
10245+
EXEC msdb.dbo.sp_send_dbmail
10246+
@recipients = @EmailRecipients,
10247+
@subject = @EmailSubject,
10248+
@body = @EmailBody,
10249+
@query_attachment_filename = 'sp_Blitz-Results.csv',
10250+
@attach_query_result_as_file = 1,
10251+
@query_result_header = 1,
10252+
@query_result_width = 32767,
10253+
@append_query_error = 1,
10254+
@query_result_no_padding = 1,
10255+
@query_result_separator = @query_result_separator,
10256+
@query = @StringToExecute;
10257+
ELSE
10258+
EXEC msdb.dbo.sp_send_dbmail
10259+
@profile_name = @EmailProfile,
10260+
@recipients = @EmailRecipients,
10261+
@subject = @EmailSubject,
10262+
@body = @EmailBody,
10263+
@query_attachment_filename = 'sp_Blitz-Results.csv',
10264+
@attach_query_result_as_file = 1,
10265+
@query_result_header = 1,
10266+
@query_result_width = 32767,
10267+
@append_query_error = 1,
10268+
@query_result_no_padding = 1,
10269+
@query_result_separator = @query_result_separator,
10270+
@query = @StringToExecute;
10271+
IF (OBJECT_ID('tempdb..##BlitzResults', 'U') IS NOT NULL) DROP TABLE ##BlitzResults;
10272+
END;
1017510273
END;
1017610274

1017710275
/* Checks if @OutputServerName is populated with a valid linked server, and that the database name specified is valid */
@@ -10180,11 +10278,18 @@ IF NOT EXISTS ( SELECT 1
1018010278
DECLARE @LinkedServerDBCheck NVARCHAR(2000);
1018110279
DECLARE @ValidLinkedServerDB INT;
1018210280
DECLARE @tmpdbchk table (cnt int);
10183-
IF @OutputServerName IS NOT NULL
10281+
IF @OutputServerName IS NOT NULL AND @IsAzureSQLDB = 1
1018410282
BEGIN
10185-
10283+
IF @Debug IN (1, 2) RAISERROR('Skipping linked server output - not supported on Azure SQL Database.', 0, 1) WITH NOWAIT;
10284+
PRINT 'Linked server output (@OutputServerName) is not supported on Azure SQL Database. Skipping remote output.';
10285+
SET @ValidOutputServer = 0;
10286+
SET @ValidOutputLocation = 0;
10287+
END;
10288+
ELSE IF @OutputServerName IS NOT NULL
10289+
BEGIN
10290+
1018610291
IF @Debug IN (1, 2) RAISERROR('Outputting to a remote server.', 0, 1) WITH NOWAIT;
10187-
10292+
1018810293
IF EXISTS (SELECT server_id FROM sys.servers WHERE QUOTENAME([name]) = @OutputServerName)
1018910294
BEGIN
1019010295
SET @LinkedServerDBCheck = 'SELECT 1 WHERE EXISTS (SELECT * FROM '+@OutputServerName+'.master.sys.databases WHERE QUOTENAME([name]) = '''+@OutputDatabaseName+''')';

0 commit comments

Comments
 (0)