SQL Server User Can't See All Logins

The minimum permission in database to make a user "see" all the users is VIEW DEFINITION.

This permission does not permit the user to alter anything, but it opens to user every definition of database objects: tables, procedures, etc.


Seeing users requires the VIEW DEFINITION permission on the User Principle. This is implied by the ALTER USER permission.

With ALTER USER you can do a handy ALTER ANY USER, which is the right db_accessadmin gets. But there's no such thing for VIEW DEFINITION only.

GRANT ALTER ANY USER TO Foo           --Works
GRANT VIEW DEFINITION ANY USER TO Foo --Syntax Error

So to accomplish what you want, the easiest way would probably be to create a new Database Role, give it through a script VIEW DEFINITION rights to all users in the database. And then assign users to this new role that you want to give these rights.

IF DATABASE_PRINCIPAL_ID('OnlySeeAllUsers') IS NULL
BEGIN
    RAISERROR('OnlySeeAllUsers role is not defined yet, creating it', 1, 1) WITH NOWAIT
    CREATE ROLE OnlySeeAllUsers
END

DECLARE @QUERY NVARCHAR(MAX);
DECLARE @UserName NVARCHAR(256);
DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState INT;

DECLARE userIterator CURSOR LOCAL FAST_FORWARD
FOR
    SELECT [DAT].[name] FROM sys.database_principals AS DAT
    WHERE ([DAT].[TYPE] = 'U' --Windows logins
    OR [DAT].[TYPE] = 'S') --SQL Logins
    AND [DAT].[SID] IS NOT NULL --Filters out sys/information schema, 
                                --which will cause errors otherwise
OPEN userIterator;
FETCH NEXT FROM userIterator INTO @UserName;    
WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        SET @QUERY = N'GRANT VIEW DEFINITION ON USER::' + @UserName + ' TO OnlySeeAllUsers';
        EXEC (@QUERY);
        FETCH NEXT FROM userIterator INTO @UserName;
    END TRY
    BEGIN CATCH
        SELECT @ErrorMessage = ERROR_MESSAGE(), 
        @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(); --Get ErrorInfo

        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState) WITH NOWAIT;
        BREAK; --Abort while
    END CATCH
END    
CLOSE userIterator;
DEALLOCATE userIterator;

The above script can be saved/made into a procedure and rerun when necessary, or one can make a trigger upon creating new users to grant view definition to the database role.

Then to add a user to this database role (SQL Server 2012+)

ALTER ROLE OnlySeeAllUsers 
ADD MEMBER Foo

EXEC sp_addrolemember 'OnlySeeAllUsers', 'Foo' --Method before SQL Server 2012