โ† Back to list
groeimetai

domain-separation

by groeimetai

๐Ÿค– AI-powered ServiceNow development with 400+ MCP tools. Works with Claude, GPT, Gemini, Ollama & 75+ providers. Deploy widgets, manage incidents, automate workflows - all through natural language. Open-source Build Agent alternative.

โญ 42๐Ÿด 9๐Ÿ“… Jan 23, 2026

SKILL.md


name: domain-separation description: This skill should be used when the user asks to "domain separation", "multi-tenant", "domain path", "domain visibility", "domain picker", "MSP", "managed services", or any ServiceNow Domain Separation development. license: Apache-2.0 compatibility: Designed for Snow-Code and ServiceNow development metadata: author: groeimetai version: "1.0.0" category: servicenow tools:

  • snow_query_table
  • snow_execute_script_with_output
  • snow_find_artifact

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

TablePurpose
domainDomain definitions
sys_user_has_domainUser domain membership
domain_pathDomain hierarchy paths
sys_db_objectTable 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

ToolPurpose
snow_query_tableQuery domain-aware data
snow_execute_script_with_outputTest domain scripts
snow_find_artifactFind 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

  1. Plan Hierarchy - Design domain structure before implementation
  2. Minimal Domains - Only create necessary separation
  3. User Access - Assign minimum required domains
  4. Testing - Test with domain picker
  5. Global Data - Keep shared data in TOP domain
  6. Performance - Domain queries add overhead
  7. Documentation - Document domain purposes
  8. ES5 Only - No modern JavaScript syntax

Score

Total Score

75/100

Based on repository quality metrics

โœ“SKILL.md

SKILL.mdใƒ•ใ‚กใ‚คใƒซใŒๅซใพใ‚Œใฆใ„ใ‚‹

+20
โœ“LICENSE

ใƒฉใ‚คใ‚ปใƒณใ‚นใŒ่จญๅฎšใ•ใ‚Œใฆใ„ใ‚‹

+10
โœ“่ชฌๆ˜Žๆ–‡

100ๆ–‡ๅญ—ไปฅไธŠใฎ่ชฌๆ˜ŽใŒใ‚ใ‚‹

+10
โ—‹ไบบๆฐ—

GitHub Stars 100ไปฅไธŠ

0/15
โœ“ๆœ€่ฟ‘ใฎๆดปๅ‹•

1ใƒถๆœˆไปฅๅ†…ใซๆ›ดๆ–ฐ

+10
โ—‹ใƒ•ใ‚ฉใƒผใ‚ฏ

10ๅ›žไปฅไธŠใƒ•ใ‚ฉใƒผใ‚ฏใ•ใ‚Œใฆใ„ใ‚‹

0/5
โœ“Issue็ฎก็†

ใ‚ชใƒผใƒ—ใƒณIssueใŒ50ๆœชๆบ€

+5
โœ“่จ€่ชž

ใƒ—ใƒญใ‚ฐใƒฉใƒŸใƒณใ‚ฐ่จ€่ชžใŒ่จญๅฎšใ•ใ‚Œใฆใ„ใ‚‹

+5
โœ“ใ‚ฟใ‚ฐ

1ใคไปฅไธŠใฎใ‚ฟใ‚ฐใŒ่จญๅฎšใ•ใ‚Œใฆใ„ใ‚‹

+5

Reviews

๐Ÿ’ฌ

Reviews coming soon