AshT
ServiceNow Employee

---Please consult security and risk organizations before proceeding with the design below. In an ideal scenario, we don't want retirees to access employee information. The following approach was socialized ONLY if the organization accepted the risk----

Executive Summary

This design replaces a legacy alumni directory application with a ServiceNow-native solution. A single custom table (u_people_directory) holds both active employee records and opted-in retiree/alumni records. A nightly scheduled sync job populates employee data from sys_user. Retirees manage their own directory preferences through the existing ASC profile page. A dedicated People Directory Search widget on the ASC portal provides the lookup experience.

Key design decisions:

  • One unified custom table holds both populations, with a record type field (employee vs. alumni/retiree) and a record source field (sync vs. self-service) governing ownership.
  • Alumni access the ASC portal only. No EC Pro access, no direct queries to sys_user, sn_employee_profile, or sn_hr_core_profile.
  • A scheduled sync job (Import Set + Transform Map) copies approved employee fields from sys_user nightly. Alumni never touch the sync infrastructure.
  • Retirees manage their directory opt-in and contact preferences from the existing ASC profile page via an embedded Directory Preferences widget. No new pages or navigation entries are needed.
  • A dedicated People Directory Search widget on a new ASC portal page provides purpose-built search for finding employees and opted-in retirees. This is intentionally separate from the ASC portal’s general search bar.
  • AI Search is not used. The directory search is a distinct user intent ("find a person") that requires consent-aware field display and clean data segregation from platform search infrastructure.

Solution Overview

What Gets Built

Component

Type

Purpose

u_people_directory

Custom table

Unified directory holding employee records (system-synced) and alumni/retiree records (self-managed with opt-in consent).

Employee Sync Job

Import Set + Transform Map

Nightly sync from sys_user to u_people_directory for active employees matching defined criteria.

People Directory Search

Custom SP widget + ASC page

Dedicated search interface for looking up employees and opted-in retirees. Embedded on a new "Directory" page in the ASC portal.

Directory Preferences

Lightweight custom SP widget

Consent toggles and contact fields for retirees. Embedded as a section on the existing ASC profile page.

What Stays Unchanged

  • Employee Center Pro (/esc): Active employees continue using EC Pro’s native people finder, org chart, and profiles. EC Pro and the alumni directory share no widgets, tables, or security model.
  • ASC portal core: Existing ASC content (knowledge articles, HR cases, targeted communications, general search) is unaffected. The directory is additive.
  • ASC profile page: The existing profile page (accessible via the user avatar/name in the top right of the ASC portal) continues to surface sys_user and HR profile fields. The Directory Preferences widget is added as a new section alongside existing profile content.

Why Not AI Search

AI Search was evaluated and intentionally excluded from this design for three reasons.

Different user intent: The ASC portal’s search bar serves a general purpose: finding knowledge articles, HR services, and catalog items. "Find a person" is a distinct intent with a distinct UX. Mixing people results into the general search creates confusion. An alumni searching "John Smith" expects a person card, not a knowledge article about an employee named John Smith. A dedicated widget with purpose-built search, filtering by record type (employee vs. retiree), and consent-aware profile cards delivers a cleaner experience.

Consent-aware display is not supported: AI Search renders result cards with a static field layout configured at the Search Source level. It cannot conditionally show or hide fields per record based on consent booleans. An alumni who opted to share their email and one who did not would receive identical result cards. The consent model requires per-record field evaluation at render time, which only a custom widget can provide.

Data segregation principle: The core architectural decision in this design is to keep alumni interactions fully contained within a single custom table, with no dependencies on platform-level system tables. AI Search introduces dependencies on sys_ais_index, sys_ais_search_source, indexed source configurations, and the search indexing pipeline. This reintroduces the coupling we specifically designed away from. The People Directory Search widget queries u_people_directory directly via GlideRecord with no intermediary.

Data Model: u_people_directory

Single custom table holding both populations. One record per person. The u_record_type and u_record_source fields govern behavior and ownership.

Core Fields (All Records)

Field Name

Type

Purpose

Source

u_user

Reference (sys_user)

Link to user record (if exists)

sys_user / alumni record

u_record_type

Choice

Values: employee, alumni, retiree

Sync job or self-service

u_record_source

Choice

Values: sync, self-service. Determines ownership.

Set on creation, never changed

u_display_name

String (150)

Name as displayed in directory

sys_user.name / self-entered

u_department

String (100)

Current or last department

sys_user.department / self-entered

u_title

String (100)

Current or former job title

sys_user.title / self-entered

u_work_phone

Phone Number

Work phone (employees only)

sys_user.phone

u_work_location

String (200)

Office location (employees only)

sys_user.location

u_is_active

True/False

Whether record appears in search results

Sync job / consent toggle

Alumni/Retiree-Specific Fields

Field Name

Type

Purpose

Source

u_personal_email

Email

Personal email address

Self-entered via profile

u_personal_phone

Phone Number

Personal phone number

Self-entered via profile

u_mailing_address

String (500)

Mailing address

Self-entered via profile

u_linkedin_url

URL

LinkedIn profile link

Self-entered via profile

u_departure_year

Integer

Year departed the organization

HR profile or self-entered

u_consent_listed

True/False

Master opt-in: appear in directory

Default: false

u_consent_email

True/False

Opt-in: show personal email

Default: false

u_consent_phone

True/False

Opt-in: show personal phone

Default: false

u_consent_address

True/False

Opt-in: show mailing address

Default: false

u_last_updated

Date/Time

Last profile edit timestamp

Auto-set on save

Ownership Rules

u_record_source = "sync" (employee records): Created and maintained exclusively by the scheduled sync job under a service account. No user can edit these records through the portal. The sync job upserts on each run: creates records for new employees, updates changed fields, and sets u_is_active = false for departed employees. Records are deactivated, not deleted, for audit purposes.

u_record_source = "self-service" (alumni/retiree records): Created and maintained by the individual retiree through the Directory Preferences section on the ASC profile page. The sync job skips any record where u_record_source = "self-service." Even if a retiree has an inactive sys_user record, the sync job will not create a duplicate or overwrite their self-managed profile.

Consent Model: u_consent_listed must be true for the record to appear in any search results. Individual field consents (email, phone, address) control which contact details are visible. LinkedIn URL and display name are always visible when the master consent is granted, since the retiree explicitly populated those fields. For employee records, consent fields are not evaluated — u_is_active is controlled directly by the sync job based on employment status.

Scheduled Sync Job

Source Query

The sync job queries sys_user with configurable filter criteria stored as system properties. Recommended defaults:

  • active = true
  • employee_type IN [configurable list, e.g., "regular", "home-based"]
  • Optional: exclude VIP users via custom flag or group membership
  • Optional: u_exclude_from_directory = true (new boolean on sys_user for individual employee opt-out)

Field Mapping

Source (sys_user)

Target (u_people_directory)

Notes

sys_id

u_user

Reference field; coalesce key for upsert

name

u_display_name

Full name

department.name

u_department

Dot-walked to department name string

title

u_title

Current job title

phone

u_work_phone

Business phone only; never home or mobile

location.name

u_work_location

Dot-walked to location name string

The transform map sets u_record_type = "employee," u_record_source = "sync," and u_is_active = true for all upserted records. An onBefore script skips any target record where u_record_source = "self-service." After the sync completes, a post-sync script deactivates any sync-managed record whose corresponding sys_user no longer matches the source query criteria.

Frequency and Performance

Nightly by default. For organizations with high turnover, configurable to every 4 hours. The job processes only changed records using delta detection (comparing sys_updated_on against the last sync timestamp). A directory of 10,000–50,000 employees typically completes a full sync in under 5 minutes.

Widget Architecture

Widget 1: People Directory Search

Location: New "Directory" page added to the ASC portal, accessible via a new navigation link in the ASC menu (e.g., under "Company" or as a top-level item). This is the only new page in the design.

Server Script: Queries u_people_directory where u_is_active = true. Uses addField() to explicitly select only the approved display columns — defense in depth beyond ACLs. Applies typeahead search against u_display_name, u_department, u_title, and u_departure_year. For alumni/retiree records, the server script evaluates per-field consent booleans before including contact details in the response. For employee records, all mapped fields are returned (they contain only pre-approved data). Pagination at 20 results per page.

Client UX: Search bar with typeahead at the top. Filter options: "All," "Employees," "Alumni/Retirees" (maps to u_record_type). Results rendered as profile cards. Employee cards show: name, department, title, work phone, work location. Retiree/alumni cards show: name, last department, departure year, plus any consented contact fields (personal email, phone, address, LinkedIn). Cards that have no consented contact fields still appear in results (with name, department, departure year) so directory presence itself has value even without contact sharing.

Security: The server script is the primary enforcement layer. It queries only u_people_directory (never sys_user), selects only specific fields via addField(), and evaluates consent booleans per record. Table ACLs provide the second layer. The widget never returns data from any table other than u_people_directory.

Widget 2: Directory Preferences

Location: Embedded as a new section on the existing ASC profile page, below the standard profile fields. No new page, no new navigation. Retirees access it by clicking their avatar/name in the top-right corner of the ASC portal, which is the same action they would take to view or update their profile today.

Server Script: On page load, queries u_people_directory where u_user = current session user AND u_record_source = "self-service." If no record exists, returns an empty state (triggers the "Join the Directory" prompt in the client). If a record exists, returns the editable fields and current consent toggle values. On save, validates field formats (email, phone, URL), writes to the record, and auto-sets u_last_updated. On create, sets u_record_source = "self-service" and u_record_type based on the user’s selection (alumni or retiree).

Client UX: When no directory record exists, shows a card with a brief explanation: "Join the people directory to let other alumni and employees find you. You control exactly what information is shared." A single "Join" button creates the record with all consents set to false. When a record exists, shows the editable contact fields (personal email, phone, mailing address, LinkedIn) and a consent toggle next to each. A master "Listed in Directory" toggle controls u_consent_listed. A live preview panel shows exactly how the retiree’s card will appear in search results, updating dynamically as toggles change. A "Remove from Directory" link hard-deletes the record with a confirmation dialog. A "Pause Listing" toggle sets u_consent_listed = false without deleting data.

Security: The server script enforces u_user = current session user on all operations. A retiree cannot view or modify another person’s directory preferences. The write ACL on u_people_directory provides the second enforcement layer.

Design Decision: Embedding Directory Preferences on the existing profile page rather than creating a standalone page eliminates a new navigation entry, reduces page count, and leverages a familiar UX pattern. Retirees already know where their profile is. Adding a "People Directory" section to that page is the lowest-friction way to drive adoption.

ACL Strategy

Alumni interact with exactly one table: u_people_directory. No ACLs are created on sys_user, sn_employee_profile, or sn_hr_core_profile for the alumni role. The sync job runs under a service account with its own access.

ACL Type

Operation

Role

Condition / Script

Table-level

Read

sn_hr_asc.alumni

Condition: u_is_active = true. Alumni see only active directory records.

Table-level

Create

sn_hr_asc.alumni

Script: u_user must equal gs.getUserID() AND u_record_source must be "self-service." Prevents alumni from creating employee-type records.

Table-level

Write

sn_hr_asc.alumni

Condition: u_user = current user AND u_record_source = "self-service." Alumni edit only their own self-managed record.

Table-level

Delete

sn_hr_asc.alumni

Condition: u_user = current user AND u_record_source = "self-service." Alumni delete only their own record.

Field-level

Write (deny)

sn_hr_asc.alumni

Fields: u_record_source, u_record_type, u_user, u_is_active. Prevents alumni from modifying governance fields directly. u_is_active is derived from u_consent_listed via business rule.

Security Principle: The attack surface for alumni is one table containing only pre-approved directory data. Even a complete ACL failure exposes nothing beyond names, departments, titles, work phones, and voluntarily shared contact details. No manager chains, no personal emails from HR profiles, no compensation data, no emergency contacts.

Business Rules

Rule Name

When

Condition

Action

Set Active from Consent

Before Insert/Update

u_record_source = "self-service"

Set u_is_active = u_consent_listed. Ensures retiree visibility is driven by master consent toggle.

Protect Sync Records

Before Update

u_record_source = "sync" AND current user is not the sync service account

Abort with message. Prevents any user from editing sync-managed records through the portal.

Detect Rehire Conflict

Before Insert

u_record_source = "sync" AND existing record with same u_user AND u_record_source = "self-service"

Skip insert. Create HR task for admin review.

Set Last Updated

Before Update

u_record_source = "self-service"

Set u_last_updated = gs.nowDateTime().

Privacy and Data Lifecycle

Consent Model: All retiree directory participation is opt-in with all consent flags defaulting to false. No retiree data is visible until the individual enables it through the Directory Preferences section on their ASC profile page.

Right to Erasure: "Remove from Directory" hard-deletes the u_people_directory record. Confirmation dialog includes: "This will permanently remove your contact information from the people directory." For employee records, the sync job handles lifecycle by setting u_is_active = false when the employee departs.

Data Minimization: The sync job copies only six fields from sys_user. The custom table physically cannot contain data it was never designed to hold. No personal email, home address, compensation, or manager chain data exists in u_people_directory for employee records.

Stale Record Cleanup: Monthly scheduled job flags retiree records where u_last_updated is older than 24 months and u_consent_listed = true. Flagged retirees receive an email to confirm continued participation. No response within 30 days sets u_consent_listed = false (hidden, not deleted). The retiree can re-enable by logging in and toggling the setting back on.

Employee Opt-Out (Optional): If the organization wants individual employees to exclude themselves, add u_exclude_from_directory (boolean) to sys_user. The sync job filters on this field. This is a policy decision, not a technical requirement.

Sample Implementation Roadmap

Phase 1: Table, ACLs, Business Rules (Weeks 1–2)

  • Create u_people_directory table with all fields.
  • Build table-level and field-level ACLs for the alumni role.
  • Build all four business rules (consent activation, sync protection, rehire detection, last-updated stamp).
  • Create sync service account with read access to sys_user.
  • Validate alumni role cannot access EC Pro, backend, or any system table.

Phase 2: Sync Job (Weeks 2–3)

  • Build Import Set and Transform Map for sys_user → u_people_directory.
  • Configure source query filters as system properties.
  • Implement upsert with coalesce on u_user and self-service record protection.
  • Implement post-sync deactivation for departed employees.
  • Test with production-representative data volume.

Phase 3: Widgets (Weeks 3–5)

  • Build People Directory Search widget with typeahead, record type filters, consent-aware field display, and pagination.
  • Create "Directory" page on ASC portal and add navigation link.
  • Build Directory Preferences widget with consent toggles, live preview, join/pause/remove actions.
  • Embed Directory Preferences widget on the existing ASC profile page.
  • UX review for consistency with ASC portal styling.

Phase 4: Testing & Hardening (Weeks 5–7)

  • Security: ACL enforcement, privilege escalation testing, field-level restriction validation, sync job access verification.
  • Privacy: consent toggle behavior, hard-delete verification, stale record cleanup validation.
  • Edge cases: rehire conflict, legacy data import, employee who was previously a retiree.
  • UAT with pilot group of alumni/retirees.
  • Performance: search at scale (10K+ records), sync job timing.

Phase 5: Migration & Launch (Weeks 7–9)

  • Import legacy alumni directory data as "self-service" records with u_consent_listed = false.
  • Run initial full employee sync.
  • Communicate to alumni/retiree community with instructions and benefits.
  • Parallel run with legacy app (2–4 weeks).
  • Decommission legacy app.

Licensing Considerations

This design consumes one custom table entitlement (u_people_directory). Verify available allocation against App Engine entitlements or bundled custom tables with HRSD Enterprise. Alumni portal access uses the existing ASC licensing model (part of HRSD Enterprise Onboarding and Transitions). No EC Pro licensing implications exist because alumni never access any EC Pro component, table, or widget. The sync job runs as a system process and does not consume user entitlements.

Risks and Mitigations

Risk

Impact

Mitigation

Sync job freshness: employee changes not reflected until next sync

Low – directory data is not time-critical

Nightly sync is sufficient. Increase to every 4 hours if needed. Real-time sync is not warranted for a people directory.

Low retiree opt-in adoption

Medium – directory has limited value with few participants

Pre-populate records from legacy app with non-PII data (name, departure year, department). Set all consent flags to false. Communicate value during migration.

Rehire creates data conflict

Low – small number of users affected

Business rule detects conflict and creates HR task. No automatic merge.

Custom table entitlement not available

Medium – blocks implementation

Confirm entitlement in Phase 1. Evaluate App Engine subscription if needed.

Appendix: Design Evolution

This design went through three iterations, each simplifying the architecture based on deeper analysis of requirements and platform capabilities.

Dimension

V1: Direct EC Pro Query

V2: Unified Table

V3: Final (This Document)

Custom tables

1 (alumni directory only)

1 (unified)

1 (unified)

Custom widgets

3

2

1 full + 1 lightweight

New portal pages

2 (directory + profile mgmt)

1 (directory)

1 (directory)

Tables alumni query

2 (sn_employee_profile + custom)

1 (u_people_directory)

1 (u_people_directory)

Licensing risk

High (EC Pro data layer)

None

None

Search approach

Custom widget

Custom widget

Custom widget (AI Search evaluated and excluded)

The final design achieves the original requirements (alumni-to-alumni directory with opt-in contact sharing, alumni-to-employee lookup) with the smallest possible custom footprint: one table, one sync job, one search page, and one profile section. Every platform component it touches (ASC portal, Service Portal widgets, Import Sets, Transform Maps, ACLs, business rules) is standard ServiceNow with no dependencies on premium features beyond the existing HRSD Enterprise license.