mcpField
A
asset-management

asset-management

github.com/serac-labs云与 DevOps7782 次安装9 周前更新
代码仓库

关于 asset-management

面向 ServiceNow 的资产管理技能,用于跟踪硬件和软件资产的生命周期。内容涵盖资产记录、软件许可证分配、将发现的配置项与资产匹配及库存报告,并提供 ES5 示例。

主要功能

  1. 01硬件资产记录
  2. 02软件许可证记录与分配
  3. 03将 CI 与资产匹配
  4. 04生命周期状态跟踪
  5. 05库存汇总报告
  6. 06ES5 脚本示例

适用场景

  1. 01跟踪硬件生命周期
  2. 02向用户分配软件许可证
  3. 03将发现的 CI 与资产匹配
  4. 04按状态或位置汇总库存

MCP 相关

文档列出用于查询资产和许可证、搜索 CMDB 中配置项、测试脚本及查找资产配置的 MCP 工具。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/serac-labs/serac --skill asset-management

安装后在 Claude Code 中输入/asset-management确认已加载。

详细介绍

ServiceNow 资产管理

资产管理可在硬件和软件资产的整个生命周期内对其进行跟踪。

资产架构

Asset (alm_asset)
    ├── Hardware Asset (alm_hardware)
    │   └── Consumable (alm_consumable)
    └── License (alm_license)

CI (cmdb_ci) ←→ Asset (alm_asset)
    ↑
    One CI can have multiple assets over time

关键表

表用途
alm_asset基础资产表
alm_hardware硬件资产
alm_license软件许可证
alm_consumable消耗品资产
ast_contract资产合同
cmdb_ci配置项

硬件资产(ES5)

创建硬件资产
// Create hardware asset (ES5 ONLY!)
var asset = new GlideRecord("alm_hardware")
asset.initialize()

// Basic info
asset.setValue("display_name", "Dell Latitude 5520")
asset.setValue("asset_tag", "ASSET-" + generateAssetTag())
asset.setValue("serial_number", "SN123456789")

// Model
asset.setValue("model", getModelSysId("Dell Latitude 5520"))
asset.setValue("model_category", getModelCategorySysId("Laptop"))

// Status and substatus
asset.setValue("install_status", 1) // Installed
asset.setValue("substatus", "in_use")

// Assignment
asset.setValue("assigned_to", userSysId)
asset.setValue("assigned", new GlideDateTime())
asset.setValue("location", locationSysId)
asset.setValue("department", departmentSysId)

// Financial
asset.setValue("cost", 1299.99)
asset.setValue("cost_center", costCenterSysId)

// Dates
asset.setValue("purchase_date", "2024-01-15")
asset.setValue("warranty_expiration", "2027-01-15")

// Link to CI if exists
asset.setValue("ci", cmdbCiSysId)

asset.insert()
资产生命周期状态
// Asset install_status values
var ASSET_STATUS = {
  INSTALLED: 1, // In use
  ON_ORDER: 2, // Ordered, not received
  IN_STOCK: 6, // In inventory
  IN_TRANSIT: 7, // Being shipped
  IN_MAINTENANCE: 8, // Under repair
  RETIRED: 9, // End of life
  DISPOSED: 10, // Disposed of
}

// Transition asset status (ES5 ONLY!)
function transitionAssetStatus(assetSysId, newStatus, notes) {
  var asset = new GlideRecord("alm_hardware")
  if (!asset.get(assetSysId)) {
    return { success: false, message: "Asset not found" }
  }

  var currentStatus = parseInt(asset.getValue("install_status"), 10)

  // Validate transition
  var validTransitions = {
    2: [6, 7], // On Order -> In Stock, In Transit
    7: [6, 1], // In Transit -> In Stock, Installed
    6: [1, 7, 9], // In Stock -> Installed, In Transit, Retired
    1: [8, 6, 9], // Installed -> In Maintenance, In Stock, Retired
    8: [1, 6, 9], // In Maintenance -> Installed, In Stock, Retired
    9: [10], // Retired -> Disposed
  }

  if (!validTransitions[currentStatus] || validTransitions[currentStatus].indexOf(newStatus) === -1) {
    return {
      success: false,
      message: "Invalid transition from " + currentStatus + " to " + newStatus,
    }
  }

  // Update status
  asset.setValue("install_status", newStatus)

  // Handle specific transitions
  if (newStatus === 1) {
    asset.setValue("installed", new GlideDateTime())
  } else if (newStatus === 9) {
    asset.setValue("retired", new GlideDateTime())
    asset.setValue("assigned_to", "")
  } else if (newStatus === 10) {
    asset.setValue("disposed", new GlideDateTime())
  }

  // Add work note
  if (notes) {
    asset.work_notes = notes
  }

  asset.update()

  return { success: true, asset_tag: asset.getValue("asset_tag") }
}

软件许可证(ES5)

创建许可证记录
// Create software license (ES5 ONLY!)
var license = new GlideRecord("alm_license")
license.initialize()

// Basic info
license.setValue("display_name", "Microsoft Office 365 E3")
license.setValue("product", getProductSysId("Microsoft Office 365"))
license.setValue("license_type", "per_user") // per_user, per_device, site, enterprise

// Quantities
license.setValue("rights", 500) // Total licenses purchased
license.setValue("used", 0) // Will be calculated
license.setValue("remaining", 500) // Will be calculated

// Dates
license.setValue("start_date", "2024-01-01")
license.setValue("end_date", "2024-12-31")

// Cost
license.setValue("cost", 25000.0)
license.setValue("cost_per_unit", 50.0)

// Vendor
license.setValue("vendor", vendorSysId)
license.setValue("contract", contractSysId)

license.insert()
许可证分配
// Allocate license to user (ES5 ONLY!)
function allocateLicense(licenseSysId, userSysId) {
  var license = new GlideRecord("alm_license")
  if (!license.get(licenseSysId)) {
    return { success: false, message: "License not found" }
  }

  // Check availability
  var remaining = parseInt(license.getValue("remaining"), 10)
  if (remaining <= 0) {
    return { success: false, message: "No licenses available" }
  }

  // Check if user already has this license
  var existing = new GlideRecord("alm_entitlement_user")
  existing.addQuery("licensed_by", licenseSysId)
  existing.addQuery("user", userSysId)
  existing.query()

  if (existing.hasNext()) {
    return { success: false, message: "User already has this license" }
  }

  // Create entitlement
  var entitlement = new GlideRecord("alm_entitlement_user")
  entitlement.initialize()
  entitlement.setValue("licensed_by", licenseSysId)
  entitlement.setValue("user", userSysId)
  entitlement.setValue("allocated", new GlideDateTime())
  entitlement.insert()

  // Update license counts
  updateLicenseCounts(licenseSysId)

  return {
    success: true,
    message: "License allocated",
    entitlement: entitlement.getUniqueValue(),
  }
}

function updateLicenseCounts(licenseSysId) {
  var license = new GlideRecord("alm_license")
  if (!license.get(licenseSysId)) return

  // Count allocations
  var ga = new GlideAggregate("alm_entitlement_user")
  ga.addQuery("licensed_by", licenseSysId)
  ga.addAggregate("COUNT")
  ga.query()

  var used = 0
  if (ga.next()) {
    used = parseInt(ga.getAggregate("COUNT"), 10)
  }

  var rights = parseInt(license.getValue("rights"), 10)

  license.setValue("used", used)
  license.setValue("remaining", rights - used)
  license.update()
}

资产发现集成(ES5)

将发现的 CI 与资产匹配
// Match discovered CI to existing asset (ES5 ONLY!)
function matchCIToAsset(ciSysId) {
  var ci = new GlideRecord("cmdb_ci_computer")
  if (!ci.get(ciSysId)) {
    return null
  }

  var serialNumber = ci.getValue("serial_number")
  var assetTag = ci.getValue("asset_tag")

  // Try to find matching asset
  var asset = new GlideRecord("alm_hardware")

  // Match by serial number first
  if (serialNumber) {
    asset.addQuery("serial_number", serialNumber)
    asset.query()
    if (asset.next()) {
      return linkAssetToCI(asset, ci)
    }
  }

  // Match by asset tag
  if (assetTag) {
    asset = new GlideRecord("alm_hardware")
    asset.addQuery("asset_tag", assetTag)
    asset.query()
    if (asset.next()) {
      return linkAssetToCI(asset, ci)
    }
  }

  // No match - create new asset
  return createAssetFromCI(ci)
}

function linkAssetToCI(asset, ci) {
  asset.setValue("ci", ci.getUniqueValue())
  asset.update()

  ci.setValue("asset", asset.getUniqueValue())
  ci.update()

  return asset.getUniqueValue()
}

function createAssetFromCI(ci) {
  var asset = new GlideRecord("alm_hardware")
  asset.initialize()
  asset.setValue("display_name", ci.getDisplayValue())
  asset.setValue("serial_number", ci.getValue("serial_number"))
  asset.setValue("asset_tag", ci.getValue("asset_tag"))
  asset.setValue("model", ci.getValue("model_id"))
  asset.setValue("ci", ci.getUniqueValue())
  asset.setValue("install_status", 1)

  var assetSysId = asset.insert()

  ci.setValue("asset", assetSysId)
  ci.update()

  return assetSysId
}

资产报告(ES5)

资产库存摘要
// Get asset inventory summary (ES5 ONLY!)
function getAssetInventorySummary() {
  var summary = {
    by_status: {},
    by_category: {},
    by_location: {},
    total_value: 0,
  }

  // By status
  var ga = new GlideAggregate("alm_hardware")
  ga.addAggregate("COUNT")
  ga.addAggregate("SUM", "cost")
  ga.groupBy("install_status")
  ga.query()

  while (ga.next()) {
    var status = ga.install_status.getDisplayValue()
    summary.by_status[status] = {
      count: parseInt(ga.getAggregate("COUNT"), 10),
      value: parseFloat(ga.getAggregate("SUM", "cost")) || 0,
    }
    summary.total_value += summary.by_status[status].value
  }

  // By model category
  ga = new GlideAggregate("alm_hardware")
  ga.addAggregate("COUNT")
  ga.groupBy("model_category")
  ga.query()

  while (ga.next()) {
    var category = ga.model_category.getDisplayValue() || "Uncategorized"
    summary.by_category[category] = parseInt(ga.getAggregate("COUNT"), 10)
  }

  // By location
  ga = new GlideAggregate("alm_hardware")
  ga.addQuery("install_status", 1) // Only installed
  ga.addAggregate("COUNT")
  ga.groupBy("location")
  ga.query()

  while (ga.next()) {
    var location = ga.location.getDisplayValue() || "Unknown"
    summary.by_location[location] = parseInt(ga.getAggregate("COUNT"), 10)
  }

  return summary
}

MCP 工具集成

可用工具
工具用途
snow_query_table查询资产和许可证
snow_cmdb_search在 CMDB 中搜索 CI
snow_execute_script测试资产脚本
snow_artifact_manage查找资产配置(action: find)
示例工作流
// 1. Query hardware assets
await snow_query_table({
  table: "alm_hardware",
  query: "install_status=1",
  fields: "asset_tag,display_name,assigned_to,location,model",
})

// 2. Check license compliance
await snow_execute_script({
  script: `
        var license = new GlideRecord('alm_license');
        license.addQuery('remainingRELATIVELT@integer@0');
        license.query();
        while (license.next()) {
            gs.info('Over-allocated: ' + license.display_name);
        }
    `,
})

// 3. Find assets nearing warranty expiration
await snow_query_table({
  table: "alm_hardware",
  query: "warranty_expirationBETWEENjavascript:gs.beginningOfToday()@javascript:gs.daysAgoEnd(-30)",
  fields: "asset_tag,display_name,warranty_expiration,assigned_to",
})

最佳实践

  1. 资产标签 - 使用唯一且可扫描的标识符
  2. 生命周期跟踪 - 跟踪所有状态变更
  3. CI 关联 - 将资产关联到 CMDB
  4. 许可证合规性 - 监控分配情况与授权数量
  5. 保修跟踪 - 在保修到期前发出提醒
  6. 财务准确性 - 维护成本数据
  7. 定期审计 - 核实实物库存
  8. 仅限 ES5 - 不使用现代 JavaScript 语法

评论

支持 Markdown