Tabellen, Scopes, Pipeline
Was du wissen musst, bevor irgendetwas anderes Sinn ergibt: das relationale Modell, Application Scopes, Multi-Instance-Setup und Quick-URLs für die tägliche Arbeit.
Alles ist eine Tabelle
ServiceNow ist eine relationale Datenbank mit Web-Frontend. Jedes Modul, jedes Formular, jede Konfiguration lebt in einer Tabelle. Wenn du die Tabelle kennst: tabelle.list oder tabelle.do?sys_id=….
Tabellen erben voneinander. incident, change_request, problem erben alle von task — daher teilen sie Felder wie number, state, assigned_to.
Multi-Instance Pipeline
Konfiguration wandert über separate Instanzen. Daten bleiben pro Instanz lokal.
Transport: Update Sets (XML) für Konfigurationen, Plugin-Aktivierung manuell pro Instanz, Properties oft instanzspezifisch.
Kern-Tabellen
20 Stück| Tabelle | Inhalt |
|---|---|
| sys_user | Alle Benutzer-Accounts |
| sys_user_group | Gruppen (Assignment, Approval, Notification) |
| sys_user_role | Rollen-Definitionen (admin, itil, …) |
| sys_user_grmember | N:M User ↔ Gruppe |
| sys_user_has_role | N:M User ↔ Rolle (inherited oder direct) |
| sys_security_acl | Access Control Rules |
| sys_properties | System-weite Key/Value-Konfiguration |
| sys_db_object | Tabellen-Definitionen (Meta) |
| sys_dictionary | Feld-Definitionen (Meta) |
| sys_scope | Application Scopes |
| sys_update_set | Update Set Container |
| sys_update_xml | Einzelne Änderungen im Update Set |
| sys_email | Email Queue (Inbound/Outbound) |
| sysevent | Event Queue (triggert Notifications & Scripts) |
| sysevent_email_action | Notification-Definitionen |
| sys_trigger | Alle Scheduled Jobs |
| syslog | Allgemeines System-Log |
| sys_log_transaction | HTTP-Transaktionen (Performance) |
| task | Basis für Incident/Change/Problem/Request |
| cmdb_ci | Configuration Items (CMDB Root) |
Quick-URLs
direkt in den Navigator tippen- /stats.doBuild, Memory, Nodes
- /cache.doCache-Stats & Flush
- /xmlstats.dostats.do als XML
- /sys_properties.listAlle Properties
- /syslog.listSystem-Log
- /sys_user.listUser-Übersicht
- /sys_email.listMail-Queue
- /wf_context.listLaufende Workflows
- /sys_db_object.listAlle Tabellen
- /sys.scripts.doBackground-Script Console
/stats.do — notiere Build (Zürich), Patch-Level, Node-Count, Memory.sys_properties.list auf name STARTSWITH glide. — verschaffe dir einen Überblick über Properties.incident von task erbt.SANDBOX_marcus und mach ihn zu deinem Default — alle Spielereien landen dort.x_vendor_app) sind upgrade-safe und isoliert. Wenn etwas „nicht sichtbar" oder „insufficient privileges" wirft obwohl du Admin bist — meist Scope-Problem.
User & Gruppen
Das Berechtigungs-Dreieck: User trägt Rollen direkt oder via Gruppe. Gruppen haben Rollen, die alle Mitglieder erben. Best Practice: Rollen nie direkt am User.
Das Dreieck
User ────── Group
\ /
\ /
Role
Best Practice: Rollen nie direkt an User vergeben, immer an Gruppen. User → Gruppe → Rolle. Macht Audits, Onboarding und Offboarding planbar.
sys_user_grmember und sys_user_has_role sind die Verknüpfungstabellen.
Wichtige Rollen
| admin | Vollzugriff (außer security_admin-Bereich) |
| security_admin | ACLs, High-Security (Elevation nötig) |
| itil | ITSM-User (Incident/Change/Problem) |
| catalog_admin | Service Catalog |
| user_admin | User & Gruppen ohne admin |
| impersonator | Andere User impersonieren |
| snc_internal | Standard für interne User |
Tabellen-Map
| Tabelle | Schlüssel-Felder | Verwendung |
|---|---|---|
| sys_user | user_name, email, active, locked_out, source | Stammdaten. source zeigt Origin (LDAP/SSO). |
| sys_user_group | name, type, manager, parent, email | Container. type = Assignment / Approval / etc. |
| sys_user_role | name, contains_roles, elevated_privilege | Rollen können andere enthalten. |
| sys_user_grmember | user, group | N:M Mitgliedschaft. |
| sys_user_has_role | user, role, granted_by, inherited | inherited=true ⇒ via Gruppe. |
| sys_user_delegate | user, delegate, starts, ends | Vertretungen (Approvals). |
user_name muss eindeutig sein.Bei deinem Setup (Entra Gallery App / SOAP) entstehen User automatisch.active = false. Alle History bleibt referenzierbar.Häufige Quick-Skripte
Background Scripts// Welche Rollen hat ein User — direkt vs. geerbt? var gr = new GlideRecord('sys_user_has_role'); gr.addQuery('user.user_name', 'marcus.muster'); gr.query(); while (gr.next()) { gs.info(gr.role.name + ' (' + (gr.inherited ? 'via Gruppe' : 'direct') + ')'); }
// Alle Mitglieder einer Gruppe var gr = new GlideRecord('sys_user_grmember'); gr.addQuery('group.name', 'IT-Support'); gr.query(); while (gr.next()) gs.info(gr.user.user_name + ' — ' + gr.user.email);
Identity & Multi-Provider SSO
Multiple IdPs gleichzeitig (Entra, ADFS, Okta, lokal) über das Multi-Provider SSO Plugin. SAML 2.0 ist der Standard, OIDC für Custom-Apps und Service-zu-Service.
Multi-Provider SSO
Erlaubt parallel mehrere IdPs. Standard für Enterprise — ein Provider pro Mandant / User-Gruppe / Domain.
- Plugin-Name:
com.snc.integration.sso.multi.installer - Aktivieren: System Definition → Plugins → suchen → Activate
- Login-URL:
/login.dozeigt Provider-Auswahl ab > 1 aktiv - Direct-IdP-Login:
/login_with_sso.do?glide_sso_id=<sys_id>
Wichtige glide.authenticate.*
| glide.authenticate.sso.redirect.idp | Default-IdP wenn nur einer |
| glide.authenticate.multisso.enabled | Multi-Provider an/aus |
| glide.authenticate.external | Externe Auth global aktivieren |
| glide.authenticate.sso.disable.basic | Basic-Auth-Bypass blocken |
| glide.entry.first.page.script | Custom Landing nach Login |
| glide.ldap.debug | Auth-Debug (nur kurz!) |
Tabellen-Landschaft
| Tabelle | Inhalt |
|---|---|
| sso_properties | Globale SSO-Einstellungen |
| multi_provider_sso | Einzel-IdP-Konfigurationen (1 Eintrag pro Provider) |
| sys_certificate | IdP-Zertifikate (Signing & Encryption) |
| sys_user.source | Feld am User: zeigt Auth-Origin (z.B. "ldap:<config>") |
| oauth_entity | OAuth-Provider/Clients (für REST/SCIM/OIDC) |
| oauth_credential | Live OAuth-Tokens |
| ldap_server_config | LDAP-Verbindungen (Sync, nicht Auth) |
| sys_ldap_ou_config | OUs zum Sync |
email oder user_name — passend zur Claim-Konfiguration im IdP. Mismatch = häufigster Fehler./navpage.do)./login_with_sso.do?glide_sso_id=<sys_id>.JIT-Provisioning & User-Sync
Bei Login User anlegen
Im IdP-Eintrag: Auto Provisioning User aktivieren. Beim ersten SAML-Login wird ein sys_user mit den Claim-Werten erzeugt. Optional: Auto Update User für laufende Aktualisierung.
- Claim-Mapping: SAML Single Sign-On Script für Custom-Logik
- Gruppen-Sync via JIT meist nicht ausreichend → besser dedizierter Provisioning-Channel
SCIM vs. SOAP (Gallery App)
Auth ≠ Provisioning. Gruppen-Mitgliedschaften, Lifecycle, Deprovisioning brauchen einen eigenen Kanal:
- SCIM — RFC-Standard. Achtung Entra: sendet PATCH-remove non-RFC-konform → SN lehnt ab.
- SOAP via Gallery App — empfohlen für Entra. Mapping auf
sys_id(nichtname) → robust gegen Display-Name-Änderungen. - LDAP-Import — klassisch, läuft als Scheduled Job, transformiert via Transform Map.
admin-Konto mit starkem PW vorhalten. Property glide.authenticate.sso.disable.basic erst aktivieren, wenn SSO bewiesen funktioniert. Notfall-Bypass: /side_door.do (sofern aktiviert).
oauth_credential.list.
SOAP-Provisioning im Detail
Entra Gallery App → ServiceNowWie der Sync funktioniert
Entra ID (Source of Truth) ↓ Enterprise App "ServiceNow" (Gallery App, Provisioning) ↓ SOAP/HTTPS, Basic Auth ↓ alle ~40 Min (Sync-Cycle) ↓ ServiceNow SOAP Endpoint /<table>.do?SOAP ↓ sys_user / sys_user_group / sys_user_grmember
Entra liest Source-Attribute aus dem Tenant, mappt sie auf SN-Felder per Attribute-Mapping, und ruft ServiceNows SOAP-API auf — meist insert, update, oder deleteRecord.
Was in SN bereitstehen muss
- Service-Account in
sys_user, z.B.svc_entra_provisioning— interner User, kein UI-Login nötig - Rollen am Service-Account:
soap,web_service_admin,user_admin,itil(für Gruppen-Schreiben). Über dedizierte Gruppe vergeben — nicht direkt. - Property:
glide.basicauth.required.soap=trueerzwingt Basic Auth (Standard) - Property:
glide.soap.require_authorization=trueblockt anonyme SOAP-Calls - Plugin: „Direct Web Services" muss aktiv sein (in Zürich Standard)
SOAP-Endpoints & Tabellen
| Endpoint | Operation | Verwendet für |
|---|---|---|
| /sys_user.do?SOAP | insert / update / get | User-Lifecycle (anlegen, ändern, deaktivieren) |
| /sys_user_group.do?SOAP | get | Gruppen-Lookup (nur lesend — Entra darf nicht anlegen!) |
| /sys_user_grmember.do?SOAP | insert / deleteRecord | Mitgliedschaft setzen/entfernen |
| /<table>.do?WSDL | GET | WSDL-Schema einer Tabelle abrufen (Test/Inspektion) |
svc_entra_provisioning, internal=true, web_service_access_only=true. Starkes Passwort generieren und sicher hinterlegen.web_service_access_only verhindert UI-Login, reduziert Angriffsfläche.integration_entra_provisioning anlegen, Rollen soap, web_service_admin, user_admin, itil hängen. Service-Account in die Gruppe.https://<instance>.service-now.com/sys_user.do?WSDL mit Basic Auth des Service-Accounts. Sollte XML-WSDL zurückgeben.Wenn 401: Rolle fehlt. Wenn 200 ohne XML: User ist auf locked_out oder web_service_access_only nicht gesetzt.userPrincipalName → user_name, mail → email, etc. Match-Attribute auf user_name.sys_id umstellen (nicht name!). Switch-Expression für Mapping (siehe unten).sys_id-Mapping macht den Sync robust gegen Display-Name-Umbenennungen in Entra.sys_user.list Filter source=ldap:Azure AD — alle synchronisierten User. sys_user_grmember.list Filter auf neue Gruppe — Mitglieder da?Group-Mapping: Switch-Expression auf sys_id
Entra liefert pro User die Liste seiner Entra-Gruppen-Display-Names. SN braucht aber die sys_id der entsprechenden SN-Gruppe als Target-Wert in sys_user_grmember.group. Die Switch-Expression im Entra-Mapping macht die Übersetzung:
Switch( [groupDisplayName], "<default-fallback-sys_id>", "AAD-IT-Support", "a1b2c3d4e5f6789012345678", "AAD-Change-Manager", "f9e8d7c6b5a4321098765432", "AAD-Service-Desk", "1234567890abcdef12345678" )
- Linke Spalte: Display-Name der Entra-Gruppe (Source)
- Rechte Spalte: sys_id der korrespondierenden SN-Gruppe (Target)
- Default-Fallback: sys_id einer „Catch-All"-Gruppe oder leer-String wenn unbekannte Gruppen ignoriert werden sollen
- Pflege: bei jeder neuen Gruppe Switch-Expression aktualisieren — manuell, das ist der Preis für Stabilität
Häufige Probleme & Diagnose
401 / 403 vom SN-Endpoint
- Service-Account
locked_out=true? Kommt nach fehlgeschlagenen Logins automatisch. - Passwort in Entra abgelaufen / rotiert? In SN
password_needs_reset=true? - Rolle
soapoderweb_service_adminfehlt? - Property
glide.basicauth.required.soapauftrue+ Service-Account hat kein Passwort?
„Permanent failure" für einzelne User
- Pflichtfeld in
sys_userfehlt im Mapping (user_name!) - Match-Attribute mehrdeutig: zwei Entra-User mit gleichem Match-Wert
- Reference-Field nicht auflösbar (z.B.
managerzeigt auf nicht-existenten User) - Group sys_id in Switch-Expression existiert nicht (mehr) in SN
// Quick-Check: alle von Entra synchronisierten User var gr = new GlideRecord('sys_user'); gr.addQuery('source', 'STARTSWITH', 'ldap:'); gr.addQuery('sys_updated_on', '>', gs.daysAgoStart(1)); gr.query(); gs.info('In den letzten 24h durch Entra geändert: ' + gr.getRowCount());
Email-System
Inbound & Outbound, Templates, Notifications, Events. Eine ungewollte Email-Schleife in Prod ist eine der häufigsten Admin-Krisen — kenne den Off-Switch.
Event → Notification → SMTP
Business Rule ↓ sysevent (queue) ↓ sysevent_email_action (matcht) ↓ sys_email (ready) ↓ SMTP → Empfänger
POP3/IMAP → Action
Mailbox (POP3/IMAP) ↓ sys_email (received) ↓ sys_email_inbound_action ↓ Record Update / Reply
Watermark in der Mail (Ref:MSG…) ordnet Reply zu Originalticket.
Tabellen & Module
| Tabelle / Modul | Inhalt |
|---|---|
| sys_email | Queue. State: ready / sent / received / error / ignored |
| sys_email_account | Outbound-SMTP & Inbound-Mailboxes |
| sysevent_email_action | Notifications (welches Event → welche Mail) |
| sys_email_template | Templates (HTML/Text + Mail Scripts) |
| sys_email_inbound_action | Was tun bei eingehender Mail |
| sysevent | Event-Queue (Trigger für Notifications) |
| System Mailboxes | UI-Modul für Queue, Diagnostics, Templates |
Killswitch & Test-Setup
glide.email.*
glide.email.smtp.active— Outbound-Masterglide.email.read.active— Inbound-Masterglide.email.test.user— alle Mails an diese Adresseglide.email.notification.send_to_active_onlyglide.email.test.exclusion.list
Mailflut stoppen
sys_properties→glide.email.smtp.active=false- Verdächtige Notification deaktivieren (System Notification → Email)
- Queue prüfen:
sys_email.list?state=ready→ ggf. State auf „send-ignored" setzen
Mail kommt nicht raus
- Steht sie in
sys_email? State? error→ Error-Feld lesenignored→ Condition der Notificationready→ SMTP-Account aktiv?- Email Diagnostics → Send Test Email
sys_email.glide.email.test.user = deine Mail-Adresse. Jetzt gehen alle Mails nur an dich.incident.commented, Recipients = „Users in field" → assigned_to.${number}, ${short_description}. Variablen sind GlideRecord-Felder vom „current".sys_email.list sollte die Mail erscheinen.Wenn nicht: Event-Log unter System Policy → Events → Event Log.glide.email.test.user setzen. Sonst feuert die geklonte Queue echte Mails an echte User.
Modern Auth mit Entra / Microsoft 365
OAuth 2.0 statt Basic AuthWarum Modern Auth?
Microsoft hat Basic Auth für Exchange Online seit Oktober 2022 abgeschaltet — SMTP AUTH, IMAP, POP3 mit Username/Passwort funktionieren nicht mehr. Stattdessen: OAuth 2.0 mit App-registriertem Token-Flow.
ServiceNow unterstützt Modern Auth in Email Accounts ab Tokyo, in Zürich vollständig integriert. Setup über Application Registry + OAuth-Profile pro Mailbox.
Zwei Wege
- OAuth 2.0 + SMTP/IMAP — drop-in Ersatz für Basic Auth. Funktioniert mit existierender System-Mailbox-Konfiguration, nur Auth-Methode ändern. Einfachster Weg.
- Microsoft Graph API — moderner, mehr Features (Send-As, Shared Mailboxes, Erweiterte Suche), aber komplexer. Über Spoke / Custom REST. Empfohlen für Service-zu-Service ohne User-Mailbox-Bindung.
Pragmatisch: starte mit OAuth+SMTP/IMAP, migriere später wenn nötig auf Graph.
Permissions: Delegated vs. Application
Wichtig zu verstehen, weil Microsoft hier eine Stolperfalle eingebaut hat:
| Permission-Typ | Wer agiert? | SMTP/IMAP | Graph |
|---|---|---|---|
| Delegated | App im Namen eines konkreten Users (Mailbox-Owner) | ✓ unterstützt | ✓ unterstützt |
| Application | App agiert eigenständig, ohne User-Kontext | ✗ NICHT unterstützt | ✓ unterstützt |
Endpoints & Scopes
| Zweck | URL / Scope |
|---|---|
| Authorize URL | https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize |
| Token URL | https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token |
| Scope SMTP Send | https://outlook.office.com/SMTP.Send offline_access |
| Scope IMAP Read | https://outlook.office.com/IMAP.AccessAsUser.All offline_access |
| Scope Graph (App) | https://graph.microsoft.com/.default |
| SMTP Server | smtp.office365.com:587 (STARTTLS) |
| IMAP Server | outlook.office365.com:993 (SSL) |
ServiceNow Mail Integration. Account Type = „Single tenant". Redirect URI: https://<instance>.service-now.com/oauth_redirect.do (Web).SMTP.Send (und ggf. IMAP.AccessAsUser.All) + offline_access für Refresh Token.Admin Consent klicken — sonst funktionieren die Permissions nicht.servicenow@firma.de). Diese User-Identität liefert beim ersten OAuth-Consent das Token.Falls Shared Mailbox: User mit „Send As" oder „Send on Behalf" Permission.https://outlook.office.com/SMTP.Send offline_access.smtp.office365.com, Port 587, Authentication = OAuth 2.0, OAuth Profile auswählen.oauth_credential abgelegt.Refresh Token wird automatisch erneuert — solange App-Registration und User-Account existieren.sys_email mit state=sent.Bei Fehler: Error-Feld prüfen. Häufig: „535 5.7.139" = Modern Auth disabled für die Mailbox (CA-Policy oder Tenant-Setting).ServiceNow Graph Mail. Account Type = „Single tenant". Keine Redirect URI nötig (Application Flow ohne User-Interaction).Mail.Send (für Outbound). Bei Bedarf Mail.ReadWrite oder Mail.Read. Admin Consent klicken — entscheidend, sonst greifen die Permissions nicht.New-ApplicationAccessPolicy -AppId <client-id> -PolicyScopeGroupId <mailbox-or-group> -AccessRight RestrictAccesshttps://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token, Default Grant type = „Client Credentials".https://graph.microsoft.com/.default (das .default ist Pflicht bei Application-Permissions, nicht weglassen).https://graph.microsoft.com/v1.0/users/{mailbox}/sendMail, Method POST, Auth = OAuth 2.0 → dein Profile. Body wie unten:// HTTP POST /v1.0/users/<mailbox-id-or-upn>/sendMail { "message": { "subject": "ServiceNow: Incident INC0012345 eskaliert", "body": { "contentType": "HTML", "content": "<p>Hallo Team,</p><p>Ein P1 wurde eröffnet ...</p>" }, "toRecipients": [ { "emailAddress": { "address": "oncall@firma.de" } } ] }, "saveToSentItems": false }
sysevent_email_action kann ein „Email Script" stehen, das statt der SMTP-Pipeline das Graph-REST-Message aufruft. Oder: Flow Designer Subflow als Notification-Trigger.Bei Bedarf einen Wrapper als Script Include: GraphMailUtil.send(to, subject, html) für Wiederverwendung.oauth_credential.list.// Wiederverwendbarer Script Include — Graph Send Email var GraphMailUtil = Class.create(); GraphMailUtil.prototype = { send: function(toAddress, subject, htmlBody) { var r = new sn_ws.RESTMessageV2('GraphMail', 'sendMail'); var body = { message: { subject: subject, body: { contentType: 'HTML', content: htmlBody }, toRecipients: [{ emailAddress: { address: toAddress } }] }, saveToSentItems: false }; r.setRequestBody(JSON.stringify(body)); var response = r.execute(); return response.getStatusCode() === 202; }, type: 'GraphMailUtil' };
Häufige Probleme
SMTP 535 / 401 / invalid_grant
- SMTP Auth disabled: Per Mailbox:
Set-CASMailbox -SmtpClientAuthenticationDisabled $falsein Exchange Online PowerShell - Tenant blockt SMTP Auth: Org Settings → Modern authentication → SMTP AUTH aktivieren
- Conditional Access blockt App: CA-Policy ausnehmen für die App-ID
- Refresh Token expired: 90 Tage Inaktivität oder Passwort-Reset des Mailbox-Users → Re-Authorize klicken
- Falsche Permission: Application statt Delegated → für SMTP nicht nutzbar (siehe oben)
Erneuerung & Monitoring
oauth_credential.list— alle aktiven Tokens. Filterexpires < javascript:gs.daysAgoEnd(1)findet ablaufende- Refresh Token wird stillschweigend erneuert beim nächsten Send
- Bei längeren Maintenance-Pausen: Token kann „verlieren" → manuelles Re-Authorize
- Monitoring-Idee: Scheduled Job, der täglich Test-Mail sendet und bei Fehler eine Notification an Admin schickt
// Manuell prüfen ob OAuth-Token noch gültig ist var gr = new GlideRecord('oauth_credential'); gr.addQuery('oauth_provider.name', 'M365 Mail'); gr.query(); if (gr.next()) { gs.info('Token-Status: ' + gr.token_status); gs.info('Expires: ' + gr.expires); gs.info('Refresh-Token vorhanden: ' + (!gr.refresh_token.nil())); }
Mail.Send, Mail.ReadWrite. ServiceNow Spoke „Microsoft 365 Mail" kapselt das in Flow Designer Actions. Setup ähnlich wie oben, aber Permission-Typ = Application + Admin Consent + andere Scope (https://graph.microsoft.com/.default). Empfohlen für unattended Notifications.
Get-Recipient in EXO PowerShell zeigt RecipientTypeDetails.
Update Sets & Deployment
Versionskontrolle für Konfiguration. XML-basiert, manuell promotet, mit Vorschau und Konfliktanzeige. Daten werden nicht getragen — nur Konfiguration.
Update-Set Stati
- In Progress — sammelt Änderungen, ist als „current" gesetzt
- Complete — abgeschlossen, exportierbar, nicht mehr beschreibbar
- Retrieved — in Ziel-Instanz gepullt
- Previewed — Konflikte angezeigt, ggf. accept
- Committed — angewendet
Was steckt drin?
| sys_update_set | Update-Set-Container |
| sys_update_xml | Einzel-Änderung (1 Record = 1 Update) |
| sys_remote_update_set | In Ziel-Instanz: empfangene Sets |
| sys_update_preview_problem | Konflikte aus Preview |
| sys_update_set_source | Verbindung zu Quell-Instanz |
TICKET-123_kurz_beschreibung. Findest du in 6 Monaten wieder.Was Update-Sets nicht tragen
- Daten in Standard-Tabellen (Incidents, User, Gruppen-Mitgliedschaften) — bewusst, sonst überschreibst du Live-Daten
- Plugin-Aktivierungen — pro Instanz manuell
- Properties in
sys_properties— werden zwar erfasst, sind aber oft instanzspezifisch (Prod ≠ Dev). Auswählen, was rüber soll. - Scheduled Jobs aktivieren sich automatisch nach Commit — kann Mailflut auslösen
ACLs & Security
Access Control Rules sind das Tor zu jedem Datensatz. Werden bei jedem Read, Write, Create, Delete geprüft. „Insufficient privileges" beginnt fast immer hier.
Drei Bedingungen, alle wahr
Eine ACL erlaubt einen Zugriff nur, wenn alle drei True sind:
- Roles — User hat eine der genannten Rollen
- Condition — Filter auf dem Record
- Script — gibt
answer = truezurück
Leeres Roles-Feld + leere Condition + leeres Script = jeder darf. Eine fehlende ACL = niemand außer Admin darf.
Tabelle, Feld, Spezifizität
ACLs gibt es auf zwei Ebenen:
- Tabellen-ACL:
incident, Operationread - Feld-ACL:
incident.assigned_to, Operationwrite
Spezifischere ACL gewinnt. Wildcard * auf Tabellen/Feld = Default für alles, was keine eigene ACL hat.
Operationen
| Operation | Wann geprüft | Folge bei Deny |
|---|---|---|
| read | Beim Anzeigen von Records / Feldern | Record/Feld unsichtbar |
| write | Beim Speichern eines Edits | Feld read-only |
| create | Beim Insert eines neuen Records | „New"-Button fehlt |
| delete | Beim Löschen | Aktion ausgeblendet |
| execute | UI-Actions, Processors | Action nicht verfügbar |
security_admin-Elevation: User-Icon → Elevate Roles.Security Hardening
Pflicht-Settings für Produktion. Authentication, Session, Network, Datenintegrität — was muss gesetzt sein, was sollte gesetzt sein, was darf NIE in Prod aktiv sein.
Login-Schutz
- SSO erzwingen, lokale Logins minimieren
- Password Policy mit Komplexität + History
- Account Lockout nach N Fehlversuchen
- Service-Accounts:
web_service_access_only=true - MFA für Admin-Accounts (über IdP)
Browser-Schutz
- Session-Timeout 30–60 Min
- Secure Cookies (HTTPS-only)
- CSRF-Token aktiviert
- Strict Transport Security (HSTS)
- Content Security Policy für UI Pages
Nachvollziehbarkeit
- Field-Level Audit für sensitive Tabellen
- Read-Replication für Reporting
- Encryption Context für Felder mit PII
- IP-Restrictions auf Admin-URLs
- Log-Retention dokumentiert
Pflicht-Properties — Production-Baseline
~25 Settings| Property | Soll | Wirkung |
|---|---|---|
| Authentication & Session | ||
| glide.ui.session_timeout | 30–60 | Inaktivitäts-Timeout in Minuten |
| glide.ui.user_password.lifespan_in_days | 90 | Maximale Passwort-Lebensdauer |
| glide.user.max_login_attempts | 5 | Lockout nach N Fehlversuchen |
| glide.ui.password_history | 10 | Letzte N Passwörter sperren |
| glide.authenticate.sso.disable.basic | true | Basic-Auth-Bypass blockieren (nach SSO-Test!) |
| glide.basicauth.required.soap | true | SOAP nur mit Auth |
| glide.basicauth.required.scriptedprocessor | true | Scripted Processors auth-pflichtig |
| Cookies, CSRF & Headers | ||
| glide.ui.secure_cookies | true | Cookies nur über HTTPS |
| glide.cookies.http_only | true | HttpOnly-Flag → kein JS-Zugriff |
| glide.cookies.same_site_cookie_attribute | Lax | CSRF-Mitigation |
| glide.security.use_csrf_token | true | CSRF-Token in Forms |
| glide.security.csrf.strict.validation.mode | strict | CSRF-Validierung scharf |
| glide.security.strict.actions | true | UI-Actions ACL-pflichtig |
| XSS & Code Injection | ||
| glide.ui.escape_html_list_field | true | HTML-Escape in Listen |
| glide.ui.escape_text | true | HTML-Escape generisch |
| glide.ui.html.iframe_sandboxing.enabled | true | iFrame-Sandboxing |
| glide.security.allow_codetag | false | Inline-Skripte in Feldern blocken |
| glide.script.block.client.globals | true | Client-Script-Sandbox |
| glide.html.sanitize_inbound | true | Eingehendes HTML säubern |
| Debug & Logging — alle FALSE in Prod | ||
| glide.security.debug | false | ACL-Debug → Performance-Killer |
| glide.sql.debug | false | SQL-Debug → Gigabytes Log/h |
| glide.script.debug | false | Script-Debug |
| glide.ldap.debug | false | LDAP/SSO-Debug |
| Email-Sicherheit | ||
| glide.email.notification.send_to_active_only | true | Keine Mails an inaktive User |
| glide.email.test.user | leer | Sub-Prod: dev-mail · Prod: leer |
Plugins — Security-relevant
High Security Plugin
Plugin-ID com.glide.high_security. Aktiviert ein Bundle aus den obigen Properties plus zusätzliche Default-Deny-ACLs auf sensiblen System-Tabellen (sys_properties, sys_user_role, sys_security_acl).
- Aktivierung: System Definition → Plugins → suchen → Activate
- Reaktivierung nach Update: einige Properties können beim Upgrade zurückspringen
- Vor Aktivierung in TEST komplett durchspielen — Default-Deny-ACLs können Custom-Apps brechen
Explicit Roles Plugin
Plugin-ID com.glide.explicit_roles. Verlangt explizite Rolle für Read-Zugriff auf eigentlich „öffentliche" Tabellen — verhindert dass snc_internal alleine schon zu viel sieht.
Empfohlen, aber: prüft eure Custom-Apps nach Aktivierung — ggf. fehlt jetzt eine Rolle bei einer Operation die früher einfach lief.
IP- & Adapter-Restrictions
ServiceNow erlaubt Auth-Path-spezifische IP-Whitelists — z.B. „Admin-Login nur aus Firmen-VPN, Self-Service von überall".
| Tabelle | Inhalt |
|---|---|
| sys_ip_access_control | IP-Ranges mit Aktion (allow/deny) |
| sys_ip_access_control_user_role | Optional: Restriction nur für bestimmte Rollen |
| cidr_ipv4_address | CIDR-Notation für Range |
| glide.ip_authenticate.failed_attempts.cap | Max Login-Fehlversuche pro IP |
sys_properties.list → Filter name CONTAINS debug. Alle die true sind in Prod: sofort hinterfragen.sys_user_has_role.list → role.name=admin. Liste an Admins muss begründbar und minimal sein. Inaktive User raus.sys_security_acl.list → Filter auf eigene/custom ACLs. Gibt es welche mit leerem Roles + leeres Script (= jeder darf)? Notwendig?sys_user.list → web_service_access_only=true. Jeder muss begründet sein — wofür, wer ist Owner, wann zuletzt rotiert?// Security-Quick-Audit — wichtigste Properties auf einen Blick var baseline = { 'glide.ui.session_timeout': '30', 'glide.security.use_csrf_token': 'true', 'glide.ui.secure_cookies': 'true', 'glide.cookies.http_only': 'true', 'glide.security.strict.actions': 'true', 'glide.security.allow_codetag': 'false', 'glide.security.debug': 'false', 'glide.sql.debug': 'false', 'glide.basicauth.required.soap': 'true', 'glide.authenticate.sso.disable.basic': 'true' }; for (var prop in baseline) { var actual = gs.getProperty(prop, '<not set>'); var ok = (actual === baseline[prop]); gs.info((ok ? '✓' : '✗') + ' ' + prop + ' = ' + actual + (ok ? '' : ' (Soll: ' + baseline[prop] + ')')); }
sys_audit + sys_audit_relation die Grundlage. Performance-Hinweis: Audit auf vielen Tabellen aktiviert kostet schnell DB-Volumen — gezielt aktivieren.
Scripting — die nötigen 20%
Du musst nicht entwickeln, aber lesen, debuggen und in Background-Scripts kleine Operationen fahren — das spart Stunden.
Server-Scripts
| Business Rule | CRUD-Trigger auf Tabelle |
| Script Include | Wiederverwendbare Funktionen |
| Scheduled Job | Cron-artig |
| Transform Script | Bei Import-Set-Run |
| Background Script | Ad-hoc Admin-Konsole |
Client-Scripts
| Client Script | onLoad, onChange, onSubmit |
| UI Policy | No-Code: visible, mandatory, read-only |
| Catalog Client Script | Im Service Catalog |
| UI Action | Buttons / Form-Links |
Faustregel: UI Policy > Client Script, wenn No-Code reicht. Pflegt sich besser, läuft schneller.
GlideRecord — die DB-API
// Standardpattern: Filter, Query, Iteration var gr = new GlideRecord('incident'); gr.addQuery('state', '!=', 7); // nicht Closed gr.addQuery('priority', 1); gr.orderByDesc('sys_created_on'); gr.setLimit(10); gr.query(); while (gr.next()) { gs.info(gr.number + ' · ' + gr.short_description); }
// Encoded Query — aus Liste kopieren (Breadcrumb → "Copy query") var gr = new GlideRecord('incident'); gr.addEncodedQuery('active=true^assignment_group.nameLIKEsupport'); gr.query(); gs.info('Treffer: ' + gr.getRowCount());
// Update mit Vorsicht — Workflow ggf. ausschalten gr.setWorkflow(false); // keine BR/Notification gr.assignment_group = 'sys_id_neue_gruppe'; gr.update();
Variablen-Cheatsheet
In Server-Scripts verfügbar
gs— GlideSystem (gs.info, gs.error, gs.getProperty, gs.setProperty, gs.getUser, gs.eventQueue)current— aktueller Record (in Business Rules / Workflow)previous— Vorher-Stand (Business Rule, before/after update)g_request,g_response— bei Scripted Processors
In Client-Scripts verfügbar
g_form— Formular-API (setValue, setVisible, setMandatory, addInfoMessage)g_user— aktueller User (g_user.hasRole, g_user.userID)GlideAjax— Async-Call zu Script Include
setLimit(5) + gs.info. Ergebnis ansehen.update(): nur loggen, was geändert würde. Erst wenn Output stimmt → echtes Update.Logs & Debugging
Wenn etwas schiefgeht: /stats.do, syslog, Transactions. Debug-Properties sind temporär — niemals in Prod „vergessen".
Direkt-URLs
- /stats.doBuild, Memory, Threads
- /cache.doCache-Stats & Flush
- /threads.doAktive Threads
- /replication.doDB-Replication
- /xmlstats.dostats.do als XML
- /sys.scripts.doBackground Scripts
Wo schauen?
syslog— Allgemein, Level error/warning/info/debugsys_log_transaction— HTTP, Response-Time, URL, Usersysevent— Event-Queue (processed?)sys_email— Mail-Queuesys_audit— Wer hat was geändertsyslog_app_scope— Logs aus Scoped Apps
Temporär an
glide.security.debug— ACL-Auswertungglide.script.debug— Script-Executionglide.sql.debug— SQL-Queries (verbose!)glide.ldap.debug— Auth/SSO/LDAPglide.workflow.debug— Workflow-Engine
Alle in Prod = false.
sys_log_transaction.list → Filter response_time >= 5000, Sort by response_time desc. Welche URL? Welcher User? Wann?syslog.list → message LIKE Slow. Zeigt SQL und Tabelle.sys_trigger.list → Filter aktive Jobs, Sort nach next_action. Hängt einer in „Running" über Stunden?Eigene Log-Messages
// gs.log() ist deprecated. Stattdessen: gs.info('Normal-Operation: ' + count); gs.warn('Achtung: ' + msg); gs.error('Bricht ab: ' + err); // Mit Source für leichtes Filtern gs.info('Provisioning OK: ' + user, 'EntraSync'); // → in syslog.list filterbar mit source=EntraSync
glide.sql.debug = true in Prod produziert Gigabytes Log pro Stunde. Nur mit Stoppuhr aktivieren, sofort wieder aus.
Scheduled Jobs & Imports
Scheduled Jobs treiben fast alle Hintergrundarbeit: Imports, Reports, Cleanups, Notifications. Imports gehen über Staging-Tabellen mit Transform Maps.
Job-Typen
| Script Execution | Beliebiger Server-Code zeitgesteuert |
| Scheduled Import | Import-Set-Run automatisch |
| Scheduled Report | Report rendern + per Mail |
| Generate Events | Event auf Records, die Bedingung erfüllen |
| Data Export | CSV/XML auf Mid-Server / Mail |
Wo verwaltet
sys_trigger— alle Scheduled Jobs (System & Custom)sysauto— Basis für Custom Schedulessysauto_script— Script-Schedulesscheduled_import_set— Import-Schedulessys_import_set_run— Run-History
Import-Pipeline
Datenquelle (CSV / JDBC / REST / LDAP)
↓
Import Set Table (u_*) — Staging
↓
Transform Map — Field-Mapping + Coalesce
↓
Ziel-Tabelle (sys_user, cmdb_ci, …)
- Coalesce-Feld = Match-Kriterium. Existiert Record mit gleichem Wert? → Update. Sonst → Insert.
- Run Business Rules / Run Script in der Transform Map: hier laufen Trigger an oder eben nicht.
- Transform Script für Datenbereinigung (z.B.
onBefore,onAfter,onForeignInsert).
u_import_users.Spaltenüberschriften der CSV werden 1:1 als Felder übernommen.user_name setzen → bestehende User werden geupdatet, neue angelegt.sys_user prüfen — ggf. active=false für die Test-Imports erst, falls Mails feuern könnten.sys_import_set_run.list zeigt jeden Lauf, Anzahl insert/update/error. Errors klickbar bis auf Zeile.Job-Hygiene
Was läuft, was hängt
sys_trigger.list, Filterstate=running AND next_action<javascript:gs.daysAgo(0)- Execution History pro Job: Tab im Form, sortiert nach Startzeit
- Scheduler-Semaphore auf
/stats.do— wenn Max erreicht: Jobs warten in Queue
Stuck-Job retten
- Im Job: state vorsichtig auf „ready" zurücksetzen
- Wenn Worker hängt: Node-Restart (kurzer Sessions-Reset)
- Bei Wiederholung: Script analysieren → wahrscheinlich endlose Query / fehlende Limits