Is it possible to get server roles and database roles in a same query/script?

Posted on

Question :

Is it possible to get all database roles and server roles in the same query/script? if so then how can it be achieved?

Initially, I wrote two separate queries, one for server roles and another one for database roles I joined the two queries with ‘Union All’ but the issue is it doesn’t show any database Name which I need.

So I approached the problem from a new angle as the end goal is to get all the databases in the SQL server instance with the roles(e.g db_owner). This will be stored in a temp table from where I can export it to excel. So I wrote a script after doing some reading and online searching. In the following query, I am getting the database roles along with the respective database so that issue is resolved but I want server roles as well and I don’t know how to get those using this query. If anyone can help me to modify the script in a way that it provides both the server roles and the database roles along with server and database name.

DECLARE @DatabaseName SYSNAME,    
        @sql VARCHAR(1000)
DECLARE @ResultTable TABLE(DatabaseName VARCHAR(100), ServerName VARCHAR(100), Name VARCHAR(100), TypeDesc Varchar(100), DatabaseRole VARCHAR(100) )
DECLARE DatabaseCursor CURSOR 
     FOR SELECT Name FROM sys.databases
OPEN databaseCursor
FETCH NEXT FROM databaseCursor INTO @DatabaseName
WHILE (@@FETCH_STATUS = 0)
BEGIN
    SET @sql ='SELECT ''' + @DatabaseName + ''' AS [Database],
                      ''' +@@SERVERNAME +  '''AS [Server Name],
                      logins.name Name,
                      logins.type_desc TypeDesc, 
                      roles.name RoleName
               FROM '+@databaseName+'.sys.database_role_members rm
               INNER JOIN '+@databaseName +'.sys.database_principals roles ON rm.role_principal_id = roles.principal_id
               INNER JOIN '+@databaseName+'.sys.database_principals logins ON rm.member_principal_id = logins.principal_id'
    INSERT INTO @ResultTable EXEC(@sql)
    FETCH NEXT FROM databaseCursor INTO @databaseName
END
CLOSE databaseCursor 
DEALLOCATE databaseCursor 
SELECT * FROM @ResultTable;

Answer :

Add an INSERT...SELECT query to get the server-level role memberships. The example below also uses QUOTENAME to properly handle the concatenated literals and identifiers.

DECLARE @DatabaseName SYSNAME,    
        @sql VARCHAR(1000);
DECLARE @ResultTable TABLE(DatabaseName VARCHAR(100), ServerName VARCHAR(100), Name VARCHAR(100), TypeDesc Varchar(100), DatabaseRole VARCHAR(100) );
DECLARE DatabaseCursor CURSOR 
     FOR SELECT Name FROM sys.databases;
OPEN databaseCursor;
FETCH NEXT FROM databaseCursor INTO @DatabaseName;
WHILE (@@FETCH_STATUS = 0)
BEGIN
    SET @sql ='SELECT ' + QUOTENAME(@DatabaseName, '''') + ' AS [Database],
                      ' + QUOTENAME(@@SERVERNAME,'''') +  'AS [Server Name],
                      logins.name Name,
                      logins.type_desc TypeDesc, 
                      roles.name RoleName
               FROM '+QUOTENAME(@databaseName) +'.sys.database_role_members rm
               INNER JOIN '+QUOTENAME(@databaseName) +'.sys.database_principals roles ON rm.role_principal_id = roles.principal_id
               INNER JOIN '+QUOTENAME(@databaseName) +'.sys.database_principals logins ON rm.member_principal_id = logins.principal_id';
    INSERT INTO @ResultTable EXEC(@sql);
    FETCH NEXT FROM databaseCursor INTO @databaseName;
END;
CLOSE databaseCursor; 
DEALLOCATE databaseCursor; 

--get server roles
INSERT INTO @ResultTable 
SELECT
      NULL
    , @@SERVERNAME
    , logins.name
    , logins.type_desc
    , roles.name AS RoleName
FROM master.sys.server_role_members AS rm
JOIN master.sys.server_principals AS roles ON rm.role_principal_id = roles.principal_id AND type_desc = N'SERVER_ROLE'
JOIN master.sys.server_principals AS logins ON rm.member_principal_id = logins.principal_id;

SELECT * FROM @ResultTable;

Leave a Reply

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