Scenario: ServiceNow to Azure Identity Ticket Routing
Overview
This scenario describes an outbound integration from ServiceNow to Azure Entra ID, where ServiceNow looks up user information from Microsoft Graph to route tickets based on organizational data.
Direction: ServiceNow → Azure (opposite of Scenario 01)
The Setup — What Was Built and Why
Company: Contoso Corp Date: June 2025
The IT Service Desk wants to automatically route identity-related tickets (password resets, access requests, account issues) based on the user's department, manager, and location — data that lives in Azure Entra ID, not ServiceNow.
An integration engineer builds a Business Rule that calls Azure Graph API to look up users.
Integration Flow Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ SERVICENOW INSTANCE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. BUSINESS RULE: "Auto-route identity tickets via Entra" │
│ ├── Table: incident │
│ ├── When: after insert │
│ ├── Condition: category = 'Identity' OR category = 'Access Request' │
│ └── Action: Calls Script Include "AzureGraphRouter" │
│ │
│ ↓ │
│ │
│ 2. SCRIPT INCLUDE: "AzureGraphRouter" │
│ ├── Created by: mike.johnson@contoso.com │
│ ├── Last modified: 2025-06-15 │
│ └── Logic: │
│ - Extract caller's email from incident │
│ - Call REST Message to look up user in Entra │
│ - Parse department, manager, location from response │
│ - Route ticket to appropriate fulfillment group │
│ │
│ ↓ │
│ │
│ 3. OUTBOUND REST MESSAGE: "Azure Graph API" │
│ ├── Name: "Graph - sn-ticket-router" │
│ ├── Endpoint: https://graph.microsoft.com │
│ └── HTTP Methods: │
│ ├── GetUserByUPN: GET /v1.0/users/{upn} │
│ ├── GetManager: GET /v1.0/users/{id}/manager │
│ └── GetDirectReports: GET /v1.0/users/{id}/directReports │
│ │
│ ↓ │
│ │
│ 4. OAUTH ENTITY PROFILE: "Microsoft Graph OAuth" │
│ ├── OAuth Application: "sn-ticket-router-oauth" │
│ ├── Client ID: b085873d-e521-4e2d-9ae6-92554800c8de │
│ ├── Client Secret: [stored in ServiceNow] │
│ └── Grant Type: client_credentials │
│ │
│ ↓ │
│ │
│ 5. OAUTH PROVIDER: "Microsoft identity platform" │
│ ├── Token URL: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
│ ├── Scope: https://graph.microsoft.com/.default │
│ └── Auth flow: OAuth 2.0 Client Credentials │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ HTTPS + OAuth Token
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ AZURE ENTRA ID │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 6. APP REGISTRATION: "sn-ticket-router" │
│ ├── Application ID: b085873d-e521-4e2d-9ae6-92554800c8de │
│ ├── Created: 2025-06-10 │
│ ├── Owner: mike.johnson@contoso.com │
│ └── Client Secret: expires 2026-12-10 │
│ │
│ 7. SERVICE PRINCIPAL (Enterprise App) │
│ ├── Object ID: 7a8b9c0d-... │
│ ├── Sign-in logs: ~200 calls/day │
│ └── Last sign-in: 2 minutes ago │
│ │
│ 8. API PERMISSIONS (Microsoft Graph) │
│ ├── User.Read.All (Application) — Read all users' profiles │
│ ├── Directory.Read.All (Application) — Read directory data │
│ └── [Optional] AuditLog.Read.All — Read audit logs │
│ │
│ 9. MICROSOFT GRAPH API │
│ └── Returns: displayName, department, jobTitle, manager, │
│ officeLocation, userPrincipalName, etc. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Data Flow Example
- User creates ticket: "I need access to the Finance SharePoint site"
- Business Rule fires: Category is "Access Request"
- Script calls Graph API: GET /v1.0/users/jane.doe@contoso.com
- Graph returns:
{
"displayName": "Jane Doe",
"department": "Finance",
"jobTitle": "Senior Accountant",
"manager": { "displayName": "Bob Smith" },
"officeLocation": "Seattle"
} - Script routes ticket: Finance users → "IT-Finance-Provisioning" group
Why This Matters for SecurityV0
Cross-Service Identity Chain
ServiceNow Business Rule
└── owned by: mike.johnson (ServiceNow user)
ServiceNow Script Include "AzureGraphRouter"
└── created by: mike.johnson
└── calls: REST Message
ServiceNow OAuth Entity "sn-ticket-router-oauth"
└── client_id: b085873d-e521-4e2d-9ae6-92554800c8de
└── links to: Azure App Registration
Azure App Registration "sn-ticket-router"
└── app_id: b085873d-e521-4e2d-9ae6-92554800c8de
└── owner: mike.johnson@contoso.com
└── permissions: User.Read.All, Directory.Read.All
What Can Go Wrong
| Month | Event | Risk |
|---|---|---|
| 1-3 | Working as designed | None |
| 6 | Mike leaves company | Orphaned on both sides |
| 8 | Someone adds User.ReadWrite.All to fix a bug | Scope drift |
| 10 | ServiceNow upgrade breaks auth, admin regenerates secret | No owner to approve |
| 12 | Client secret expires | Integration breaks, no one to fix |
Blast Radius
If this integration is compromised (ServiceNow Script Include vulnerability, stolen OAuth credentials), the attacker can:
- Read all user profiles in Azure Entra ID (names, emails, phone numbers, departments)
- Read organizational hierarchy (who reports to whom, department structure)
- Enumerate the entire directory (all 2,000+ employees)
ServiceNow Tables to Query for Discovery
To discover this type of integration, SecurityV0 needs to query the following tables. Each section shows both the API query and the UI navigation path.
1. OAuth Applications (oauth_entity)
What it contains: OAuth client applications registered in ServiceNow (stores client_id that links to Azure).
API Query:
Table: oauth_entity
Fields: sys_id, name, client_id, active, type, sys_created_by, sys_created_on
Query: active=true
UI Navigation:
All > System OAuth > Application Registry
Or use the Navigator filter: type oauth_entity.list
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | Display name of the OAuth application |
| Client ID | The OAuth client_id (matches Azure App Registration app_id) |
| Client Secret | Hidden, but can be regenerated here |
| Active | Whether the OAuth app is enabled |
| Type | "OAuth API endpoint for external clients" for inbound, varies for outbound |
| Created by | User who created the OAuth app |
Screenshot location: Click on any OAuth app to see full details including redirect URIs, token lifespans, and linked profiles.
2. OAuth Entity Profiles (oauth_entity_profile)
What it contains: Links OAuth applications to OAuth providers and defines grant types.
API Query:
Table: oauth_entity_profile
Fields: sys_id, name, oauth_entity, oauth_provider, grant_type
Query: [join to oauth_entity]
UI Navigation:
All > System OAuth > Application Registry > [Select an OAuth App] > OAuth Entity Profiles (related list)
Or directly: type oauth_entity_profile.list in Navigator
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | Profile name |
| OAuth Entity | Link to parent OAuth application |
| OAuth Provider | Link to provider (Microsoft, Okta, etc.) |
| Grant Type | client_credentials, authorization_code, etc. |
| Default Grant User | User to impersonate for client_credentials flow |
3. OAuth Providers (oauth_entity_provider)
What it contains: External OAuth providers (Microsoft, Google, Okta) with their token endpoints.
API Query:
Table: oauth_entity_provider
Fields: sys_id, name, token_url, authorization_url
Query: nameLIKEmicrosoft OR token_urlLIKEmicrosoftonline
UI Navigation:
All > System OAuth > Application Registry
Then filter by Type = "OAuth Provider"
Or: type oauth_entity.list and add filter type=oauth_provider
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | Provider name (e.g., "Microsoft Azure AD") |
| Token URL | https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token |
| Authorization URL | For auth code flow |
| Client ID | ServiceNow's client_id registered with the provider |
4. Outbound REST Messages (sys_rest_message)
What it contains: Configurations for calling external REST APIs (like Microsoft Graph).
API Query:
Table: sys_rest_message
Fields: sys_id, name, rest_endpoint, authentication_type, sys_created_by
Query: rest_endpointLIKEgraph.microsoft OR rest_endpointLIKEmanagement.azure
UI Navigation:
All > System Web Services > Outbound > REST Message
Or: type sys_rest_message.list in Navigator
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | REST message name (e.g., "Microsoft Graph API") |
| Endpoint | Base URL (e.g., https://graph.microsoft.com) |
| Authentication | None, Basic, OAuth 2.0, etc. |
| OAuth Profile | Which OAuth profile to use for auth |
To see HTTP methods: Click on a REST Message, then scroll to "HTTP Methods" related list.
5. REST Message HTTP Methods (sys_rest_message_fn)
What it contains: Individual HTTP operations (GET /users, POST /groups, etc.).
API Query:
Table: sys_rest_message_fn
Fields: sys_id, name, rest_message, http_method, rest_endpoint
Query: rest_message={sys_id from above}
UI Navigation:
All > System Web Services > Outbound > REST Message > [Select a message] > HTTP Methods (related list at bottom)
Or: type sys_rest_message_fn.list in Navigator
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | Method name (e.g., "GetUserByUPN") |
| HTTP Method | GET, POST, PUT, DELETE, PATCH |
| Endpoint | Relative path (e.g., /v1.0/users/${upn}) |
| Authentication | Inherit from parent or override |
6. Business Rules (sys_script)
What it contains: Server-side scripts triggered by table operations (insert, update, delete).
API Query:
Table: sys_script
Fields: sys_id, name, collection, when, condition, script, active, sys_created_by
Query: active=true^scriptLIKEAzure OR scriptLIKEGraph OR scriptLIKEEntra
UI Navigation:
All > System Definition > Business Rules
Or: type sys_script.list in Navigator
To find Azure-related rules: Use the search/filter to search in the Script field for "Azure", "Graph", "REST", or specific REST message names.
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | Rule name |
| Table | Which table triggers this rule (incident, task, etc.) |
| When | before/after/async |
| Insert/Update/Delete/Query | Which operations trigger it |
| Active | Is it enabled |
| Script | The actual code (click to view) |
7. Script Includes (sys_script_include)
What it contains: Reusable server-side JavaScript classes/functions called by Business Rules.
API Query:
Table: sys_script_include
Fields: sys_id, name, api_name, script, active, sys_created_by, sys_updated_by
Query: active=true^scriptLIKERESTMessage OR scriptLIKEOAuthClient
UI Navigation:
All > System Definition > Script Includes
Or: type sys_script_include.list in Navigator
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | Class/function name |
| API Name | Global API name to call from other scripts |
| Client Callable | Can browser-side scripts call this |
| Active | Is it enabled |
| Script | The actual code |
| Created by / Updated by | Ownership trail |
Tip: Search for script includes that reference RESTMessageV2, sn_ws.RESTMessageV2, or specific OAuth entity names.
8. Scheduled Jobs (sysauto_script)
What it contains: Time-based automations that run on a schedule.
API Query:
Table: sysauto_script
Fields: sys_id, name, script, run_as, active, sys_created_by
Query: active=true^scriptLIKEAzure OR scriptLIKEGraph
UI Navigation:
All > System Definition > Scheduled Jobs > Scheduled Script Executions
Or: type sysauto_script.list in Navigator
What you'll see in the UI:
| Column | Description |
|---|---|
| Name | Job name |
| Run | Schedule (Daily, Weekly, etc.) |
| Run as | Which user context it executes under |
| Active | Is it enabled |
| Next action | When it will run next |
| Script | The code to execute |
Quick Navigation Cheat Sheet
| What you're looking for | Navigator filter |
|---|---|
| OAuth Applications | oauth_entity.list |
| OAuth Profiles | oauth_entity_profile.list |
| REST Messages | sys_rest_message.list |
| REST HTTP Methods | sys_rest_message_fn.list |
| Business Rules | sys_script.list |
| Script Includes | sys_script_include.list |
| Scheduled Jobs | sysauto_script.list |
| Users | sys_user.list |
| User Roles | sys_user_has_role.list |
Pro tip: In any list view, right-click column headers to add/remove columns, or use the filter icon to search within specific fields.
Complete Discovery Query Chain
┌─────────────────────────────────────────────────────────────────────────────┐
│ DISCOVERY CHAIN: Find all ServiceNow → Azure integrations │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Find Azure-related REST Messages │
│ sys_rest_message WHERE rest_endpoint LIKE '%graph.microsoft%' │
│ OR rest_endpoint LIKE '%management.azure%' │
│ │
│ 2. For each REST Message, find authentication config │
│ → What OAuth entity does it use? │
│ → What credentials (client_id) are configured? │
│ │
│ 3. Find OAuth Entity by client_id │
│ oauth_entity WHERE client_id = {value} │
│ → Who created it? (sys_created_by) │
│ → Is it active? │
│ │
│ 4. Find what CALLS this REST Message │
│ sys_script WHERE script LIKE '%{rest_message_name}%' │
│ sys_script_include WHERE script LIKE '%{rest_message_name}%' │
│ sysauto_script WHERE script LIKE '%{rest_message_name}%' │
│ │
│ 5. Build execution chain │
│ Business Rule → Script Include → REST Message → OAuth Entity │
│ │
│ 6. Correlate to Azure │
│ oauth_entity.client_id = Azure App Registration.app_id │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
What SecurityV0 Detects
Finding 1: Orphaned Cross-Service Integration
TRIGGER: OAuth Entity "sn-ticket-router-oauth" created by mike.johnson
mike.johnson account status: INACTIVE in ServiceNow
Azure SP owner mike.johnson@contoso.com: DELETED in Entra ID
EXPLANATION:
This outbound integration to Microsoft Graph was created by
mike.johnson, who is no longer active in either system.
The integration continues to execute via Business Rule
"Auto-route identity tickets via Entra" on every identity ticket.
No one is accountable for this cross-service execution path.
EVIDENCE:
- ServiceNow OAuth entity: sn-ticket-router-oauth
- Azure App Registration: sn-ticket-router
- Client ID: b085873d-e521-4e2d-9ae6-92554800c8de
- Last execution: 2 minutes ago
- Owner (both sides): MISSING
Finding 2: High-Privilege API Access Without Owner
TRIGGER: Azure SP has User.Read.All + Directory.Read.All
No owner assigned
Called from ServiceNow Business Rule
BLAST RADIUS:
- Can read all 2,000+ user profiles
- Can enumerate entire organizational hierarchy
- Can access department, manager, location for all employees
REMEDIATION:
1. Assign owner on Azure side (App Registration)
2. Assign owner on ServiceNow side (OAuth Entity, Script Include)
3. Review if Directory.Read.All is necessary (User.Read.All may suffice)
4. Set up alerting for unusual query patterns
Key Insight
Scenario 01 (Azure → ServiceNow): Azure Function calls into ServiceNow to create tickets.
Scenario 02 (ServiceNow → Azure): ServiceNow calls out to Azure to look up user data.
Both create cross-service execution paths where:
- An autonomous identity (OAuth app / Service Principal) executes continuously
- Ownership can decay on either side (or both)
- Scope can drift in either system
- Existing tools only see "their" side of the integration
SecurityV0 connects both sides to show the complete execution path, blast radius, and ownership chain.