关于 change-management
面向 ServiceNow 变更管理的技能,涵盖变更请求、审批、任务和状态流转,并提供相应的 MCP 工具流程参考。适合需要规范基础设施变更操作的团队。
主要功能
- 01区分标准、普通和紧急变更
- 02创建变更请求与任务
- 03关联受影响的 CI
- 04检查审批与排期冲突
- 05管理变更状态流转
适用场景
- 01创建服务器补丁变更计划
- 02为变更添加实施任务与 CI
- 03跟踪审批并推进变更状态
MCP 相关
文档列出用于创建、查询、更新变更及安排 CAB 的 MCP 工具,并示范变更工作流。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill change-management安装后在 Claude Code 中输入/change-management确认已加载。
详细介绍
ServiceNow 的变更管理
变更管理通过标准化流程和审批,确保对 IT 基础设施进行受控修改。
变更类型
| 类型 | 审批 | 风险 | 示例 |
|---|---|---|---|
| 标准 | 预先批准 | 低 | 密码重置模板 |
| 普通 | CAB 审批 | 中等 | 服务器修补 |
| 紧急 | 实施后审批 | 高 | 生产中断修复 |
变更请求结构
Change Request: CHG0010001
├── Risk Assessment
│ ├── Impact: Medium
│ ├── Risk: Low
│ └── Conflict Status: Clear
├── Schedule
│ ├── Planned Start: 2024-03-15 22:00
│ └── Planned End: 2024-03-15 23:00
├── Approvals
│ ├── Manager Approval: Approved
│ └── CAB Approval: Pending
├── Change Tasks
│ ├── Task 1: Backup current config
│ ├── Task 2: Apply patches
│ └── Task 3: Verify functionality
└── Affected CIs
├── PROD-WEB-001
└── PROD-DB-001
创建变更
普通变更(ES5)
// Create normal change request
var change = new GlideRecord("change_request")
change.initialize()
// Basic info
change.setValue("short_description", "Apply security patches to web servers")
change.setValue("description", "Monthly security patch deployment for production web tier")
change.setValue("type", "normal") // normal, standard, emergency
// Classification
change.setValue("category", "Software")
change.setValue("priority", 3)
// Risk assessment
change.setValue("risk", "low") // high, moderate, low
change.setValue("impact", 2) // 1-High, 2-Medium, 3-Low
// Schedule
var startDate = new GlideDateTime()
startDate.addDaysLocalTime(7)
startDate.setValue("2024-03-15 22:00:00")
change.setValue("start_date", startDate)
var endDate = new GlideDateTime(startDate)
endDate.addSeconds(3600) // 1 hour
change.setValue("end_date", endDate)
// Assignment
change.setValue("assignment_group", getGroupSysId("Change Management"))
change.setValue("assigned_to", getOnCallUser())
// Implementation plan
change.setValue(
"implementation_plan",
"1. Take backup of current configuration\\n" +
"2. Stop web services\\n" +
"3. Apply security patches\\n" +
"4. Restart services\\n" +
"5. Verify functionality",
)
change.setValue(
"backout_plan",
"1. Stop web services\\n" + "2. Restore from backup\\n" + "3. Restart services\\n" + "4. Verify rollback successful",
)
change.setValue(
"test_plan",
"1. Check web server response\\n" + "2. Verify all pages load\\n" + "3. Run automated health checks",
)
var changeSysId = change.insert()
紧急变更(ES5)
// Create emergency change
var emergencyChange = new GlideRecord("change_request")
emergencyChange.initialize()
emergencyChange.setValue("short_description", "Emergency: Fix production database connection leak")
emergencyChange.setValue("type", "emergency")
emergencyChange.setValue("priority", 1)
emergencyChange.setValue("risk", "high")
emergencyChange.setValue("impact", 1)
// Emergency justification
emergencyChange.setValue(
"justification",
"Production database connections exhausted causing service outage. " + "Immediate fix required to restore service.",
)
// Immediate start
emergencyChange.setValue("start_date", new GlideDateTime())
var endDate = new GlideDateTime()
endDate.addSeconds(7200) // 2 hours
emergencyChange.setValue("end_date", endDate)
emergencyChange.insert()
// Emergency changes bypass normal CAB
gs.eventQueue("change.emergency.created", emergencyChange)
标准变更(ES5)
// Create from standard change template
function createStandardChange(templateName, variables) {
// Find template
var template = new GlideRecord("std_change_producer")
template.addQuery("name", templateName)
template.query()
if (!template.next()) {
gs.error("Standard change template not found: " + templateName)
return null
}
// Create change from template
var change = new GlideRecord("change_request")
change.initialize()
// Copy template values
change.setValue("short_description", template.getValue("short_description"))
change.setValue("description", template.getValue("description"))
change.setValue("type", "standard")
change.setValue("std_change_producer", template.getUniqueValue())
// Apply variables
for (var key in variables) {
if (variables.hasOwnProperty(key) && change.isValidField(key)) {
change.setValue(key, variables[key])
}
}
return change.insert()
}
// Usage
createStandardChange("Password Reset", {
requested_by: userSysId,
cmdb_ci: applicationSysId,
})
变更任务
创建变更任务(ES5)
// Add tasks to change
function addChangeTask(changeSysId, taskConfig) {
var task = new GlideRecord("change_task")
task.initialize()
task.setValue("change_request", changeSysId)
task.setValue("short_description", taskConfig.description)
task.setValue("order", taskConfig.order)
task.setValue("assignment_group", taskConfig.group)
task.setValue("planned_start_date", taskConfig.start)
task.setValue("planned_end_date", taskConfig.end)
return task.insert()
}
// Create implementation tasks
var tasks = [
{ description: "Pre-implementation backup", order: 100, group: "Database Team" },
{ description: "Apply patches", order: 200, group: "Server Team" },
{ description: "Verify functionality", order: 300, group: "QA Team" },
{ description: "Update documentation", order: 400, group: "Change Management" },
]
for (var i = 0; i < tasks.length; i++) {
addChangeTask(changeSysId, tasks[i])
}
受影响的 CI
将 CI 关联到变更(ES5)
// Add affected CIs
function addAffectedCI(changeSysId, ciSysId) {
var task = new GlideRecord("task_ci")
task.initialize()
task.setValue("task", changeSysId)
task.setValue("ci_item", ciSysId)
return task.insert()
}
// Add all affected servers
var servers = ["server1_sys_id", "server2_sys_id", "db_sys_id"]
for (var i = 0; i < servers.length; i++) {
addAffectedCI(changeSysId, servers[i])
}
审批
检查审批状态(ES5)
// Get approval status
function getApprovalStatus(changeSysId) {
var approvals = []
var approval = new GlideRecord("sysapproval_approver")
approval.addQuery("sysapproval", changeSysId)
approval.query()
while (approval.next()) {
approvals.push({
approver: approval.approver.getDisplayValue(),
state: approval.state.getDisplayValue(),
comments: approval.getValue("comments"),
sys_updated_on: approval.getValue("sys_updated_on"),
})
}
return approvals
}
请求审批(ES5)
// Add approval request
function requestApproval(changeSysId, approverSysId) {
var approval = new GlideRecord("sysapproval_approver")
approval.initialize()
approval.setValue("sysapproval", changeSysId)
approval.setValue("approver", approverSysId)
approval.setValue("state", "requested")
return approval.insert()
}
变更冲突检测
检查冲突(ES5)
// Check for scheduling conflicts
function checkChangeConflicts(changeSysId) {
var change = new GlideRecord("change_request")
if (!change.get(changeSysId)) return []
var conflicts = []
var startDate = change.getValue("start_date")
var endDate = change.getValue("end_date")
// Find overlapping changes on same CIs
var affected = new GlideAggregate("task_ci")
affected.addQuery("task", changeSysId)
affected.groupBy("ci_item")
affected.query()
while (affected.next()) {
var ciSysId = affected.ci_item.toString()
// Find other changes affecting this CI
var otherChanges = new GlideRecord("change_request")
otherChanges.addQuery("sys_id", "!=", changeSysId)
otherChanges.addQuery("state", "NOT IN", "closed,cancelled")
otherChanges.addQuery("start_date", "<=", endDate)
otherChanges.addQuery("end_date", ">=", startDate)
// Join to task_ci
otherChanges.addJoinQuery("task_ci", "sys_id", "task").addCondition("ci_item", ciSysId)
otherChanges.query()
while (otherChanges.next()) {
conflicts.push({
change: otherChanges.getValue("number"),
ci: ciSysId,
start: otherChanges.getValue("start_date"),
end: otherChanges.getValue("end_date"),
})
}
}
return conflicts
}
状态转换
变更状态
| 状态 | 后续状态 | 条件 |
|---|---|---|
| 新建 | 评估 | 已提交 |
| 评估 | 授权、已取消 | 评估完成 |
| 授权 | 已安排、已取消 | 审批完成 |
| 已安排 | 实施、已取消 | 在维护窗口内 |
| 实施 | 审查、已取消 | 任务已完成 |
| 审查 | 已关闭 | PIR 已完成 |
| 已关闭 | - | 最终状态 |
转换变更(ES5)
// Move change to next state
function transitionChange(changeSysId, newState, notes) {
var change = new GlideRecord("change_request")
if (!change.get(changeSysId)) {
gs.error("Change not found: " + changeSysId)
return false
}
// Validate transition
var currentState = change.getValue("state")
var validTransitions = {
"-5": ["-4", "4"], // New -> Assess or Cancelled
"-4": ["-3", "4"], // Assess -> Authorize or Cancelled
"-3": ["-2", "4"], // Authorize -> Scheduled or Cancelled
"-2": ["-1", "4"], // Scheduled -> Implement or Cancelled
"-1": ["0", "4"], // Implement -> Review or Cancelled
0: ["3"], // Review -> Closed
}
if (!validTransitions[currentState] || validTransitions[currentState].indexOf(newState) === -1) {
gs.error("Invalid state transition: " + currentState + " -> " + newState)
return false
}
change.setValue("state", newState)
if (notes) {
change.work_notes = notes
}
change.update()
return true
}
MCP 工具集成
可用的变更工具
| 工具 | 用途 |
|---|---|
snow_change_manage (action='create') | 创建变更 |
snow_change_manage (action='create_task') | 添加任务 |
snow_change_query (action='get') | 获取详情 |
snow_change_manage (action='update_state') | 转换状态 |
snow_change_query (action='search') | 查找变更 |
snow_change_manage (action='schedule_cab') | 安排 CAB |
示例工作流
// 1. Create change (scheduling happens later via action: "update_state" -> "scheduled")
var changeId = await snow_change_manage({
action: "create",
short_description: "Database upgrade",
type: "normal",
risk: "medium",
})
// 2. Add tasks
await snow_change_manage({
action: "create_task",
sys_id: changeId,
short_description: "Backup database",
})
// 3. Check conflicts: search for overlapping open changes in the same window
// (see checkChangeConflicts above for per-CI conflict detection)
var conflicts = await snow_change_query({
action: "search",
query: "stateNOT INclosed,cancelled^start_date<=2024-03-21 02:00:00^end_date>=2024-03-20 22:00:00",
})
// 4. Submit for approval
await snow_change_manage({
action: "update_state",
sys_id: changeId,
state: "assess",
})
最佳实践
- 完整文档 - 实施、回退和测试计划
- 风险评估 - 准确评估风险和影响
- CI 关联 - 附加所有受影响的 CI
- 合理安排时间 - 在维护窗口内进行
- 任务拆分 - 明确实施步骤
- 审批流程 - 完成所有必需的审批
- 冲突检查 - 确认不存在重叠的变更
- PIR - 实施后审查,总结经验教训
评论
支持 Markdown