cmdb-patterns について
ServiceNow の CMDB 管理を扱うスキルです。CI のクラス、関係、影響分析、データ品質の確認を説明し、重複を避けてデータソースを記録できる IRE の利用を重視します。
主な機能
- 01CI クラスと主要テーブルの説明
- 02CI の作成と関係の管理
- 03上流・下流への影響分析
- 04孤立 CI と古い CI の検出
- 05IRE による CI の識別と照合
活用シーン
- 01検出したサーバーとアプリの関係を登録
- 02CI 障害が影響するビジネスサービスの評価
- 03CMDB 内の孤立 CI や古い CI の確認
MCP 対応
CI の検索・取得、作成、関係の設定、影響分析、Discovery の起動に使う MCP ツールを記載しています。重複を避け、データソースを記録するため、IRE を使う snow_cmdb_identify_reconcile を推奨しています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ serac-labs/ serac --skill cmdb-patternsインストール後、Claude Code で/cmdb-patternsを入力して読み込まれたか確認します。
詳細
ServiceNow の CMDB パターン
構成管理データベース(CMDB)は ServiceNow ITSM の基盤であり、すべての構成アイテム(CI)とその関係を追跡します。
CMDB アーキテクチャ
CI クラス階層
cmdb (Base)
└── cmdb_ci (Configuration Item)
├── cmdb_ci_computer
│ ├── cmdb_ci_server
│ │ ├── cmdb_ci_linux_server
│ │ ├── cmdb_ci_win_server
│ │ └── cmdb_ci_unix_server
│ └── cmdb_ci_pc_hardware
├── cmdb_ci_service
│ ├── cmdb_ci_service_business
│ ├── cmdb_ci_service_technical
│ └── cmdb_ci_service_auto
│ └── cmdb_ci_service_discovered
├── cmdb_ci_appl
│ ├── cmdb_ci_app_server
│ └── cmdb_ci_db_instance
└── cmdb_ci_network_gear
├── cmdb_ci_netgear
└── cmdb_ci_lb
主な CI テーブル
| テーブル | 用途 | 主なフィールド |
|---|---|---|
cmdb_ci | 基本 CI テーブル | name, sys_class_name, operational_status |
cmdb_ci_server | サーバー | ip_address, os, cpu_count, ram |
cmdb_ci_service | サービス(基本クラス — ビジネスサービスは cmdb_ci_service_business) | service_classification, busines_criticality |
cmdb_ci_appl | アプリケーション | version, install_directory |
cmdb_rel_ci | CI 関係 | parent, child, type |
構成アイテムの作成
基本的な CI の作成(ES5)
// Create a new server CI
var ci = new GlideRecord("cmdb_ci_server")
ci.initialize()
ci.setValue("name", "PROD-WEB-001")
ci.setValue("ip_address", "10.0.1.100")
ci.setValue("os", "Linux Red Hat")
ci.setValue("os_version", "8.5")
ci.setValue("cpu_count", 8)
ci.setValue("ram", 32768)
ci.setValue("operational_status", 1) // Operational
ci.setValue("install_status", 1) // Installed
ci.setValue("used_for", "Production")
ci.setValue("owned_by", "sys_id_of_owner")
ci.setValue("support_group", "sys_id_of_group")
var sysId = ci.insert()
ディスカバリーソースを指定した CI
// CI from Discovery
var ci = new GlideRecord("cmdb_ci_linux_server")
ci.initialize()
ci.setValue("name", "discovered-server-001")
ci.setValue("discovery_source", "ServiceNow")
ci.setValue("first_discovered", new GlideDateTime())
ci.setValue("last_discovered", new GlideDateTime())
ci.setValue("ip_address", "10.0.2.50")
// Set classification
ci.setValue("classification", "Production")
ci.setValue("environment", "Production")
ci.insert()
CI の関係
関係タイプ
| タイプ | 親 → 子 | 例 |
|---|---|---|
Runs on::Runs | アプリ → サーバー | ERP は PROD-DB-01 上で動作 |
Depends on::Used by | サービス → アプリ | HR サービスは SAP に依存 |
Contains::Contained by | クラスター → サーバー | クラスターは Node1 を含む |
Hosted on::Hosts | VM → ハイパーバイザー | VM01 は ESX01 上でホストされる |
Members::Member of | CI → グループ | サーバーはプールのメンバー |
関係の作成(ES5)
// Create relationship between CIs
function createCIRelationship(parentSysId, childSysId, relationType) {
// Find relationship type
var relType = new GlideRecord("cmdb_rel_type")
relType.addQuery("name", relationType)
relType.query()
if (!relType.next()) {
gs.error("Relationship type not found: " + relationType)
return null
}
// Check if relationship already exists
var existing = new GlideRecord("cmdb_rel_ci")
existing.addQuery("parent", parentSysId)
existing.addQuery("child", childSysId)
existing.addQuery("type", relType.getUniqueValue())
existing.query()
if (existing.next()) {
gs.info("Relationship already exists")
return existing.getUniqueValue()
}
// Create new relationship
var rel = new GlideRecord("cmdb_rel_ci")
rel.initialize()
rel.setValue("parent", parentSysId)
rel.setValue("child", childSysId)
rel.setValue("type", relType.getUniqueValue())
return rel.insert()
}
// Usage
createCIRelationship(appSysId, serverSysId, "Runs on::Runs")
関係のクエリ
// Find all servers an application runs on
function getAppServers(appSysId) {
var servers = []
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", appSysId)
rel.addQuery("type.name", "Runs on::Runs")
rel.query()
while (rel.next()) {
var server = rel.child.getRefRecord()
servers.push({
sys_id: server.getUniqueValue(),
name: server.getValue("name"),
ip_address: server.getValue("ip_address"),
})
}
return servers
}
// Find all dependencies of a service
function getServiceDependencies(serviceSysId) {
var deps = []
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", serviceSysId)
rel.addQuery("type.name", "Depends on::Used by")
rel.query()
while (rel.next()) {
deps.push({
sys_id: rel.child.getUniqueValue(),
name: rel.child.getDisplayValue(),
class: rel.child.sys_class_name.toString(),
})
}
return deps
}
影響分析
上流/下流の分析
// Get all CIs affected by a CI outage (downstream impact)
function getDownstreamImpact(ciSysId, depth) {
if (typeof depth === "undefined") depth = 3
var impacted = []
var processed = {}
function traverse(sysId, currentDepth) {
if (currentDepth > depth || processed[sysId]) return
processed[sysId] = true
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("child", sysId)
rel.query()
while (rel.next()) {
var parentId = rel.parent.toString()
if (!processed[parentId]) {
impacted.push({
sys_id: parentId,
name: rel.parent.getDisplayValue(),
depth: currentDepth,
})
traverse(parentId, currentDepth + 1)
}
}
}
traverse(ciSysId, 1)
return impacted
}
// Get all CIs this CI depends on (upstream dependencies)
function getUpstreamDependencies(ciSysId, depth) {
if (typeof depth === "undefined") depth = 3
var dependencies = []
var processed = {}
function traverse(sysId, currentDepth) {
if (currentDepth > depth || processed[sysId]) return
processed[sysId] = true
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", sysId)
rel.query()
while (rel.next()) {
var childId = rel.child.toString()
if (!processed[childId]) {
dependencies.push({
sys_id: childId,
name: rel.child.getDisplayValue(),
depth: currentDepth,
})
traverse(childId, currentDepth + 1)
}
}
}
traverse(ciSysId, 1)
return dependencies
}
ビジネスサービスへの影響
// Find all business services impacted by a CI
function getImpactedServices(ciSysId) {
var services = []
var processed = {}
function findServices(sysId) {
if (processed[sysId]) return
processed[sysId] = true
// Check if this CI is a service
var ci = new GlideRecord("cmdb_ci")
if (ci.get(sysId)) {
if (ci.sys_class_name.toString().indexOf("cmdb_ci_service") === 0) {
services.push({
sys_id: sysId,
name: ci.getValue("name"),
criticality: ci.getValue("busines_criticality"),
})
}
}
// Traverse upstream
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("child", sysId)
rel.query()
while (rel.next()) {
findServices(rel.parent.toString())
}
}
findServices(ciSysId)
return services
}
CMDB の健全性とデータ品質
孤立した CI の検出
// Find CIs without relationships
function findOrphanCIs(ciClass) {
var orphans = []
var ci = new GlideRecord(ciClass || "cmdb_ci")
ci.addQuery("operational_status", 1) // Operational only
ci.query()
while (ci.next()) {
var sysId = ci.getUniqueValue()
// Check for any relationships
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", sysId).addOrCondition("child", sysId)
rel.setLimit(1)
rel.query()
if (!rel.hasNext()) {
orphans.push({
sys_id: sysId,
name: ci.getValue("name"),
class: ci.getValue("sys_class_name"),
})
}
}
return orphans
}
古くなった CI の検出
// Find CIs not updated by discovery
function findStaleCIs(daysOld) {
if (typeof daysOld === "undefined") daysOld = 30
var stale = []
var cutoff = new GlideDateTime()
cutoff.addDaysLocalTime(-daysOld)
var ci = new GlideRecord("cmdb_ci")
ci.addQuery("operational_status", 1)
ci.addQuery("last_discovered", "<", cutoff)
ci.addNotNullQuery("last_discovered")
ci.query()
while (ci.next()) {
stale.push({
sys_id: ci.getUniqueValue(),
name: ci.getValue("name"),
last_discovered: ci.getValue("last_discovered"),
})
}
return stale
}
MCP ツールの統合
利用可能な CMDB ツール
| ツール | 用途 |
|---|---|
snow_cmdb_identify_reconcile | IRE を通じて CI を挿入/更新します。書き込み前に CI を識別します |
snow_create_ci | 適切なクラスで新しい CI を作成します(クラスのテーブルに直接書き込みます) |
snow_cmdb_search | フィルターを使用して CI を検索します |
snow_create_ci_relationship | CI の関係を作成します |
snow_impact_analysis | CI の影響を分析します |
snow_get_ci_details | CI の詳細情報を取得します |
snow_run_discovery | ディスカバリーを実行します |
まず検索するのではなく、IRE を通じて CI を書き込む
CI を検索し、見つからなければ作成する方法は、CMDB を重複で埋め尽くす原因になります。名前、IP、シリアル番号のどれかに差異があれば一致せず、2 回目の書き込みにはデータの出所を示す記録も残りません。Identification and Reconciliation Engine は、適切に照合するための仕組みです。snow_cmdb_identify_reconcile はペイロードを /api/now/identifyreconcile に送信するため、各クラスの識別ルールが適用され、既存の CI が重複作成される代わりに更新されます。また、調整ルールによって、このデータソースが各属性を上書きできるかどうかが決まり、書き込みは Source [sys_object_source] に記録されます。
snow_create_ci と snow_update_ci はクラスのテーブルに直接書き込むため、こうした処理はすべて省略されます。CMDB に存在しないことが確実で、ソースに帰属させる必要もない CI に使用してください。名前に惑わされず、snow_reconcile_ci は使用しないでください。これはフィールドをベースの cmdb_ci に PUT し、reconciliation_rule 引数を変更せずに返すだけで、ルールは実行されません。
1 つのペイロードには、独自のアイテム間の relations も含められます。type は CI Relationship Type [cmdb_rel_type] の名前フィールド値で、parent/child は items 内のインデックスです。これにより、ホストとそのホスト上のアプリケーションを検出したソースが、両方を 1 回の呼び出しで送信できます。
dry_run では何も確定されませんが、権限は引数ごとではなくツールごとに設定されているため、書き込みとして扱われます。したがって、本番インスタンスでは、ドライランにも実際の呼び出しと同じ __confirmProd が必要です。
ワークフローの例
// 1. Dry run first: IRE reports what it would do and commits nothing. On a
// 400-CI payload this is the only way to see the verdict before taking it.
await snow_cmdb_identify_reconcile({
data_source: "ServiceNow", // must be a choice on cmdb_ci.discovery_source
dry_run: true,
items: [
{
className: "cmdb_ci_linux_server",
values: { name: "PROD-WEB-002", ip_address: "10.0.1.101", serial_number: "VMW-42-8A" },
},
],
})
// Read items[0].operation — INSERT, UPDATE, NO_CHANGE, UPDATE_WITH_UPGRADE,
// UPDATE_WITH_DOWNGRADE, UPDATE_WITH_SWITCH or DELETE — and
// items[0].identificationAttempts for the identifier rule that matched.
// 2. Same payload without dry_run commits it. IRE answers HTTP 200 even when
// individual items fail, so check data.items[i].errors, which the tool
// surfaces as a failure rather than a success.
const ire = await snow_cmdb_identify_reconcile({
data_source: "ServiceNow",
items: [
{
className: "cmdb_ci_linux_server",
values: { name: "PROD-WEB-002", ip_address: "10.0.1.101", serial_number: "VMW-42-8A" },
},
],
})
// sys_id of the CI IRE inserted or matched: ire.data.items[0].sysId
// 3. Create relationship (relationship_type is a cmdb_rel_type sys_id,
// look it up via snow_query_table on cmdb_rel_type)
await snow_create_ci_relationship({
parent_ci: appSysId,
child_ci: serverSysId,
relationship_type: runsOnRelTypeSysId, // sys_id of "Runs on::Runs"
})
// 4. Impact analysis (the CI argument is ci_id; there is no direction parameter).
// include_services is left off on purpose: the business_services key it fills
// is not impact — see the csdm-modeling skill.
await snow_impact_analysis({
ci_id: serverSysId,
depth: 3,
include_services: false,
})
ベストプラクティス
- 正しい CI クラスを使用する - 常に最も具体的なクラスを使用します(
cmdb_ciではなくcmdb_ci_linux_server)。 - リレーションシップを維持する - リレーションシップのない CI の価値は限られます。
- ディスカバリーとの整合性を保つ - 手動で作成した CI をディスカバリーのパターンに合わせます。
- 運用ステータスを管理する - ステータスを最新に保ちます(運用中、廃止など)。
- 一意の識別子を使用する - 一意性を保つために
serial_number、asset_tagを使用します。 - サービスマッピングを行う - CI をビジネスサービスに接続します。
- 定期的にクリーンアップする - 廃止された CI をアーカイブし、孤立した CI を削除します。