domain-separation

Domain Separation for ServiceNow

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "domain-separation" with this command: npx skills add groeimetai/snow-flow/groeimetai-snow-flow-domain-separation

Domain Separation for ServiceNow

Domain Separation enables multi-tenancy by partitioning data and processes between domains.

Domain Architecture

TOP (Global) ├── Domain A (Customer 1) │ ├── Sub-domain A1 │ └── Sub-domain A2 └── Domain B (Customer 2) └── Sub-domain B1

Key Tables

Table Purpose

domain

Domain definitions

sys_user_has_domain

User domain membership

domain_path

Domain hierarchy paths

sys_db_object

Table domain settings

Domain Configuration (ES5)

Create Domain

// Create domain (ES5 ONLY!) var domain = new GlideRecord("domain") domain.initialize()

domain.setValue("name", "Acme Corp") domain.setValue("description", "Domain for Acme Corporation")

// Parent domain (empty for top-level) domain.setValue("parent", parentDomainSysId)

// Domain visibility domain.setValue("active", true)

domain.insert()

Domain-Aware Queries

// Query respecting domain separation (ES5 ONLY!) function getDomainAwareRecords(tableName, query) { var gr = new GlideRecord(tableName)

// Domain separation is automatic when enabled // Records are filtered to user's visible domains

if (query) { gr.addEncodedQuery(query) } gr.query()

var records = [] while (gr.next()) { records.push({ sys_id: gr.getUniqueValue(), sys_domain: gr.getValue("sys_domain"), sys_domain_path: gr.getValue("sys_domain_path"), }) }

return records }

Cross-Domain Access

// Access records across domains (requires elevated privileges) (ES5 ONLY!) function getCrossdomainRecords(tableName) { var gr = new GlideRecord(tableName)

// Disable domain separation for this query gr.setQueryReferences(false)

// Query all domains gr.queryNoDomain()

var records = [] while (gr.next()) { records.push({ sys_id: gr.getUniqueValue(), domain: gr.sys_domain.getDisplayValue(), }) }

return records }

User Domain Membership (ES5)

Assign User to Domain

// Add user to domain (ES5 ONLY!) function addUserToDomain(userSysId, domainSysId, isPrimary) { // Check if already assigned var existing = new GlideRecord("sys_user_has_domain") existing.addQuery("user", userSysId) existing.addQuery("domain", domainSysId) existing.query()

if (existing.next()) { return existing.getUniqueValue() }

// Create assignment var assignment = new GlideRecord("sys_user_has_domain") assignment.initialize() assignment.setValue("user", userSysId) assignment.setValue("domain", domainSysId) assignment.setValue("primary", isPrimary) return assignment.insert() }

Get User's Domains

// Get domains accessible to user (ES5 ONLY!) function getUserDomains(userSysId) { var domains = []

var membership = new GlideRecord("sys_user_has_domain") membership.addQuery("user", userSysId) membership.query()

while (membership.next()) { var domain = membership.domain.getRefRecord() domains.push({ sys_id: domain.getUniqueValue(), name: domain.getValue("name"), is_primary: membership.getValue("primary") === "true", }) }

return domains }

Domain-Separated Tables (ES5)

Configure Table for Domain Separation

// Enable domain separation on table (ES5 ONLY!) // Note: This is typically done via UI, shown for reference

var tableConfig = new GlideRecord("sys_db_object") if (tableConfig.get("name", "u_custom_table")) { // Enable domain separation tableConfig.setValue("domain_separated", true)

// Domain separation type // 'simple' = records belong to one domain // 'containment' = records visible to parent domains tableConfig.setValue("domain_id_type", "simple")

tableConfig.update() }

Create Record in Specific Domain

// Create record in specific domain (ES5 ONLY!) function createInDomain(tableName, data, domainSysId) { var gr = new GlideRecord(tableName) gr.initialize()

// Set field values for (var field in data) { if (data.hasOwnProperty(field)) { gr.setValue(field, data[field]) } }

// Set domain gr.setValue("sys_domain", domainSysId)

return gr.insert() }

Domain Picker (ES5)

Get Available Domains for Picker

// Get domains for domain picker widget (ES5 ONLY!) function getDomainsForPicker() { var domains = [] var userId = gs.getUserID()

// Get user's accessible domains var membership = new GlideRecord("sys_user_has_domain") membership.addQuery("user", userId) membership.query()

while (membership.next()) { var domain = membership.domain.getRefRecord() if (domain.getValue("active") === "true") { domains.push({ sys_id: domain.getUniqueValue(), name: domain.getValue("name"), is_primary: membership.getValue("primary") === "true", is_current: domain.getUniqueValue() === gs.getSession().getCurrentDomainID(), }) } }

// Sort: primary first, then alphabetically domains.sort(function (a, b) { if (a.is_primary && !b.is_primary) return -1 if (!a.is_primary && b.is_primary) return 1 return a.name.localeCompare(b.name) })

return domains }

Switch Current Domain

// Switch user's current domain (ES5 ONLY!) function switchDomain(domainSysId) { var session = gs.getSession()

// Verify user has access var membership = new GlideRecord("sys_user_has_domain") membership.addQuery("user", gs.getUserID()) membership.addQuery("domain", domainSysId) membership.query()

if (!membership.next()) { gs.addErrorMessage("You do not have access to this domain") return false }

// Switch domain session.setDomainID(domainSysId) gs.addInfoMessage("Switched to domain: " + membership.domain.getDisplayValue())

return true }

Domain Visibility Rules (ES5)

Check Domain Visibility

// Check if record is visible in current domain (ES5 ONLY!) function isRecordVisibleInDomain(tableName, recordSysId) { var gr = new GlideRecord(tableName) gr.addQuery("sys_id", recordSysId) gr.query()

// If record is found, it's visible in current domain context return gr.hasNext() }

Get Domain Path

// Get full domain hierarchy path (ES5 ONLY!) function getDomainPath(domainSysId) { var path = []

var domain = new GlideRecord("domain") if (!domain.get(domainSysId)) { return path }

// Build path from current to root while (domain.isValidRecord()) { path.unshift({ sys_id: domain.getUniqueValue(), name: domain.getValue("name"), })

if (!domain.parent) break
domain = domain.parent.getRefRecord()

}

return path }

MSP/Managed Services Patterns (ES5)

Onboard New Tenant

// Create new tenant domain with initial setup (ES5 ONLY!) function onboardTenant(tenantData) { // Create domain var domain = new GlideRecord("domain") domain.initialize() domain.setValue("name", tenantData.name) domain.setValue("parent", tenantData.parentDomain || "") var domainSysId = domain.insert()

// Create tenant admin user var adminUser = new GlideRecord("sys_user") adminUser.initialize() adminUser.setValue("user_name", tenantData.adminEmail) adminUser.setValue("email", tenantData.adminEmail) adminUser.setValue("first_name", tenantData.adminFirstName) adminUser.setValue("last_name", tenantData.adminLastName) var adminSysId = adminUser.insert()

// Assign user to domain addUserToDomain(adminSysId, domainSysId, true)

// Assign tenant admin role var role = new GlideRecord("sys_user_has_role") role.initialize() role.setValue("user", adminSysId) role.setValue("role", getTenantAdminRoleSysId()) role.insert()

return { domain_sys_id: domainSysId, admin_sys_id: adminSysId, } }

MCP Tool Integration

Available Tools

Tool Purpose

snow_query_table

Query domain-aware data

snow_execute_script_with_output

Test domain scripts

snow_find_artifact

Find domain configurations

Example Workflow

// 1. Query domains await snow_query_table({ table: "domain", query: "active=true", fields: "name,parent,sys_id", })

// 2. Get user domain memberships await snow_query_table({ table: "sys_user_has_domain", query: "user=user_sys_id", fields: "domain,primary", })

// 3. Check domain-separated tables await snow_query_table({ table: "sys_db_object", query: "domain_separated=true", fields: "name,label,domain_id_type", })

Best Practices

  • Plan Hierarchy - Design domain structure before implementation

  • Minimal Domains - Only create necessary separation

  • User Access - Assign minimum required domains

  • Testing - Test with domain picker

  • Global Data - Keep shared data in TOP domain

  • Performance - Domain queries add overhead

  • Documentation - Document domain purposes

  • ES5 Only - No modern JavaScript syntax

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

predictive-intelligence

No summary provided by upstream source.

Repository SourceNeeds Review
General

scheduled-jobs

No summary provided by upstream source.

Repository SourceNeeds Review
General

document-management

No summary provided by upstream source.

Repository SourceNeeds Review
General

reporting-dashboards

No summary provided by upstream source.

Repository SourceNeeds Review