What This Table Is

The sys_user_has_role table stores direct role assignments to individual users. This is the junction table that creates many-to-many relationships between sys_user and sys_user_role records. When you explicitly assign a role to a user through the platform UI or via script, a record gets created here.

This table is owned by the Platform module and supports the core identity and access management process. It's part of the broader role-based access control (RBAC) system that determines what users can see and do in the platform. Every record represents one user having one role through direct assignment, not inheritance.

This is a standalone table that doesn't extend any parent table. No other tables extend from it. The relationship structure is simple: it references sys_user for the user and sys_user_role for the role, with additional metadata about the assignment itself.

In large enterprises, this table typically contains 50,000-500,000 records depending on how heavily they rely on direct role assignments versus group memberships. Performance is generally good since it's a simple junction table with indexed reference fields. The biggest volume comes from organizations that assign incident-specific or project-specific roles directly rather than managing them through groups.

When You'll Script Against This Table

You'll most commonly script against this table in Business Rules that need to verify user permissions, Script Includes that handle role management, and Scheduled Jobs that audit role assignments. It's also frequently queried in ACL scripts, Transform Maps during user imports, and custom applications that need to understand user capabilities. Background scripts often hit this table when cleaning up role assignments or performing bulk role management.

Access to this table requires the user_admin role for write operations, though read access is available to users who can view user records. Global scope applications can freely query this table, but scoped applications have limited access and typically need to use platform APIs rather than direct GlideRecord queries.

  • Role verification — checking if a specific user has a specific role directly assigned
  • Bulk role assignment — programmatically adding roles to users based on business logic
  • Role cleanup — removing expired or inappropriate direct role assignments
  • Audit reporting — identifying who has what roles through direct assignment vs groups
  • User onboarding — automatically assigning standard roles based on department or job function
  • Role inheritance analysis — comparing direct assignments to group-inherited roles
  • Temporary role grants — implementing time-bound role assignments with custom logic

Table Gotchas

⚠️

This table only shows direct role assignments, not inherited roles from groups. Use gs.hasRole() or the User object's hasRole() method to check effective permissions including group inheritance.

⚠️

The state field defaults to 'active' but can be set to 'inactive' to temporarily disable a role assignment without deleting the record. Many developers miss inactive assignments in their queries.

  • Duplicate prevention isn't enforced at the database level — you can create multiple active records for the same user-role combination, which can cause confusion in role management
  • The granted_by field is often empty for system-created assignments, making it hard to track assignment origins
⚠️

Deleting records from this table immediately affects user permissions. There's no soft delete or approval process — the role is instantly removed from the user.

  • Performance can degrade when joining this table with large user queries — the user and role reference fields are indexed, but compound queries across all three tables can be slow
  • The inh_count field is a computed counter that can become stale and doesn't accurately reflect current inheritance in all cases
Free Newsletter

Enjoying this? Get one deep-dive per week.

Join 1,000+ ServiceNow pros — scripts, GlideRecord patterns, Flow Designer techniques, and career moves. Free.

No spam · Unsubscribe anytime

The two primary related tables are sys_user and sys_user_role, which this table connects through foreign key relationships. You'll frequently join all three when building user management interfaces or generating role assignment reports. The sys_user_grmember table is equally important because it shows role inheritance through group membership, which is often more significant than direct assignments.

The sys_user_role_contains table defines role hierarchy and inheritance, so you'll query it alongside this table when you need to understand the full scope of permissions a user receives from a direct role assignment. For audit trails, the sys_audit table tracks changes to role assignments when auditing is enabled.

For complete access control analysis, developers typically query this table alongside sys_group_has_role and sys_user_group to get the complete picture of how users acquire their effective roles. This is particularly important for compliance reporting where you need to document all paths by which a user gains specific permissions.