ServiceNow Integration
This guide covers the integration between NopeSight and ServiceNow, enabling seamless synchronization of configuration items, automated incident creation, and bidirectional data flow.
Overview
The NopeSight-ServiceNow integration provides:
- Bidirectional CI Synchronization - Keep CMDB data in sync
- Automated Incident Management - Create and update incidents
- Change Request Integration - Link changes to CI relationships
- Service Mapping Sync - Share service dependencies
- Real-time Updates - Webhook-based synchronization
Architecture
Installation
1. Install NopeSight Integration App
- In ServiceNow, navigate to System Applications > Applications
- Search for "NopeSight Integration"
- Click Install
- Follow the installation wizard
2. Configure Integration User
// Create integration user in ServiceNow
var user = new GlideRecord('sys_user');
user.initialize();
user.user_name = 'nopesight_integration';
user.first_name = 'NopeSight';
user.last_name = 'Integration';
user.email = 'nopesight@company.com';
user.active = true;
user.insert();
// Assign roles
var role = new GlideRecord('sys_user_has_role');
role.initialize();
role.user = user.sys_id;
role.role = getRoleByName('itil');
role.insert();
role.initialize();
role.user = user.sys_id;
role.role = getRoleByName('import_admin');
role.insert();
3. Generate API Credentials
# In NopeSight
curl -X POST https://api.nopesight.com/v1/integrations/servicenow/setup \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instance_url": "https://company.service-now.com",
"username": "nopesight_integration",
"password": "secure_password"
}'
Configuration
Connection Settings
# NopeSight configuration
integrations:
servicenow:
enabled: true
instance_url: https://company.service-now.com
auth:
method: oauth2
client_id: ${SERVICENOW_CLIENT_ID}
client_secret: ${SERVICENOW_CLIENT_SECRET}
sync:
interval: 300 # seconds
batch_size: 100
field_mapping:
ci:
name: name
serial_number: serial_number
ip_address: ip_address
os: os
location: location
department: u_department
webhook:
endpoint: /api/integrations/servicenow/webhook
secret: ${WEBHOOK_SECRET}
ServiceNow Business Rules
// Business rule for CI updates
(function executeRule(current, previous /*null when async*/) {
// Only sync if NopeSight flag is set
if (current.u_sync_to_nopesight != true) {
return;
}
var nopesight = new NopeSightIntegration();
// Prepare CI data
var ciData = {
sys_id: current.sys_id.toString(),
name: current.name.toString(),
sys_class_name: current.sys_class_name.toString(),
serial_number: current.serial_number.toString(),
ip_address: current.ip_address.toString(),
operational_status: current.operational_status.toString(),
attributes: {}
};
// Add class-specific attributes
if (current.sys_class_name == 'cmdb_ci_server') {
ciData.attributes.cpu_count = current.cpu_count.toString();
ciData.attributes.ram = current.ram.toString();
ciData.attributes.disk_space = current.disk_space.toString();
}
// Send to NopeSight
var response = nopesight.updateCI(ciData);
if (!response.success) {
gs.error('Failed to sync CI to NopeSight: ' + response.error);
}
})(current, previous);
Data Synchronization
CI Synchronization
# NopeSight sync script
import requests
from datetime import datetime
class ServiceNowSync:
def __init__(self, config):
self.snow_url = config['instance_url']
self.auth = (config['username'], config['password'])
self.headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
def sync_configuration_items(self):
"""Sync CIs from ServiceNow to NopeSight"""
# Get CIs modified in last sync interval
query = f"sys_updated_on>{self.last_sync_time}"
response = requests.get(
f"{self.snow_url}/api/now/table/cmdb_ci",
params={'sysparm_query': query},
auth=self.auth,
headers=self.headers
)
cis = response.json()['result']
for ci in cis:
# Transform ServiceNow CI to NopeSight format
nopesight_ci = self.transform_ci(ci)
# Check if CI exists in NopeSight
existing = self.get_ci_by_external_id(ci['sys_id'])
if existing:
self.update_ci(existing['id'], nopesight_ci)
else:
self.create_ci(nopesight_ci)
def transform_ci(self, snow_ci):
"""Transform ServiceNow CI to NopeSight format"""
# Map CI class to NopeSight type
type_mapping = {
'cmdb_ci_server': 'server',
'cmdb_ci_win_server': 'server',
'cmdb_ci_linux_server': 'server',
'cmdb_ci_app_server': 'application',
'cmdb_ci_database': 'database'
}
return {
'name': snow_ci['name'],
'type': type_mapping.get(snow_ci['sys_class_name'], 'other'),
'external_id': snow_ci['sys_id'],
'external_source': 'servicenow',
'attributes': {
'serial_number': snow_ci.get('serial_number', ''),
'ip_address': snow_ci.get('ip_address', ''),
'os': snow_ci.get('os', ''),
'location': snow_ci.get('location', {}).get('display_value', ''),
'operational_status': snow_ci.get('operational_status', '')
},
'custom_fields': {
'snow_class': snow_ci['sys_class_name'],
'snow_updated': snow_ci['sys_updated_on']
}
}
Incident Integration
// ServiceNow Script Include
var NopeSightIncidentIntegration = Class.create();
NopeSightIncidentIntegration.prototype = {
initialize: function() {
this.nopesightAPI = new NopeSightAPI();
},
createIncidentFromEvent: function(eventData) {
var incident = new GlideRecord('incident');
incident.initialize();
// Map event data to incident
incident.short_description = eventData.title;
incident.description = eventData.description;
incident.impact = this.mapSeverityToImpact(eventData.severity);
incident.urgency = this.mapSeverityToUrgency(eventData.severity);
incident.category = 'Infrastructure';
incident.subcategory = 'Server';
// Link to CI if available
if (eventData.affected_cis && eventData.affected_cis.length > 0) {
var ci = this.findCIByExternalId(eventData.affected_cis[0]);
if (ci) {
incident.cmdb_ci = ci.sys_id;
}
}
// Add NopeSight reference
incident.u_nopesight_event_id = eventData.id;
incident.u_source = 'NopeSight Discovery';
// Insert incident
var incidentId = incident.insert();
// Add work notes with AI analysis
if (eventData.ai_analysis) {
incident.work_notes = '[AI Analysis]\n' + eventData.ai_analysis;
incident.update();
}
return incidentId;
},
updateIncidentStatus: function(incidentSysId, status, notes) {
var incident = new GlideRecord('incident');
if (incident.get(incidentSysId)) {
// Map status
var stateMap = {
'resolved': 6,
'closed': 7,
'in_progress': 2
};
incident.state = stateMap[status] || incident.state;
if (notes) {
incident.work_notes = notes;
}
incident.update();
// Sync back to NopeSight if needed
if (incident.u_nopesight_event_id) {
this.nopesightAPI.updateEventStatus(
incident.u_nopesight_event_id,
status
);
}
}
},
mapSeverityToImpact: function(severity) {
var impactMap = {
'critical': 1, // High
'high': 2, // Medium
'medium': 3, // Low
'low': 3 // Low
};
return impactMap[severity] || 3;
},
mapSeverityToUrgency: function(severity) {
var urgencyMap = {
'critical': 1, // High
'high': 1, // High
'medium': 2, // Medium
'low': 3 // Low
};
return urgencyMap[severity] || 3;
}
};
Webhook Configuration
ServiceNow Webhook Setup
// Register webhook in ServiceNow
var webhook = new GlideRecord('sys_webhook');
webhook.initialize();
webhook.name = 'NopeSight CI Update';
webhook.url = 'https://api.nopesight.com/v1/webhooks/servicenow';
webhook.method = 'POST';
webhook.authentication_type = 'bearer_token';
webhook.bearer_token = gs.getProperty('nopesight.webhook.token');
webhook.content_type = 'application/json';
webhook.active = true;
webhook.insert();
// Create webhook subscription
var subscription = new GlideRecord('sys_webhook_subscription');
subscription.initialize();
subscription.webhook = webhook.sys_id;
subscription.table = 'cmdb_ci';
subscription.operation = 'insert_or_update';
subscription.condition = 'u_sync_to_nopesight=true';
subscription.active = true;
subscription.insert();
NopeSight Webhook Handler
from flask import Flask, request, jsonify
import hmac
import hashlib
app = Flask(__name__)
@app.route('/webhooks/servicenow', methods=['POST'])
def handle_servicenow_webhook():
# Verify webhook signature
signature = request.headers.get('X-ServiceNow-Signature')
if not verify_signature(request.data, signature):
return jsonify({'error': 'Invalid signature'}), 401
# Parse webhook data
data = request.json
# Route based on table
if data['table'] == 'cmdb_ci':
handle_ci_update(data)
elif data['table'] == 'incident':
handle_incident_update(data)
elif data['table'] == 'change_request':
handle_change_update(data)
return jsonify({'status': 'processed'}), 200
def verify_signature(payload, signature):
"""Verify ServiceNow webhook signature"""
secret = app.config['SERVICENOW_WEBHOOK_SECRET']
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
def handle_ci_update(data):
"""Handle CI update from ServiceNow"""
ci_data = data['record']
# Transform and update in NopeSight
ci_service = CIService()
ci_service.sync_from_servicenow(ci_data)
Field Mapping
Standard Field Mappings
| ServiceNow Field | NopeSight Field | Notes |
|---|---|---|
| sys_id | external_id | Unique identifier |
| name | name | Display name |
| sys_class_name | type | Mapped to NopeSight types |
| serial_number | attributes.serial_number | |
| ip_address | attributes.ip_address | |
| operational_status | status | Status mapping required |
| location | attributes.location | Reference field |
| company | custom_fields.company | |
| department | custom_fields.department |
Custom Field Mapping
// ServiceNow Transform Map Script
(function transformEntry(source) {
var target = new GlideRecord('cmdb_ci_server');
// Standard mappings
target.name = source.name;
target.serial_number = source.serial_number;
// Custom field mappings
target.u_nopesight_id = source.nopesight_id;
target.u_last_discovered = source.last_scan_date;
target.u_discovery_source = 'NopeSight';
// Map NopeSight attributes to ServiceNow
if (source.attributes) {
target.cpu_count = source.attributes.cpu_cores;
target.ram = source.attributes.memory_gb * 1024; // Convert GB to MB
target.disk_space = source.attributes.disk_gb;
}
// Map status
var statusMap = {
'active': '1', // Operational
'inactive': '2', // Non-Operational
'maintenance': '3', // Repair in Progress
'retired': '6' // Retired
};
target.operational_status = statusMap[source.status] || '1';
return target;
})(source);
Advanced Features
Service Mapping Integration
def sync_service_dependencies():
"""Sync service dependencies between platforms"""
# Get NopeSight service map
services = nopesight_client.get('/services')
for service in services['data']:
# Get dependencies
deps = nopesight_client.get(f'/services/{service["id"]}/dependencies')
# Create or update in ServiceNow
snow_service = find_or_create_service(service)
# Map dependencies
for upstream in deps['dependencies']['upstream']:
create_service_relationship(
parent=upstream['service_id'],
child=snow_service['sys_id'],
type='depends_on'
)
def create_service_relationship(parent, child, type):
"""Create service relationship in ServiceNow"""
relationship = {
'parent': parent,
'child': child,
'type': {'value': type},
'u_discovered_by': 'NopeSight'
}
response = snow_client.post(
'/api/now/table/cmdb_rel_ci',
json=relationship
)
return response.json()
Change Management Integration
// Link changes to NopeSight discoveries
var ChangeNopeSightIntegration = Class.create();
ChangeNopeSightIntegration.prototype = {
linkChangeToDiscovery: function(changeId, discoveryData) {
var change = new GlideRecord('change_request');
if (change.get(changeId)) {
// Add relationship insights
var insights = this.getRelationshipInsights(
change.cmdb_ci.toString()
);
// Update change with impact analysis
change.u_nopesight_impact = JSON.stringify({
affected_services: insights.affected_services,
dependency_count: insights.total_dependencies,
risk_score: insights.risk_score,
ai_recommendations: insights.recommendations
});
change.update();
// Create relationship records
this.createImpactedCIRelationships(
changeId,
insights.impacted_cis
);
}
},
getRelationshipInsights: function(ciId) {
var nopesight = new NopeSightAPI();
// Get CI relationships from NopeSight
var relationships = nopesight.getCIRelationships(ciId);
// Get AI analysis
var analysis = nopesight.getImpactAnalysis(ciId);
return {
affected_services: analysis.affected_services,
total_dependencies: relationships.length,
risk_score: analysis.risk_score,
impacted_cis: relationships.map(function(rel) {
return rel.target_ci;
}),
recommendations: analysis.recommendations
};
}
};
Monitoring and Troubleshooting
Health Check Endpoint
@app.route('/integrations/servicenow/health', methods=['GET'])
def health_check():
"""ServiceNow integration health check"""
health = {
'status': 'healthy',
'checks': {}
}
# Check ServiceNow connectivity
try:
response = requests.get(
f"{SNOW_URL}/api/now/table/sys_properties",
auth=SNOW_AUTH,
timeout=5
)
health['checks']['servicenow_api'] = {
'status': 'ok' if response.status_code == 200 else 'error',
'response_time': response.elapsed.total_seconds()
}
except Exception as e:
health['checks']['servicenow_api'] = {
'status': 'error',
'error': str(e)
}
health['status'] = 'unhealthy'
# Check sync status
last_sync = get_last_sync_time()
health['checks']['sync_status'] = {
'last_sync': last_sync,
'sync_lag': (datetime.now() - last_sync).seconds,
'pending_items': get_sync_queue_size()
}
return jsonify(health)
Common Issues
troubleshooting:
authentication_errors:
symptom: "401 Unauthorized errors"
causes:
- Expired credentials
- Incorrect OAuth configuration
- User account locked
solutions:
- Regenerate OAuth tokens
- Verify client ID/secret
- Check user account status
sync_delays:
symptom: "Data not syncing timely"
causes:
- Large backlog
- Rate limiting
- Network issues
solutions:
- Increase batch size
- Implement queue prioritization
- Check network connectivity
field_mapping_issues:
symptom: "Missing or incorrect data"
causes:
- Field name changes
- Custom field not mapped
- Data type mismatch
solutions:
- Review field mappings
- Update transform scripts
- Add data validation
Best Practices
1. Data Governance
- Define master data source for each CI type
- Implement conflict resolution rules
- Regular data quality audits
2. Performance
- Use bulk APIs for large syncs
- Implement intelligent caching
- Monitor API rate limits
3. Security
- Use OAuth 2.0 over basic auth
- Encrypt sensitive field data
- Audit all integration activities
4. Error Handling
- Implement retry logic with backoff
- Queue failed syncs for replay
- Alert on critical failures