Configure SCOM Health Service account for SQL Server
SQL Server 2014 (12.x) introduced the CONNECT ANY DATABASE permission. With this permission it becomes unnecessary to add the SCOM Health Service account to each individual user database. The script in the Microsoft docs does not make use of the new permissions. I created my own version of the script and made it idempotent.
Executing the script will remove all configuration related to the Health Service account and then reapply the configuration so the result will always be the same. If you used the script from the Microsoft docs, the one from Kevin Holman or even and older version of my script it should execute without issues and apply the latest configuration.
Details of what the script does and why can be found below with the full script at the end of this blog post.
Cleanup current configuration
If any existing Health Service configuration exists we need to remove this configuration before we apply the new configuration. The database user and database role are removed from all databases then the Health Service login and server role are removed from the SQL Server. This gives us a clean environment to start from.
USE [master]; SET NOCOUNT ON; DECLARE @accountname sysname = 'NT SERVICEHealthService'; DECLARE @rolename sysname = 'SCOM_HealthService'; DECLARE @createLoginCommand NVARCHAR(MAX) = 'CREATE LOGIN '+ QUOTENAME(@accountname) +' FROM WINDOWS WITH DEFAULT_DATABASE = [master];'; DECLARE @createusercommand NVARCHAR(MAX) = 'CREATE USER ' + QUOTENAME(@accountname) + ' FOR LOGIN ' + QUOTENAME(@accountname) + ' WITH DEFAULT_SCHEMA = [dbo];'; -- Remove current configuration from all databases DECLARE @droprolecommand NVARCHAR(MAX) = 'USE [?]; IF EXISTS (SELECT * FROM sys.database_principals WHERE [name] = ''' + @rolename + ''') BEGIN DROP ROLE ' + QUOTENAME(@rolename) + '; END;' DECLARE @dropschemacommand NVARCHAR(MAX) = 'USE [?]; IF EXISTS (SELECT 1 FROM sys.schemas WHERE [name] = ''' + @accountname + ''') BEGIN DROP SCHEMA ' + QUOTENAME(@accountname) + ' END;'; DECLARE @dropusercommand NVARCHAR(MAX) = 'USE [?]; IF EXISTS (SELECT 1 FROM sys.database_principals WHERE [name] = ''' + @accountname + ''') BEGIN DROP USER ' + QUOTENAME(@accountname) + ' END;'; EXECUTE sp_MSforeachdb @command1 = @dropschemacommand, @command2 = @dropusercommand, @command3 = @droprolecommand -- Remove server level configuration DECLARE @droplogincommand NVARCHAR(MAX) = 'IF EXISTS (SELECT 1 FROM sys.server_principals WHERE [name] = ''' + @accountname + ''') BEGIN DROP LOGIN ' + QUOTENAME(@accountname) + ' END;'; DECLARE @dropserverrolecommand NVARCHAR(MAX) = 'IF EXISTS (SELECT 1 FROM sys.server_principals WHERE [name] = ''' + @accountname + ''') BEGIN DROP LOGIN ' + QUOTENAME(@accountname) + ' END;'; EXEC (@droplogincommand) EXEC (@dropserverrolecommand)
Drop the login.
EXEC(@droplogincommand)
Drop the server role on supported version. User defined server roles were added in SQL Server 2012.
IF CAST(SERVERPROPERTY('ProductMajorVersion') AS int) >= 11 BEGIN EXEC(@dropserverrolecommand) END;
Create login and add server level permissions
USE [master]; SET NOCOUNT ON; DECLARE @accountname sysname = 'NT SERVICEHealthService'; -- Create the server role and grant instance level permissions IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = 'SCOM_HealthService' AND [type] = 'R') BEGIN CREATE SERVER ROLE [SCOM_HealthService]; END; GRANT VIEW ANY DATABASE TO [SCOM_HealthService]; GRANT VIEW ANY DEFINITION TO [SCOM_HealthService]; GRANT VIEW SERVER STATE TO [SCOM_HealthService]; --GRANT ALTER ANY DATABASE TO [SCOM_HealthService]; --Required only for SCOMDB tasks -- Add the GRANT CONNECT ANY DATABASE permission to SQL Server 2014 and newer IF CAST(SERVERPROPERTY('ProductMajorVersion') AS int) >= 12 BEGIN EXEC ('GRANT CONNECT ANY DATABASE TO [SCOM_HealthService];'); END; -- Create the login on server level IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = @accountname AND [type] = 'U') BEGIN DECLARE @createLoginCommand NVARCHAR(MAX); SET @createLoginCommand = ' CREATE LOGIN '+ QUOTENAME(@accountname) +' FROM WINDOWS WITH DEFAULT_DATABASE=[master];' EXEC(@createLoginCommand); END; EXEC sp_addsrvrolemember @loginame = @accountname, @rolename = 'SCOM_HealthService' -- Create database specific roles and users for SQL Server 2012 and older IF CAST(SERVERPROPERTY('ProductMajorVersion') AS int) < 12 BEGIN -- Add the login and database role to each database DECLARE @createDatabaseUserAndRole nvarchar(max) SET @createDatabaseUserAndRole = ''; SELECT @createDatabaseUserAndRole = @createDatabaseUserAndRole + ' USE ' + QUOTENAME(db.name) + '; CREATE USER ' + QUOTENAME(@accountname) + ' FOR LOGIN ' + QUOTENAME(@accountname) + '; CREATE ROLE [SCOM_HealthService]; EXEC sp_addrolemember @rolename = ''SCOM_HealthService'', @membername = '+ QUOTENAME(@accountname) + '' FROM sys.databases db LEFT JOIN sys.dm_hadr_availability_replica_states hadrstate ON db.replica_id = hadrstate.replica_id WHERE db.database_id != 2 AND db.user_access = 0 AND db.state = 0 AND db.is_read_only = 0 AND (hadrstate.role = 1 or hadrstate.role is null); EXEC(@createDatabaseUserAndRole) END; DECLARE @createusercommand NVARCHAR(MAX) = 'CREATE USER ' + QUOTENAME(@accountname) + ' FOR LOGIN ' + QUOTENAME(@accountname) + ';';
Add master database configuration
We’re adding some specific permissions in the master database so SCOM will be able to read the SQL Server Logs.
-- Add master database specific permissions USE [master] DECLARE @createusercommand NVARCHAR(MAX) = 'CREATE USER ' + QUOTENAME(@accountname) + ' FOR LOGIN ' + QUOTENAME(@accountname) + ';'; IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE [name] = 'SCOM_HealthService' AND [type] = 'R') BEGIN EXEC('CREATE ROLE [SCOM_HealthService];'); END; GRANT EXECUTE ON sys.xp_readerrorlog TO [SCOM_HealthService]; IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE [name] = @accountname) BEGIN EXEC(@createusercommand); END; EXEC sp_addrolemember @membername = @accountname, @rolename = 'SCOM_HealthService';
Add msdb database configuration
We’re adding some specific permissions in the msdbdatabase so SCOM will be able to monitor the SQL Server Agent Job health.
-- CONFIGURE MSDB DATABASE SPECIFIC PERMISSIONS USE [msdb]; IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE [name] = 'SCOM_HealthService' AND [type] = 'R') BEGIN EXEC('CREATE ROLE [SCOM_HealthService];'); END; IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE [name] = @accountname) BEGIN EXEC(@createusercommand); END; EXEC sp_addrolemember @membername = @accountname, @rolename = 'SCOM_HealthService'; GRANT SELECT on [dbo].[sysjobschedules] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[sysschedules] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[sysjobs_view] TO [SCOM_HealthService]; GRANT SELECT ON [dbo].[syscategories] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_primary_databases] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_secondary_databases] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_monitor_history_detail] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_monitor_secondary] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_monitor_primary] TO [SCOM_HealthService]; GRANT EXECUTE on [dbo].[sp_help_job] TO [SCOM_HealthService]; GRANT EXECUTE on [dbo].[sp_help_jobactivity] TO [SCOM_HealthService]; GRANT EXECUTE on [dbo].[SQLAGENT_SUSER_SNAME] TO [SCOM_HealthService]; EXEC sp_addrolemember @rolename='SQLAgentReaderRole', @membername= 'SCOM_HealthService'; EXEC sp_addrolemember @rolename='PolicyAdministratorRole', @membername= 'SCOM_HealthService';
Full Script
USE [master]; SET NOCOUNT ON; DECLARE @accountname sysname = 'NT SERVICEHealthService'; -- Create the server role and grant instance level permissions IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = 'SCOM_HealthService' AND [type] = 'R') BEGIN CREATE SERVER ROLE [SCOM_HealthService]; END; GRANT VIEW ANY DATABASE TO [SCOM_HealthService]; GRANT VIEW ANY DEFINITION TO [SCOM_HealthService]; GRANT VIEW SERVER STATE TO [SCOM_HealthService]; --GRANT ALTER ANY DATABASE TO [SCOM_HealthService]; --Required only for SCOMDB tasks -- Add the GRANT CONNECT ANY DATABASE permission to SQL Server 2014 and newer IF CAST(SERVERPROPERTY('ProductMajorVersion') AS int) >= 12 BEGIN EXEC ('GRANT CONNECT ANY DATABASE TO [SCOM_HealthService];'); END; -- Create the login on server level IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = @accountname AND [type] = 'U') BEGIN DECLARE @createLoginCommand NVARCHAR(MAX); SET @createLoginCommand = ' CREATE LOGIN '+ QUOTENAME(@accountname) +' FROM WINDOWS WITH DEFAULT_DATABASE=[master];' EXEC(@createLoginCommand); END; EXEC sp_addsrvrolemember @loginame = @accountname, @rolename = 'SCOM_HealthService' -- Create database specific roles and users for SQL Server 2012 and older IF CAST(SERVERPROPERTY('ProductMajorVersion') AS int) < 12 BEGIN -- Add the login and database role to each database DECLARE @createDatabaseUserAndRole nvarchar(max) SET @createDatabaseUserAndRole = ''; SELECT @createDatabaseUserAndRole = @createDatabaseUserAndRole + ' USE ' + QUOTENAME(db.name) + '; IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE [name] = ''' + @accountname + ''' AND [type] = ''U'') CREATE USER ' + QUOTENAME(@accountname) + ' FOR LOGIN ' + QUOTENAME(@accountname) + '; IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE [name] = ''SCOM_HealthService'' AND [type] = ''R'') CREATE ROLE [SCOM_HealthService]; EXEC sp_addrolemember @rolename = ''SCOM_HealthService'', @membername = '+ QUOTENAME(@accountname) + '' -- 'ALTER ROLE [SCOM_HealthService] ADD MEMBER ' + QUOTENAME(@accountname) + ';' FROM sys.databases db LEFT JOIN sys.dm_hadr_availability_replica_states hadrstate ON db.replica_id = hadrstate.replica_id WHERE db.database_id != 2 AND db.user_access = 0 AND db.state = 0 AND db.is_read_only = 0 AND (hadrstate.role = 1 or hadrstate.role is null); EXEC(@createDatabaseUserAndRole) END; DECLARE @createusercommand NVARCHAR(MAX) = 'CREATE USER ' + QUOTENAME(@accountname) + ' FOR LOGIN ' + QUOTENAME(@accountname) + ';'; -- CONFIGURE MASTER DATABASE SPECIFIC PERMISSIONS USE [master]; IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE [name] = 'SCOM_HealthService' AND [type] = 'R') BEGIN EXEC('CREATE ROLE [SCOM_HealthService];') END; IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE [name] = @accountname) BEGIN EXEC(@createusercommand); END; -- Add database specific permissions to database role GRANT EXECUTE ON sys.xp_readerrorlog TO [SCOM_HealthService]; EXEC sp_addrolemember @membername = @accountname, @rolename = 'SCOM_HealthService'; -- CONFIGURE MSDB DATABASE SPECIFIC PERMISSIONS USE [msdb]; IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE [name] = 'SCOM_HealthService' AND [type] = 'R') BEGIN EXEC('CREATE ROLE [SCOM_HealthService];'); END; IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE [name] = @accountname) BEGIN EXEC(@createusercommand); END; EXEC sp_addrolemember @membername = @accountname, @rolename = 'SCOM_HealthService'; GRANT SELECT on [dbo].[sysjobschedules] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[sysschedules] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[sysjobs_view] TO [SCOM_HealthService]; GRANT SELECT ON [dbo].[syscategories] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_primary_databases] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_secondary_databases] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_monitor_history_detail] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_monitor_secondary] TO [SCOM_HealthService]; GRANT SELECT on [dbo].[log_shipping_monitor_primary] TO [SCOM_HealthService]; GRANT EXECUTE on [dbo].[sp_help_job] TO [SCOM_HealthService]; GRANT EXECUTE on [dbo].[sp_help_jobactivity] TO [SCOM_HealthService]; GRANT EXECUTE on [dbo].[SQLAGENT_SUSER_SNAME] TO [SCOM_HealthService]; EXEC sp_addrolemember @rolename='SQLAgentReaderRole', @membername= 'SCOM_HealthService'; EXEC sp_addrolemember @rolename='PolicyAdministratorRole', @membername= 'SCOM_HealthService';