servicenow-change-workflow

$npx mdskill add automateyournetwork/netclaw/servicenow-change-workflow

Manages end-to-end ITSM-gated network change workflows with audit trails

  • Tracks change requests from creation to closure following ITIL processes
  • Integrates with ServiceNow, pyats-config-mgmt, and GAIT for execution and auditing
  • Enforces approval gates and validates incidents before and after changes
  • Delivers status updates and audit trails through structured workflow states
SKILL.md
.github/skills/servicenow-change-workflowView on GitHub ↗
---
name: servicenow-change-workflow
description: "Full ITSM-gated change lifecycle - CR creation, pre-change incident validation, approval gate, execution via pyats-config-mgmt, post-change verification, and closure with GAIT audit trail. Use when creating a change request, making a network change that needs approval, tracking change management, or following ITIL change process."
license: Apache-2.0
user-invocable: true
metadata:
  { "openclaw": { "requires": { "bins": ["python3"], "env": ["SERVICENOW_MCP_SCRIPT"] } } }
---

# ServiceNow Change Workflow

## Golden Rule

**NEVER execute a network change without an approved Change Request.** The only exception is an Emergency change, which still requires a CR and immediate human notification.

## How to Call the Tools

The ServiceNow MCP server provides change management and incident tools. Call them via mcp-call:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" TOOL_NAME '{"param":"value"}'
```

## ServiceNow Change Request States

```
New -> Assess -> Authorize -> Scheduled -> Implement -> Review -> Closed
                                                    \-> Canceled
```

- **New**: CR created, not yet submitted
- **Assess**: Submitted for review / risk assessment
- **Authorize**: Awaiting approval
- **Scheduled**: Approved and scheduled for implementation window
- **Implement**: Approved and ready for execution (this is the gate)
- **Review**: Post-implementation review
- **Closed**: Change completed and verified

## Change Types

| Type | Approval | Use When |
|------|----------|----------|
| Normal | CAB / manager approval required | Interface config, routing changes, ACL updates, any production change |
| Standard | Pre-approved template | Password rotation, NTP update, banner change |
| Emergency | Expedited (post-facto approval OK) | Active outage, security incident, critical vulnerability |

---

## Full Change Lifecycle

### Phase 0: Pre-Change Incident Check

Before creating a CR, verify no open P1/P2 incidents exist on affected CIs. Executing changes during active incidents violates ITIL best practice and risks compounding the outage.

#### 0A: Check for Open Critical Incidents

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" list_incidents '{"limit":20,"state":"1","query":"priority=1^ORpriority=2"}'
```

State `1` = New/Open. Review results for any incidents affecting the target device or service.

**Decision gate:**
- Open P1/P2 on affected CI -> STOP. Do not proceed. Notify the human.
- Open P1/P2 on unrelated CI -> Proceed with caution, note in CR description.
- No open P1/P2 -> Proceed.

#### 0B: Check for Active Change Freezes

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" list_change_requests '{"limit":10,"state":"implement","type":"normal"}'
```

Review active in-progress changes for conflicts. Two changes on the same device at the same time is a collision.

### Phase 1: Create Change Request

#### 1A: Create the CR

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" create_change_request '{"short_description":"Add Loopback99 to R1 for OSPF RID migration","description":"Add Loopback99 (99.99.99.99/32) to R1 as new OSPF router-id. Pre-change baseline captured. Rollback plan: no interface Loopback99. Affected CI: R1 (devnetsandboxiosxec8k.cisco.com). No open P1/P2 incidents on this CI.","type":"normal","category":"Network","risk":"moderate","impact":"3","assignment_group":"Network Engineering"}'
```

**CR description must include:**
1. What is being changed and why
2. Affected CI (device hostname, management IP)
3. Pre-change incident status (from Phase 0)
4. Rollback plan
5. Expected impact and duration
6. Verification criteria

#### 1B: Add Change Tasks

Break the change into discrete tasks for tracking:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" add_change_task '{"change_id":"CHG0000123","short_description":"Capture pre-change baseline on R1","description":"Save running config, OSPF neighbors, routing table, interface states, and connectivity baselines."}'
```

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" add_change_task '{"change_id":"CHG0000123","short_description":"Apply Loopback99 configuration on R1","description":"Apply: interface Loopback99, ip address 99.99.99.99 255.255.255.255, description OSPF-RID-Migration, no shutdown"}'
```

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" add_change_task '{"change_id":"CHG0000123","short_description":"Post-change verification on R1","description":"Verify Loopback99 up/up, OSPF neighbors stable, routing table +1 connected route, connectivity 100%."}'
```

#### 1C: Record CR Creation in GAIT

```bash
python3 $MCP_CALL "python3 -u $GAIT_MCP_SCRIPT" gait_record_turn '{"user_text":"Create change request for Loopback99 addition on R1","assistant_text":"Created CR CHG0000123: Add Loopback99 to R1 for OSPF RID migration. Type: normal, Risk: moderate, Impact: 3. 3 change tasks added. Pre-change incident check: CLEAR (no P1/P2 on R1)."}'
```

### Phase 2: Approval Gate

#### 2A: Submit for Approval

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" submit_change_for_approval '{"change_id":"CHG0000123","approval_comments":"Pre-change checks complete. No open P1/P2 incidents. Rollback plan documented. Ready for CAB review."}'
```

#### 2B: Wait for Approval

Poll the CR status until it reaches Implement state:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" get_change_request_details '{"change_id":"CHG0000123"}'
```

**Decision gate:**
- State = `implement` -> Proceed to Phase 3
- State = `authorize` -> Still awaiting approval. Wait.
- State = `canceled` -> CR was rejected. STOP. Notify the human with rejection reason.

**CRITICAL: Do NOT proceed to Phase 3 unless the CR state is `implement` (approved).**

#### 2C: Approve Change (when authorized to do so)

If the human operator has approval authority:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" approve_change '{"change_id":"CHG0000123","approval_comments":"Approved. Rollback plan verified. Maintenance window confirmed."}'
```

### Phase 3: Execution

This phase uses the **pyats-config-mgmt** skill for the actual device work. The ServiceNow CR gates entry to this phase.

#### 3A: Update CR to Implementation

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" update_change_request '{"change_id":"CHG0000123","work_notes":"Beginning implementation. Capturing pre-change baseline."}'
```

#### 3B: Pre-Change Baseline (via pyats-config-mgmt)

Follow the pyats-config-mgmt skill Phase 1 procedure:

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_show_running_config '{"device_name":"R1"}'
```

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip ospf neighbor"}'
```

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route"}'
```

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_ping_from_network_device '{"device_name":"R1","command":"ping 8.8.8.8 repeat 10"}'
```

Update the CR with baseline status:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" update_change_request '{"change_id":"CHG0000123","work_notes":"Pre-change baseline captured: Running config saved, 2 OSPF neighbors FULL, 47 routes, 100% connectivity to 8.8.8.8 (23ms RTT)."}'
```

#### 3C: Apply Configuration (via pyats-config-mgmt)

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_configure_device '{"device_name":"R1","config_commands":["interface Loopback99","ip address 99.99.99.99 255.255.255.255","description OSPF-RID-Migration","no shutdown"]}'
```

Update the CR:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" update_change_request '{"change_id":"CHG0000123","work_notes":"Configuration applied to R1. Proceeding to post-change verification."}'
```

### Phase 4: Post-Change Verification

#### 4A: Verify the Change (via pyats-config-mgmt)

Follow the pyats-config-mgmt skill Phase 4 procedure:

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_show_running_config '{"device_name":"R1"}'
```

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip ospf neighbor"}'
```

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route"}'
```

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_ping_from_network_device '{"device_name":"R1","command":"ping 8.8.8.8 repeat 10"}'
```

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_show_logging '{"device_name":"R1"}'
```

#### 4B: Verification Decision

**Compare pre-change vs post-change state:**

| Check | Pre-Change | Post-Change | Expected Delta |
|-------|-----------|-------------|----------------|
| OSPF neighbors | 2 FULL | 2 FULL | No change |
| Route count | 47 | 48 | +1 connected (99.99.99.99/32) |
| Connectivity | 100% | 100% | No change |
| New errors in log | - | %LINEPROTO-5-UPDOWN Lo99 up | Expected |

**Decision gate:**
- All checks PASS -> Proceed to Phase 5 (Close)
- Any check FAILS -> Initiate Rollback (Phase 4C), do NOT close the CR

#### 4C: Rollback (If Verification Fails)

If post-change verification fails, roll back immediately:

```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_configure_device '{"device_name":"R1","config_commands":["no interface Loopback99"]}'
```

Update the CR with rollback details:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" update_change_request '{"change_id":"CHG0000123","work_notes":"ROLLBACK EXECUTED: Post-change verification failed. [Describe what failed]. Configuration rolled back. Device restored to baseline state. Verification of rollback: [results]."}'
```

Create an incident for the failed change:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" create_incident '{"short_description":"Failed change CHG0000123 on R1 - rollback executed","description":"Change CHG0000123 (Add Loopback99 to R1) failed post-change verification. Rollback executed successfully. Device restored to pre-change state. Root cause investigation required.","urgency":"2","impact":"2","category":"Network"}'
```

**CRITICAL: If rollback fails, this becomes a P1 incident. Notify the human IMMEDIATELY.**

### Phase 5: Close the Change Request

#### 5A: Update CR with Final Status

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" update_change_request '{"change_id":"CHG0000123","work_notes":"Post-change verification PASSED. OSPF neighbors stable (2 FULL), route table +1 connected (99.99.99.99/32), connectivity 100%, no unexpected log entries. Change successful.","state":"review"}'
```

#### 5B: Close the CR

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" update_change_request '{"change_id":"CHG0000123","state":"closed","work_notes":"Change completed successfully. All verification checks passed. No rollback required."}'
```

#### 5C: Record Full Session in GAIT

```bash
python3 $MCP_CALL "python3 -u $GAIT_MCP_SCRIPT" gait_record_turn '{"user_text":"Execute change CHG0000123: Add Loopback99 to R1","assistant_text":"Change CHG0000123 completed successfully.\n\nTimeline:\n- Pre-check: No open P1/P2 incidents on R1\n- CR created and approved\n- Baseline: 2 OSPF neighbors FULL, 47 routes, 100% connectivity\n- Config applied: interface Loopback99, 99.99.99.99/32\n- Verification: PASSED (2 OSPF FULL, 48 routes, 100% connectivity)\n- CR closed: successful\n\nArtifacts: Pre/post running config diff, routing table diff, log entries."}'
```

#### 5D: Display GAIT Audit Trail

```bash
python3 $MCP_CALL "python3 -u $GAIT_MCP_SCRIPT" gait_show '{"commit":"HEAD"}'
```

---

## Emergency Change Process

Emergency changes bypass the normal approval gate but still require a CR and immediate human notification.

### When to Use Emergency Changes

- Active outage requiring immediate remediation
- Security incident requiring immediate mitigation (e.g., ACL to block attack)
- Critical vulnerability with active exploitation

### Emergency Workflow

#### E1: Create Emergency CR

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" create_change_request '{"short_description":"EMERGENCY: Block attack source 203.0.113.99 on R1","description":"EMERGENCY CHANGE: Active DDoS from 203.0.113.99 detected. Applying inbound ACL on GigabitEthernet1 to block source. Post-facto approval required.","type":"emergency","category":"Network","risk":"high","impact":"1","assignment_group":"Network Engineering"}'
```

#### E2: Notify Human Immediately

**CRITICAL: For emergency changes, you MUST notify the human operator before executing.** State:
- The emergency condition
- The proposed remediation
- The CR number
- Request verbal/written approval to proceed

#### E3: Execute Upon Human Approval

Follow Phase 3 (Execution) with accelerated timelines. Baseline capture is still mandatory.

#### E4: Post-Facto Approval

After execution, submit the emergency CR for post-facto approval:

```bash
python3 $MCP_CALL "python3 -u $SERVICENOW_MCP_SCRIPT" submit_change_for_approval '{"change_id":"CHG0000456","approval_comments":"EMERGENCY CHANGE - Post-facto approval requested. Change executed during active incident. Full audit trail in GAIT."}'
```

---

## Integration with Other Skills

| Skill | Integration Point |
|-------|------------------|
| **pyats-config-mgmt** | Phase 3 (Execution) and Phase 4 (Verification) - actual device configuration |
| **pyats-health-check** | Pre-change device health validation before entering Phase 3 |
| **netbox-reconcile** | Post-change: verify NetBox source of truth reflects the change (or ticket the delta) |
| **GAIT** | Every phase records to the audit trail - CR creation, approval, execution, verification, closure |
| **markmap-viz** | Generate change summary mind map for complex multi-device changes |

## Change Report Format

After every change, produce this summary:

```
Change Report - CHG0000123
Date: YYYY-MM-DD HH:MM UTC
Device: R1 (devnetsandboxiosxec8k.cisco.com)
Type: Normal | Risk: Moderate | Impact: 3

Pre-Check: No open P1/P2 incidents on R1
Approval: Approved by [approver] at HH:MM UTC

Pre-Change State:
  - OSPF neighbors: 2 (FULL)
  - Routes: 47
  - Connectivity: 100% to 8.8.8.8 (23ms)

Config Applied:
  interface Loopback99
   ip address 99.99.99.99 255.255.255.255
   description OSPF-RID-Migration
   no shutdown

Post-Change State:
  - OSPF neighbors: 2 (FULL) - no change
  - Routes: 48 (+1 connected 99.99.99.99/32)
  - Connectivity: 100% to 8.8.8.8 (23ms) - no change
  - Log: %LINEPROTO-5-UPDOWN Lo99 up/up (expected)

Verification: PASSED
Rollback Required: No
CR Status: Closed (successful)
GAIT Commit: [commit hash]
```
More from automateyournetwork/netclaw