Fixed Server Roles in SQL Server

Applies to SQL Server 2019, 2022 and 2025.

Fixed server roles are the built-in groups SQL Server gives you for instance-wide permissions. You add a login to a role and it picks up everything that role can do. They’re quick to use, which is exactly why they get overused. Since SQL Server 2022 there are two families of them, and knowing which to reach for is one of the easier ways to tighten up an instance.

The original fixed server roles

Every version from 2019 onwards has the nine classic roles: sysadmin, serveradmin, securityadmin, processadmin, setupadmin, bulkadmin, diskadmin, dbcreator and public. Apart from public, you can’t change what they’re allowed to do; you can only change who is in them.

public is the odd one out. Every login is a member, you can’t remove anyone from it, and anything you grant to it is granted to everybody. I leave it alone.

The newer ##MS_ fixed server roles (2022 and 2025)

SQL Server 2022 added ten more fixed server roles, all named ##MS_Something##, and SQL Server 2025 carries the same set. They were designed for least privilege: each one covers a narrow job rather than “most of the server”. The ones I use most:

  • ##MS_ServerStateReader##: read the server DMVs (what VIEW SERVER STATE gives you). Ideal for monitoring tools.
  • ##MS_DefinitionReader##: see object definitions across the instance (VIEW ANY DEFINITION).
  • ##MS_LoginManager##: create, alter and drop logins, without the power to grant permissions.
  • ##MS_DatabaseConnector##: connect to any database without a user in it.
  • ##MS_DatabaseManager##: create databases, and drop the ones it owns.

The rest split those jobs up further, for example performance-only or security-only versions of the readers. On 2019 none of these exist, so there you grant the individual server permissions instead.

Seeing who is in which role

This is the first thing I run when I’m handed an unfamiliar instance:

SELECT r.name      AS role_name,
       m.name      AS member_name,
       m.type_desc AS member_type
FROM sys.server_role_members AS rm
JOIN sys.server_principals AS r ON r.principal_id = rm.role_principal_id
JOIN sys.server_principals AS m ON m.principal_id = rm.member_principal_id
ORDER BY r.name, m.name;

Members of public aren’t listed because membership is implicit. To see every server role on the instance, including any user-defined ones:

SELECT name, is_fixed_role
FROM sys.server_principals
WHERE type = 'R'
ORDER BY name;

A worked example: a monitoring login

Say a monitoring tool needs to read wait stats, sessions and object definitions, and nothing else. On 2022 or 2025:

CREATE LOGIN [DOMAIN\svc_monitor] FROM WINDOWS;

ALTER SERVER ROLE [##MS_ServerStateReader##] ADD MEMBER [DOMAIN\svc_monitor];
ALTER SERVER ROLE [##MS_DefinitionReader##]  ADD MEMBER [DOMAIN\svc_monitor];

On 2019 the same job is done with plain grants:

GRANT VIEW SERVER STATE   TO [DOMAIN\svc_monitor];
GRANT VIEW ANY DEFINITION TO [DOMAIN\svc_monitor];

Use ALTER SERVER ROLE ... ADD MEMBER rather than sp_addsrvrolemember, which is deprecated.

The common mistake with fixed server roles

The mistake I see most is treating securityadmin as a safe, junior version of sysadmin. It isn’t. A member can grant most server-level permissions and manage logins, so in practice it’s only a short step from full control. Microsoft’s own guidance is to treat it as equivalent to sysadmin. If someone only needs to look after logins, ##MS_LoginManager## on 2022 and 2025 is the right answer, because it can’t hand out permissions.

The second one catches people with the new roles. Being in ##MS_ServerStateReader## also gives VIEW DATABASE STATE in each database, but only where the login can actually get into that database. With no user in the database and no ##MS_DatabaseConnector## membership, the database-level DMVs fail with a “can’t open database” style error, and it looks as if the role doesn’t work. Either create a user in the databases you care about or add the login to ##MS_DatabaseConnector## as well, knowing that it opens the door to every database.

Watch out for

  • CONTROL SERVER isn’t quite sysadmin (it can be denied things), but there are enough ways to escalate from it that you should treat them the same.
  • bulkadmin and ##MS_DatabaseManager## can be used to elevate privileges in some circumstances. Grant them sparingly and audit their use.
  • Anything granted to public goes to every login, including ones created next year.
  • Logins in Windows groups inherit role membership from the group, so the membership query above shows the group, not each person. Check the group in Active Directory too.
  • Copying logins between servers doesn’t copy role membership. My post on replicating SQL Server logins with their SIDs covers the login side; script the ALTER SERVER ROLE statements separately.

Microsoft’s full reference, including the exact permissions behind each role, is on Microsoft Learn: Server-level roles. For more security and setup checks, see my SQL Server DBA Handbook; the Health Check 00 script also lists your sysadmin members.

This topic came up in the SQLServerCentral Question of the Day: SQL Server 2025 Server-level Roles.

Clive Roberts DBA