Skip to content
Home » Get required permissions for DMVs

Get required permissions for DMVs

Part of my job at work is to update Transact-SQL reference content. System dynamic management views (DMVs) have permissions that are managed in the SQL Server Database Engine source code, so it’s a little tricky to figure out the required permissions for a specific DMV.

This blog post provides a stored procedure that uses sys.fn_builtin_permissions to calculate permissions, but keep in mind that any custom permissions that you’ve implemented, can override the defaults.

SQL Server 2022 introduced new PERFORMANCE and SECURITY permission levels, so think of the first column as >= 2022, and the second column as <= 2019.

You still need SELECT on the DMV itself (system objects grant this by default unless explicitly revoked).

On Azure SQL Database, you can’t grant server-level permissions. Instead, you need built-in roles (for example, ##MS_ServerStateReader##).

Enjoy! This code isn’t the greatest, but it seems to work. It’s free for anyone to use, and no attribution is necessary.

-- Run once in any user DB
CREATE OR ALTER PROCEDURE dbo.DmvPermission
@dmv sysname, @principal sysname = NULL
AS
BEGIN
    SET NOCOUNT ON;

    -- Normalize
    DECLARE @bare AS sysname = CASE WHEN LEFT(@dmv, 4) = 'sys.' THEN SUBSTRING(@dmv, 5, 255) ELSE @dmv END;

    -- Is it a real system view here?
    IF NOT EXISTS (SELECT 1
                   FROM sys.system_views
                   WHERE name = @bare)
        BEGIN
            RAISERROR ('"%s" is not a system view on this instance.', 16, 1, @dmv);
            RETURN;
        END

    DECLARE @is_db_scoped AS BIT = CASE WHEN @bare LIKE N'dm_db\_%' ESCAPE '\' THEN 1 ELSE 0 END;

    -- Heuristics: scope + security-ish DMVs
    DECLARE @is_security AS BIT = CASE WHEN @bare LIKE N'dm[_]%audit[%]%' ESCAPE '\'
                                            OR @bare LIKE N'dm[_]%security[%]%' ESCAPE '\' THEN 1 ELSE 0 END;
    DECLARE @perm AS sysname, @superset AS sysname, @scope AS sysname;
    SET @scope = CASE WHEN @is_db_scoped = 1 THEN 'DATABASE' ELSE 'SERVER' END;
    IF @is_db_scoped = 1
        BEGIN
            IF @is_security = 1
               AND EXISTS (SELECT 1
                           FROM sys.fn_builtin_permissions('DATABASE')
                           WHERE permission_name = 'VIEW DATABASE SECURITY STATE')
                SET @perm = 'VIEW DATABASE SECURITY STATE';
            ELSE
                IF EXISTS (SELECT 1
                           FROM sys.fn_builtin_permissions('DATABASE')
                           WHERE permission_name = 'VIEW DATABASE PERFORMANCE STATE')
                    SET @perm = 'VIEW DATABASE PERFORMANCE STATE';
                ELSE
                    SET @perm = 'VIEW DATABASE STATE';
            SET @superset = CASE WHEN @perm IN ('VIEW DATABASE PERFORMANCE STATE', 'VIEW DATABASE SECURITY STATE') THEN 'VIEW DATABASE STATE' END;
        END
    ELSE
        BEGIN
            IF @is_security = 1
               AND EXISTS (SELECT 1
                           FROM sys.fn_builtin_permissions('SERVER')
                           WHERE permission_name = 'VIEW SERVER SECURITY STATE')
                SET @perm = 'VIEW SERVER SECURITY STATE';
            ELSE
                IF EXISTS (SELECT 1
                           FROM sys.fn_builtin_permissions('SERVER')
                           WHERE permission_name = 'VIEW SERVER PERFORMANCE STATE')
                    SET @perm = 'VIEW SERVER PERFORMANCE STATE';
                ELSE
                    SET @perm = 'VIEW SERVER STATE';
            SET @superset = CASE WHEN @perm IN ('VIEW SERVER PERFORMANCE STATE', 'VIEW SERVER SECURITY STATE') THEN 'VIEW SERVER STATE' END;
        END
    SELECT 'sys.' + @bare AS DMV,
           @scope AS Scope,
           @perm AS RequiredPermission,
           @superset AS SupersetPermissionThatAlsoWorks,
           CASE WHEN @is_db_scoped = 1 THEN HAS_PERMS_BY_NAME(DB_NAME(), 'DATABASE', @perm) ELSE HAS_PERMS_BY_NAME(NULL, NULL, @perm) END AS YouHaveIt;

    -- Optional GRANT script
    IF @principal IS NOT NULL
        BEGIN
            IF @is_db_scoped = 1
                SELECT 'GRANT ' + @perm + ' TO ' + QUOTENAME(@principal) + ';' AS GrantScript;
            ELSE
                SELECT 'USE master; GRANT ' + @perm + ' TO ' + QUOTENAME(@principal) + ';' AS GrantScript;
        END
END
GO

Here are some examples:

EXECUTE dbo.DmvPermission @dmv = 'sys.dm_exec_sessions';
EXECUTE dbo.DmvPermission @dmv = 'sys.dm_server_audit_status';
EXECUTE dbo.DmvPermission @dmv = 'sys.dm_db_index_usage_stats';
EXECUTE dbo.DmvPermission @dmv = 'sys.dm_exec_requests', @principal = 'MyLogin';

If you want to generate the full list of DMVs to check, here’s a little script to generate a script to run the stored procedure.

SELECT 'EXEC dbo.DmvPermission @dmv=''sys.' + name + ''';'
FROM sys.system_objects
WHERE name LIKE 'dm_%' AND type = 'V';

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.