Skip to content

6.18 ITSM Integration

6.18.1 ITSM Integration Overview

Integration Architecture

itsm_integration_architecture:
  purpose: "Integrate SD-WAN operations with enterprise IT Service Management"

  primary_itsm: "ServiceNow"
  version: "Tokyo"

  integration_points:
    incident_management:
      direction: "SD-WAN → ServiceNow"
      trigger: "Alarms and events"
      automation: "Auto-ticket creation"

    change_management:
      direction: "Bidirectional"
      trigger: "Configuration changes"
      automation: "Change record sync"

    cmdb:
      direction: "SD-WAN → ServiceNow"
      trigger: "Device discovery/changes"
      automation: "CI sync"

    service_catalog:
      direction: "ServiceNow → SD-WAN"
      trigger: "Service requests"
      automation: "Provisioning workflows"

ServiceNow Integration Components

servicenow_components:
  integration_hub:
    type: "Mid Server"
    location: "Mumbai DC"
    connectivity: "HTTPS to vManage API"

  tables_used:
    incidents: "incident"
    changes: "change_request"
    cmdb_devices: "cmdb_ci_netgear"
    cmdb_circuits: "cmdb_ci_circuit"
    events: "em_event"

  integration_user:
    username: "svc_sdwan_integration"
    roles:
      - "itil"
      - "evt_mgmt_integration"
      - "cmdb_write"
    api_access: "REST API enabled"

6.18.2 Incident Management Integration

Auto-Ticket Creation

incident_auto_creation:
  trigger_events:
    severity_1:
      sd_wan_alarms:
        - "control-connection-state-change"
        - "site-down"
        - "cluster-state-change"
      auto_create: true
      priority: "1 - Critical"
      assignment_group: "Network Operations"

    severity_2:
      sd_wan_alarms:
        - "bfd-state-change"
        - "interface-state-change"
        - "omp-state-change"
      auto_create: true
      priority: "2 - High"
      assignment_group: "Network Operations"

    severity_3:
      sd_wan_alarms:
        - "certificate-expiring"
        - "memory-threshold"
        - "cpu-threshold"
      auto_create: true
      priority: "3 - Moderate"
      assignment_group: "Network Operations"

  deduplication:
    enabled: true
    window: "30 minutes"
    key_fields:
      - "device_id"
      - "alarm_type"
      - "interface"

ServiceNow Integration Script

#!/usr/bin/env python3
"""
SD-WAN to ServiceNow Incident Integration
Creates and updates incidents from SD-WAN alarms
"""

import requests
import json
from datetime import datetime
import hashlib

class ServiceNowIncidentIntegration:
    def __init__(self, snow_instance, snow_user, snow_password, vmanage_host, vmanage_user, vmanage_password):
        self.snow_url = f"https://{snow_instance}.service-now.com"
        self.snow_auth = (snow_user, snow_password)

        self.vmanage_url = f"https://{vmanage_host}"
        self.vmanage_session = requests.Session()
        self.vmanage_session.verify = False
        self.authenticate_vmanage(vmanage_user, vmanage_password)

        # Severity mapping
        self.severity_map = {
            'Critical': '1',
            'Major': '2',
            'Minor': '3',
            'Warning': '4'
        }

        # Assignment groups
        self.assignment_groups = {
            'Critical': 'Network Operations - Tier 3',
            'Major': 'Network Operations - Tier 2',
            'Minor': 'Network Operations - Tier 1',
            'Warning': 'Network Operations - Tier 1'
        }

    def authenticate_vmanage(self, username, password):
        """Authenticate to vManage"""
        auth_url = f"{self.vmanage_url}/j_security_check"
        payload = {'j_username': username, 'j_password': password}
        self.vmanage_session.post(auth_url, data=payload)

        token_url = f"{self.vmanage_url}/dataservice/client/token"
        token_response = self.vmanage_session.get(token_url)
        if token_response.status_code == 200:
            self.vmanage_session.headers['X-XSRF-TOKEN'] = token_response.text

    def get_active_alarms(self):
        """Get active alarms from vManage"""
        url = f"{self.vmanage_url}/dataservice/alarms"
        params = {'query': json.dumps({'rules': [{'field': 'active', 'value': ['true']}]})}

        response = self.vmanage_session.get(url, params=params)
        return response.json().get('data', []) if response.status_code == 200 else []

    def generate_dedup_key(self, alarm):
        """Generate deduplication key for alarm"""
        key_string = f"{alarm.get('system-ip')}-{alarm.get('type')}-{alarm.get('component')}"
        return hashlib.md5(key_string.encode()).hexdigest()

    def check_existing_incident(self, dedup_key):
        """Check if incident already exists"""
        url = f"{self.snow_url}/api/now/table/incident"
        params = {
            'sysparm_query': f"correlation_id={dedup_key}^active=true",
            'sysparm_limit': 1
        }

        response = requests.get(url, auth=self.snow_auth, params=params)
        if response.status_code == 200:
            results = response.json().get('result', [])
            return results[0] if results else None
        return None

    def create_incident(self, alarm):
        """Create ServiceNow incident from alarm"""
        dedup_key = self.generate_dedup_key(alarm)

        # Check for existing incident
        existing = self.check_existing_incident(dedup_key)
        if existing:
            return self.update_incident(existing['sys_id'], alarm)

        severity = alarm.get('severity', 'Minor')

        incident_data = {
            'caller_id': 'SD-WAN Monitoring',
            'category': 'Network',
            'subcategory': 'SD-WAN',
            'short_description': f"SD-WAN Alert: {alarm.get('type')} on {alarm.get('host-name')}",
            'description': self.format_description(alarm),
            'impact': self.severity_map.get(severity, '3'),
            'urgency': self.severity_map.get(severity, '3'),
            'assignment_group': self.assignment_groups.get(severity, 'Network Operations'),
            'correlation_id': dedup_key,
            'correlation_display': f"SD-WAN-{alarm.get('uuid', 'unknown')[:8]}",
            'cmdb_ci': alarm.get('host-name'),
            'u_source_system': 'Cisco SD-WAN',
            'u_alarm_id': alarm.get('uuid'),
            'u_device_ip': alarm.get('system-ip')
        }

        url = f"{self.snow_url}/api/now/table/incident"
        headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}

        response = requests.post(url, auth=self.snow_auth, headers=headers, json=incident_data)

        if response.status_code == 201:
            result = response.json().get('result', {})
            return {
                'status': 'created',
                'incident_number': result.get('number'),
                'sys_id': result.get('sys_id')
            }
        else:
            return {
                'status': 'error',
                'error': response.text
            }

    def update_incident(self, sys_id, alarm):
        """Update existing incident with new alarm info"""
        update_data = {
            'work_notes': f"[{datetime.now().isoformat()}] Alarm still active\n{self.format_description(alarm)}"
        }

        url = f"{self.snow_url}/api/now/table/incident/{sys_id}"
        headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}

        response = requests.patch(url, auth=self.snow_auth, headers=headers, json=update_data)

        return {
            'status': 'updated',
            'sys_id': sys_id
        }

    def format_description(self, alarm):
        """Format alarm details for incident description"""
        return f"""
SD-WAN Alarm Details
====================
Alarm Type: {alarm.get('type')}
Severity: {alarm.get('severity')}
Device: {alarm.get('host-name')}
System IP: {alarm.get('system-ip')}
Site ID: {alarm.get('site-id')}
Component: {alarm.get('component')}
Message: {alarm.get('message')}
Timestamp: {alarm.get('entry_time')}
UUID: {alarm.get('uuid')}

Recommended Actions:
{self.get_recommended_actions(alarm.get('type'))}
"""

    def get_recommended_actions(self, alarm_type):
        """Get recommended actions based on alarm type"""
        actions = {
            'control-connection-state-change': """
1. Verify network connectivity to controllers
2. Check certificate validity
3. Review firewall rules
4. Check vBond reachability""",
            'bfd-state-change': """
1. Check physical interface status
2. Verify BFD timers
3. Check for packet loss on path
4. Review MTU settings""",
            'site-down': """
1. Verify device power status
2. Check all WAN circuits
3. Verify console access
4. Escalate to site contact if needed"""
        }
        return actions.get(alarm_type, "Review alarm details and troubleshoot accordingly")

    def close_incident(self, sys_id, resolution_notes):
        """Close incident when alarm clears"""
        update_data = {
            'state': '6',  # Resolved
            'close_code': 'Solved (Permanently)',
            'close_notes': resolution_notes
        }

        url = f"{self.snow_url}/api/now/table/incident/{sys_id}"
        headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}

        response = requests.patch(url, auth=self.snow_auth, headers=headers, json=update_data)

        return response.status_code == 200

    def sync_alarms(self):
        """Main sync function - process all active alarms"""
        alarms = self.get_active_alarms()
        results = {
            'processed': 0,
            'created': 0,
            'updated': 0,
            'errors': 0
        }

        for alarm in alarms:
            try:
                result = self.create_incident(alarm)
                results['processed'] += 1

                if result['status'] == 'created':
                    results['created'] += 1
                elif result['status'] == 'updated':
                    results['updated'] += 1
                else:
                    results['errors'] += 1

            except Exception as e:
                results['errors'] += 1
                print(f"Error processing alarm: {e}")

        return results


if __name__ == "__main__":
    integration = ServiceNowIncidentIntegration(
        snow_instance="abhavtech",
        snow_user="svc_sdwan_integration",
        snow_password="integration_password",
        vmanage_host="10.100.1.10",
        vmanage_user="admin",
        vmanage_password="admin_password"
    )

    results = integration.sync_alarms()
    print(f"Sync complete: {results}")

6.18.3 Change Management Integration

Change Record Synchronization

change_management_sync:
  sd_wan_to_servicenow:
    trigger: "Template push or policy change"

    change_types:
      template_attach:
        snow_type: "Normal"
        category: "Network"
        risk: "Medium"

      policy_modification:
        snow_type: "Normal"
        category: "Network"
        risk: "Medium-High"

      software_upgrade:
        snow_type: "Normal"
        category: "Infrastructure"
        risk: "High"

  servicenow_to_sd_wan:
    trigger: "Change approval"
    automation:
      - "Maintenance window scheduling"
      - "Notification suppression"
      - "Automated deployment trigger"

Change Integration Script

#!/usr/bin/env python3
"""
SD-WAN Change Management Integration
Syncs changes between SD-WAN and ServiceNow
"""

import requests
import json
from datetime import datetime, timedelta

class ChangeManagementIntegration:
    def __init__(self, snow_instance, snow_auth, vmanage_host, vmanage_auth):
        self.snow_url = f"https://{snow_instance}.service-now.com"
        self.snow_auth = snow_auth
        self.vmanage_url = f"https://{vmanage_host}"
        self.vmanage_session = self.setup_vmanage_session(vmanage_auth)

    def setup_vmanage_session(self, auth):
        """Setup authenticated vManage session"""
        session = requests.Session()
        session.verify = False

        auth_url = f"{self.vmanage_url}/j_security_check"
        session.post(auth_url, data={'j_username': auth[0], 'j_password': auth[1]})

        token_url = f"{self.vmanage_url}/dataservice/client/token"
        token_response = session.get(token_url)
        if token_response.status_code == 200:
            session.headers['X-XSRF-TOKEN'] = token_response.text

        return session

    def get_recent_changes(self, hours=24):
        """Get recent configuration changes from vManage"""
        url = f"{self.vmanage_url}/dataservice/device/action/status/tasks"
        params = {'hours': hours}

        response = self.vmanage_session.get(url, params=params)
        return response.json().get('data', []) if response.status_code == 200 else []

    def create_change_record(self, change_data):
        """Create ServiceNow change request"""
        change_request = {
            'type': 'normal',
            'category': 'Network',
            'short_description': f"SD-WAN: {change_data['action']} - {change_data.get('summary', '')}",
            'description': self.format_change_description(change_data),
            'assignment_group': 'Network Operations',
            'requested_by': 'SD-WAN Manager',
            'u_source_system': 'Cisco SD-WAN',
            'u_change_id': change_data.get('task_id'),
            'start_date': change_data.get('start_time'),
            'end_date': change_data.get('end_time'),
            'risk': self.assess_risk(change_data),
            'impact': self.assess_impact(change_data)
        }

        url = f"{self.snow_url}/api/now/table/change_request"
        headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}

        response = requests.post(url, auth=self.snow_auth, headers=headers, json=change_request)

        if response.status_code == 201:
            return response.json().get('result', {})
        return None

    def format_change_description(self, change_data):
        """Format change details"""
        return f"""
SD-WAN Configuration Change
===========================
Action: {change_data.get('action')}
Task ID: {change_data.get('task_id')}
Initiated By: {change_data.get('user', 'System')}
Start Time: {change_data.get('start_time')}
End Time: {change_data.get('end_time')}
Status: {change_data.get('status')}

Affected Devices:
{self.format_device_list(change_data.get('devices', []))}

Change Details:
{change_data.get('details', 'N/A')}
"""

    def format_device_list(self, devices):
        """Format list of affected devices"""
        if not devices:
            return "No devices specified"
        return "\n".join([f"- {d}" for d in devices[:20]])

    def assess_risk(self, change_data):
        """Assess change risk level"""
        action = change_data.get('action', '').lower()
        device_count = len(change_data.get('devices', []))

        if 'upgrade' in action or 'software' in action:
            return 'high'
        elif device_count > 10:
            return 'moderate'
        elif 'policy' in action:
            return 'moderate'
        else:
            return 'low'

    def assess_impact(self, change_data):
        """Assess change impact level"""
        device_count = len(change_data.get('devices', []))

        if device_count > 50:
            return '1'  # High
        elif device_count > 10:
            return '2'  # Medium
        else:
            return '3'  # Low

    def get_approved_changes(self):
        """Get approved changes from ServiceNow for scheduling"""
        url = f"{self.snow_url}/api/now/table/change_request"
        params = {
            'sysparm_query': 'state=scheduled^u_source_system=Cisco SD-WAN',
            'sysparm_fields': 'number,sys_id,short_description,start_date,end_date,u_change_id'
        }

        response = requests.get(url, auth=self.snow_auth, params=params)
        return response.json().get('result', []) if response.status_code == 200 else []

    def sync_change_status(self, snow_change_id, vmanage_task_id):
        """Sync change status between systems"""
        # Get vManage task status
        url = f"{self.vmanage_url}/dataservice/device/action/status/{vmanage_task_id}"
        response = self.vmanage_session.get(url)

        if response.status_code == 200:
            task_status = response.json().get('data', {})

            # Map vManage status to ServiceNow state
            state_map = {
                'in_progress': 'implement',
                'done': 'review',
                'failure': 'implement',
                'scheduled': 'scheduled'
            }

            snow_state = state_map.get(task_status.get('status'), 'implement')

            # Update ServiceNow
            update_url = f"{self.snow_url}/api/now/table/change_request/{snow_change_id}"
            update_data = {
                'state': snow_state,
                'work_notes': f"Status update from SD-WAN: {task_status.get('status')}"
            }

            requests.patch(update_url, auth=self.snow_auth, json=update_data)


if __name__ == "__main__":
    integration = ChangeManagementIntegration(
        snow_instance="abhavtech",
        snow_auth=("svc_sdwan_integration", "password"),
        vmanage_host="10.100.1.10",
        vmanage_auth=("admin", "admin_password")
    )

    # Sync recent changes
    changes = integration.get_recent_changes(hours=24)
    for change in changes:
        result = integration.create_change_record(change)
        if result:
            print(f"Created change: {result.get('number')}")

6.18.4 CMDB Integration

Configuration Item Sync

cmdb_integration:
  ci_types:
    wan_edge_router:
      snow_class: "cmdb_ci_ip_router"
      attributes:
        - name: "name"
          source: "host-name"
        - name: "ip_address"
          source: "system-ip"
        - name: "serial_number"
          source: "board-serial"
        - name: "model_id"
          source: "device-model"
        - name: "os_version"
          source: "version"
        - name: "location"
          source: "site-id"
        - name: "u_device_type"
          value: "SD-WAN Edge"

    controller:
      snow_class: "cmdb_ci_server"
      attributes:
        - name: "name"
          source: "host-name"
        - name: "ip_address"
          source: "deviceIP"
        - name: "u_device_type"
          source: "device-type"

    circuit:
      snow_class: "cmdb_ci_circuit"
      attributes:
        - name: "name"
          source: "interface-name"
        - name: "bandwidth"
          source: "speed"
        - name: "u_transport"
          source: "color"

  sync_schedule:
    full_sync: "Daily 02:00 IST"
    incremental_sync: "Every 15 minutes"

  relationship_mapping:
    - parent: "wan_edge_router"
      child: "circuit"
      type: "Connects to::Connected by"

CMDB Sync Script

#!/usr/bin/env python3
"""
SD-WAN CMDB Integration
Synchronizes SD-WAN inventory with ServiceNow CMDB
"""

import requests
import json
from datetime import datetime

class CMDBIntegration:
    def __init__(self, snow_instance, snow_auth, vmanage_host, vmanage_auth):
        self.snow_url = f"https://{snow_instance}.service-now.com"
        self.snow_auth = snow_auth
        self.vmanage_url = f"https://{vmanage_host}"
        self.vmanage_session = self.setup_vmanage_session(vmanage_auth)

    def setup_vmanage_session(self, auth):
        """Setup authenticated vManage session"""
        session = requests.Session()
        session.verify = False

        auth_url = f"{self.vmanage_url}/j_security_check"
        session.post(auth_url, data={'j_username': auth[0], 'j_password': auth[1]})

        token_url = f"{self.vmanage_url}/dataservice/client/token"
        token_response = session.get(token_url)
        if token_response.status_code == 200:
            session.headers['X-XSRF-TOKEN'] = token_response.text

        return session

    def get_sdwan_inventory(self):
        """Get complete SD-WAN device inventory"""
        url = f"{self.vmanage_url}/dataservice/device"
        response = self.vmanage_session.get(url)
        return response.json().get('data', []) if response.status_code == 200 else []

    def get_device_details(self, device_id):
        """Get detailed device information"""
        url = f"{self.vmanage_url}/dataservice/device/system/status?deviceId={device_id}"
        response = self.vmanage_session.get(url)
        return response.json().get('data', [{}])[0] if response.status_code == 200 else {}

    def find_existing_ci(self, serial_number):
        """Find existing CI in CMDB"""
        url = f"{self.snow_url}/api/now/table/cmdb_ci_ip_router"
        params = {
            'sysparm_query': f"serial_number={serial_number}",
            'sysparm_limit': 1
        }

        response = requests.get(url, auth=self.snow_auth, params=params)
        if response.status_code == 200:
            results = response.json().get('result', [])
            return results[0] if results else None
        return None

    def create_or_update_ci(self, device):
        """Create or update CI in CMDB"""
        serial = device.get('board-serial', device.get('uuid'))
        existing = self.find_existing_ci(serial)

        ci_data = {
            'name': device.get('host-name'),
            'ip_address': device.get('system-ip'),
            'serial_number': serial,
            'model_id': device.get('device-model'),
            'os_version': device.get('version'),
            'location': device.get('site-id'),
            'u_device_type': 'SD-WAN Edge',
            'u_reachability': device.get('reachability'),
            'u_last_sync': datetime.now().isoformat(),
            'operational_status': '1' if device.get('reachability') == 'reachable' else '2',
            'manufacturer': 'Cisco',
            'u_system_ip': device.get('system-ip'),
            'u_site_id': device.get('site-id')
        }

        headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}

        if existing:
            # Update existing CI
            url = f"{self.snow_url}/api/now/table/cmdb_ci_ip_router/{existing['sys_id']}"
            response = requests.patch(url, auth=self.snow_auth, headers=headers, json=ci_data)
            action = 'updated'
        else:
            # Create new CI
            url = f"{self.snow_url}/api/now/table/cmdb_ci_ip_router"
            response = requests.post(url, auth=self.snow_auth, headers=headers, json=ci_data)
            action = 'created'

        if response.status_code in [200, 201]:
            return {
                'status': 'success',
                'action': action,
                'ci': response.json().get('result', {})
            }
        else:
            return {
                'status': 'error',
                'error': response.text
            }

    def sync_all_devices(self):
        """Full CMDB sync of all devices"""
        devices = self.get_sdwan_inventory()

        results = {
            'total': len(devices),
            'created': 0,
            'updated': 0,
            'errors': 0,
            'timestamp': datetime.now().isoformat()
        }

        for device in devices:
            try:
                result = self.create_or_update_ci(device)

                if result['status'] == 'success':
                    if result['action'] == 'created':
                        results['created'] += 1
                    else:
                        results['updated'] += 1
                else:
                    results['errors'] += 1

            except Exception as e:
                results['errors'] += 1
                print(f"Error syncing {device.get('host-name')}: {e}")

        return results

    def create_relationships(self):
        """Create CI relationships"""
        # Get all SD-WAN CIs
        url = f"{self.snow_url}/api/now/table/cmdb_ci_ip_router"
        params = {'sysparm_query': 'u_device_type=SD-WAN Edge'}

        response = requests.get(url, auth=self.snow_auth, params=params)
        cis = response.json().get('result', []) if response.status_code == 200 else []

        # Create relationships between hub and spoke
        # This would require additional logic based on your topology
        pass


if __name__ == "__main__":
    integration = CMDBIntegration(
        snow_instance="abhavtech",
        snow_auth=("svc_sdwan_integration", "password"),
        vmanage_host="10.100.1.10",
        vmanage_auth=("admin", "admin_password")
    )

    results = integration.sync_all_devices()
    print(f"CMDB Sync Results: {json.dumps(results, indent=2)}")

6.18.5 Service Catalog Integration

Self-Service Requests

service_catalog_items:
  new_site_provisioning:
    name: "SD-WAN New Site Request"
    category: "Network Services"
    workflow:
      1_request: "User submits request with site details"
      2_approval: "Manager and Network team approval"
      3_provisioning: "Automated device provisioning"
      4_validation: "Connectivity verification"
      5_completion: "Handoff to user"
    variables:
      - name: "site_name"
        type: "string"
        mandatory: true
      - name: "site_type"
        type: "choice"
        choices: ["Hub", "Branch", "Remote"]
      - name: "bandwidth_required"
        type: "choice"
        choices: ["100 Mbps", "200 Mbps", "500 Mbps", "1 Gbps"]
      - name: "primary_transport"
        type: "choice"
        choices: ["MPLS", "Internet", "5G/LTE"]

  bandwidth_change:
    name: "SD-WAN Bandwidth Change"
    category: "Network Services"
    workflow:
      1_request: "User requests bandwidth change"
      2_assessment: "Capacity assessment"
      3_approval: "Approval based on cost"
      4_implementation: "Circuit modification"
      5_verification: "Performance validation"

  vpn_access_request:
    name: "SD-WAN VPN Access"
    category: "Network Services"
    workflow:
      1_request: "User requests VPN access"
      2_security_review: "Security team review"
      3_approval: "Manager approval"
      4_configuration: "Policy configuration"
      5_testing: "Access verification"

6.18.6 Event Management Integration

Event Correlation Rules

event_management:
  event_sources:
    sd_wan_alarms:
      connector: "REST API"
      poll_interval: "60 seconds"

  correlation_rules:
    site_outage:
      conditions:
        - "Multiple device unreachable alarms from same site"
        - "Within 5 minute window"
      action: "Create single P1 incident"

    controller_cluster_issue:
      conditions:
        - "Multiple controller alarms"
        - "Same cluster"
      action: "Create P1 incident with escalation"

    circuit_degradation:
      conditions:
        - "BFD state changes"
        - "Loss/latency threshold alarms"
      action: "Create P3 incident, correlate with provider"

  alert_suppression:
    maintenance_window:
      source: "Change calendar"
      action: "Suppress non-critical alerts"

    known_issues:
      source: "Problem records"
      action: "Link to existing problem"

6.18.7 Reporting Integration

ITSM Reports

itsm_reporting:
  operational_reports:
    daily_summary:
      metrics:
        - "Incidents created"
        - "Incidents resolved"
        - "MTTR by severity"
        - "Open incidents by priority"
      distribution: "NOC Team"

    weekly_report:
      metrics:
        - "SLA achievement"
        - "Change success rate"
        - "CMDB accuracy"
        - "Top incident categories"
      distribution: "IT Management"

    monthly_executive:
      metrics:
        - "Network availability"
        - "Incident trends"
        - "Change metrics"
        - "Cost analysis"
      distribution: "IT Leadership"

  dashboard_widgets:
    real_time:
      - "Open SD-WAN incidents"
      - "Active alarms count"
      - "Device health status"

    trending:
      - "Incidents over time"
      - "MTTR trend"
      - "Top affected sites"

Document version: 1.0 Last updated: 2025 Classification: Internal Use