Install
$ agentstack add skill-happy-technologies-llc-happy-platform-skills-data-quality ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
CMDB Data Quality Management
Overview
A CMDB is only as valuable as its data quality. Poor data leads to failed changes, incorrect impact analysis, and loss of trust. This skill covers:
- Measuring data quality KPIs (completeness, accuracy, consistency)
- Detecting and resolving duplicate CIs
- Identifying and managing stale/orphan records
- Implementing reconciliation processes
- Establishing ongoing data governance
When to use: During CMDB health assessments, before major changes, as part of regular maintenance, or when CMDB trust is low.
Value proposition: High-quality CMDB data can reduce change failure rates by 50%, decrease incident resolution time by 30%, and improve audit compliance.
Prerequisites
- Roles:
cmdb_adminfor full access,itilfor read-only assessment - Access: cmdbci, cmdbrelci, sysaudit tables
- Knowledge: CMDB structure, identification rules
- Related skills:
cmdb/ci-discovery,cmdb/relationship-mapping
Data Quality Dimensions
| Dimension | Definition | Target | Measurement | |-----------|------------|--------|-------------| | Completeness | Required fields populated | >95% | % records with all mandatory fields | | Accuracy | Data matches reality | >90% | % verified against authoritative source | | Consistency | No conflicting data | >98% | % records without conflicts | | Timeliness | Data is current | 99% | % unique records | | Validity | Values within allowed ranges | >99% | % passing validation rules |
Procedure
Step 1: Assess Completeness
Completeness measures whether required fields are populated.
Define mandatory fields by CI class:
| CI Class | Mandatory Fields | |----------|------------------| | cmdbciserver | name, ipaddress, os, operationalstatus, supportgroup | | cmdbciappl | name, version, operationalstatus, supportgroup, installstatus | | cmdbciservice | name, operationalstatus, businesscriticality, ownedby | | cmdbcidatabase | name, type, version, operationalstatus |
Check completeness for servers:
Tool: SN-Query-Table
Parameters:
table_name: cmdb_ci_server
query: operational_status=1^nameISEMPTY^ORip_addressISEMPTY^ORsupport_groupISEMPTY
fields: sys_id,name,ip_address,support_group
limit: 100
Comprehensive completeness audit:
Tool: SN-Execute-Background-Script
Parameters:
description: CMDB Completeness Audit
script: |
var mandatoryFields = {
'cmdb_ci_server': ['name', 'ip_address', 'os', 'operational_status', 'support_group'],
'cmdb_ci_appl': ['name', 'version', 'operational_status', 'support_group'],
'cmdb_ci_service': ['name', 'operational_status', 'business_criticality', 'owned_by'],
'cmdb_ci_database_instance': ['name', 'type', 'version', 'operational_status']
};
var results = {};
for (var ciClass in mandatoryFields) {
var fields = mandatoryFields[ciClass];
var total = 0;
var complete = 0;
var incomplete = [];
var gr = new GlideRecord(ciClass);
gr.addQuery('operational_status', '1'); // Only operational CIs
gr.query();
while (gr.next()) {
total++;
var isComplete = true;
var missingFields = [];
for (var i = 0; i 0 ? Math.round((complete / total) * 100) : 100,
incomplete: incomplete.slice(0, 10) // First 10 examples
};
}
gs.info('=== COMPLETENESS AUDIT RESULTS ===');
for (var cls in results) {
var r = results[cls];
gs.info(cls + ': ' + r.completeness + '% complete (' + r.complete + '/' + r.total + ')');
if (r.incomplete.length > 0) {
gs.info(' Examples of incomplete records:');
r.incomplete.forEach(function(inc) {
gs.info(' ' + inc.name + ' - Missing: ' + inc.missing.join(', '));
});
}
}
Step 2: Detect Duplicates
Duplicates undermine CMDB trust and cause confusion.
Find potential duplicates by name:
Tool: SN-Execute-Background-Script
Parameters:
description: Find duplicate CIs by name
script: |
var duplicates = new GlideAggregate('cmdb_ci');
duplicates.addQuery('operational_status', '1');
duplicates.addAggregate('COUNT');
duplicates.groupBy('name');
duplicates.addHaving('COUNT', '>', 1);
duplicates.orderByAggregate('COUNT', 'DESC');
duplicates.query();
gs.info('=== DUPLICATE CI NAMES ===');
var count = 0;
while (duplicates.next() && count ', 1);
duplicates.query();
gs.info('=== DUPLICATE IP ADDRESSES ===');
while (duplicates.next()) {
var ip = duplicates.ip_address.toString();
var count = duplicates.getAggregate('COUNT');
gs.info('IP: ' + ip + ' - Count: ' + count);
var details = new GlideRecord('cmdb_ci_computer');
details.addQuery('ip_address', ip);
details.addQuery('operational_status', '1');
details.query();
while (details.next()) {
gs.info(' ' + details.name + ' (' + details.sys_class_name + ')');
}
}
Find duplicates by serial number:
Tool: SN-Query-Table
Parameters:
table_name: cmdb_ci_hardware
query: serial_numberISNOTEMPTY^operational_status=1
fields: serial_number,name,sys_class_name,sys_id
Then analyze for duplicates:
Tool: SN-Execute-Background-Script
Parameters:
description: Find duplicate serial numbers
script: |
var duplicates = new GlideAggregate('cmdb_ci_hardware');
duplicates.addQuery('operational_status', '1');
duplicates.addNotNullQuery('serial_number');
duplicates.addAggregate('COUNT');
duplicates.groupBy('serial_number');
duplicates.addHaving('COUNT', '>', 1);
duplicates.query();
gs.info('=== DUPLICATE SERIAL NUMBERS ===');
gs.info('Found: ' + duplicates.getRowCount() + ' duplicate serial numbers');
while (duplicates.next()) {
gs.info('Serial: ' + duplicates.serial_number + ' Count: ' + duplicates.getAggregate('COUNT'));
}
Step 3: Identify Stale Records
Stale records haven't been updated within acceptable timeframes.
Define staleness thresholds:
| CI Type | Staleness Threshold | Rationale | |---------|---------------------|-----------| | Servers | 30 days | Should be scanned regularly | | Applications | 60 days | Less dynamic than infrastructure | | Services | 90 days | Business services change slowly | | Network | 14 days | Network scans are frequent |
Find stale server records:
Tool: SN-Query-Table
Parameters:
table_name: cmdb_ci_server
query: operational_status=1^sys_updated_on 0 ? Math.round(((total - staleCount) / total) * 100) : 100
};
// Sample stale records
var samples = [];
stale.setLimit(5);
stale.query();
while (stale.next()) {
samples.push({
name: stale.name.toString(),
last_updated: stale.sys_updated_on.getDisplayValue()
});
}
results[ciClass].samples = samples;
}
gs.info('=== STALENESS AUDIT RESULTS ===');
for (var cls in results) {
var r = results[cls];
gs.info(cls + ': ' + r.freshness + '% fresh (' + r.stale + ' stale of ' + r.total + ', threshold: ' + r.threshold + ' days)');
if (r.samples.length > 0) {
r.samples.forEach(function(s) {
gs.info(' Stale: ' + s.name + ' (last updated: ' + s.last_updated + ')');
});
}
}
Step 4: Check Relationship Health
Relationships are a key quality dimension.
Find orphan CIs (no relationships):
Tool: SN-Execute-Background-Script
Parameters:
description: Find CIs with no relationships
script: |
var ciClasses = ['cmdb_ci_server', 'cmdb_ci_appl', 'cmdb_ci_database_instance'];
var orphans = {};
ciClasses.forEach(function(ciClass) {
orphans[ciClass] = [];
var gr = new GlideRecord(ciClass);
gr.addQuery('operational_status', '1');
gr.query();
while (gr.next()) {
var hasRel = new GlideRecord('cmdb_rel_ci');
hasRel.addQuery('parent', gr.sys_id);
hasRel.addOrCondition('child', gr.sys_id);
hasRel.setLimit(1);
hasRel.query();
if (!hasRel.hasNext()) {
orphans[ciClass].push({
sys_id: gr.sys_id.toString(),
name: gr.name.toString()
});
}
}
});
gs.info('=== ORPHAN CI REPORT ===');
for (var cls in orphans) {
gs.info(cls + ': ' + orphans[cls].length + ' orphans');
orphans[cls].slice(0, 5).forEach(function(o) {
gs.info(' ' + o.name + ' (' + o.sys_id + ')');
});
}
Find invalid relationships:
Tool: SN-Query-Table
Parameters:
table_name: cmdb_rel_ci
query: parent.operational_status!=1^ORchild.operational_status!=1
fields: parent.name,child.name,parent.operational_status,child.operational_status
limit: 50
Step 5: Validate Data Accuracy
Compare CMDB data against authoritative sources.
Validate against discovery data:
Tool: SN-Execute-Background-Script
Parameters:
description: Compare CMDB to discovery data
script: |
// Find CIs where CMDB data differs from discovery
var mismatches = [];
var gr = new GlideRecord('cmdb_ci_server');
gr.addQuery('operational_status', '1');
gr.addNotNullQuery('discovery_source');
gr.query();
while (gr.next()) {
var issues = [];
// Check if last discovery was recent
if (gr.last_discovered) {
var lastDisc = new GlideDateTime(gr.last_discovered);
var now = new GlideDateTime();
var daysSince = gs.dateDiff(lastDisc, now, true) / (24 * 60 * 60 * 1000);
if (daysSince > 30) {
issues.push('Not discovered in ' + Math.round(daysSince) + ' days');
}
}
// Check for empty critical fields that discovery should populate
if (!gr.os || gr.os.toString() === '') {
issues.push('OS not set');
}
if (!gr.cpu_count || gr.cpu_count.toString() === '0') {
issues.push('CPU count not set');
}
if (!gr.ram || gr.ram.toString() === '0') {
issues.push('RAM not set');
}
if (issues.length > 0) {
mismatches.push({
name: gr.name.toString(),
sys_id: gr.sys_id.toString(),
issues: issues
});
}
}
gs.info('=== ACCURACY VALIDATION ===');
gs.info('CIs with potential accuracy issues: ' + mismatches.length);
mismatches.slice(0, 20).forEach(function(m) {
gs.info(' ' + m.name + ': ' + m.issues.join(', '));
});
Step 6: Generate Data Quality Dashboard
Create a comprehensive quality score:
Tool: SN-Execute-Background-Script
Parameters:
description: Generate CMDB Data Quality Score
script: |
var dashboard = {
generatedAt: new GlideDateTime().getDisplayValue(),
totalCIs: 0,
operationalCIs: 0,
scores: {
completeness: 0,
uniqueness: 0,
freshness: 0,
relationships: 0
},
overallScore: 0,
grade: '',
topIssues: []
};
// Count total CIs
var totalGr = new GlideAggregate('cmdb_ci');
totalGr.addAggregate('COUNT');
totalGr.query();
if (totalGr.next()) {
dashboard.totalCIs = parseInt(totalGr.getAggregate('COUNT'));
}
// Count operational CIs
var opGr = new GlideAggregate('cmdb_ci');
opGr.addQuery('operational_status', '1');
opGr.addAggregate('COUNT');
opGr.query();
if (opGr.next()) {
dashboard.operationalCIs = parseInt(opGr.getAggregate('COUNT'));
}
// Completeness Score (check name and class are set)
var completeGr = new GlideAggregate('cmdb_ci');
completeGr.addQuery('operational_status', '1');
completeGr.addNotNullQuery('name');
completeGr.addNotNullQuery('sys_class_name');
completeGr.addAggregate('COUNT');
completeGr.query();
if (completeGr.next()) {
var complete = parseInt(completeGr.getAggregate('COUNT'));
dashboard.scores.completeness = Math.round((complete / dashboard.operationalCIs) * 100);
}
// Uniqueness Score (inverse of duplicate rate)
var dupGr = new GlideAggregate('cmdb_ci');
dupGr.addQuery('operational_status', '1');
dupGr.addAggregate('COUNT');
dupGr.groupBy('name');
dupGr.addHaving('COUNT', '>', 1);
dupGr.query();
var dupGroups = dupGr.getRowCount();
dashboard.scores.uniqueness = Math.max(0, 100 - Math.round((dupGroups / dashboard.operationalCIs) * 100 * 10));
// Freshness Score (updated in last 30 days)
var cutoff = new GlideDateTime();
cutoff.addDays(-30);
var freshGr = new GlideAggregate('cmdb_ci');
freshGr.addQuery('operational_status', '1');
freshGr.addQuery('sys_updated_on', '>', cutoff);
freshGr.addAggregate('COUNT');
freshGr.query();
if (freshGr.next()) {
var fresh = parseInt(freshGr.getAggregate('COUNT'));
dashboard.scores.freshness = Math.round((fresh / dashboard.operationalCIs) * 100);
}
// Relationship Score (CIs with at least one relationship)
var withRelGr = new GlideAggregate('cmdb_rel_ci');
withRelGr.addAggregate('COUNT', 'DISTINCT', 'parent');
withRelGr.query();
var withRel = 0;
if (withRelGr.next()) {
withRel = parseInt(withRelGr.getAggregate('COUNT', 'DISTINCT', 'parent'));
}
dashboard.scores.relationships = Math.min(100, Math.round((withRel / dashboard.operationalCIs) * 100));
// Overall Score (weighted average)
dashboard.overallScore = Math.round(
(dashboard.scores.completeness * 0.3) +
(dashboard.scores.uniqueness * 0.25) +
(dashboard.scores.freshness * 0.25) +
(dashboard.scores.relationships * 0.2)
);
// Grade
if (dashboard.overallScore >= 90) dashboard.grade = 'A';
else if (dashboard.overallScore >= 80) dashboard.grade = 'B';
else if (dashboard.overallScore >= 70) dashboard.grade = 'C';
else if (dashboard.overallScore >= 60) dashboard.grade = 'D';
else dashboard.grade = 'F';
// Top Issues
if (dashboard.scores.completeness 0) {
gs.info('Top Issues to Address:');
dashboard.topIssues.forEach(function(issue) {
gs.info(' - ' + issue);
});
}
Step 7: Remediate Issues
Fix incomplete records:
Tool: SN-Update-Record
Parameters:
table_name: cmdb_ci_server
sys_id: [ci_sys_id]
data:
support_group: [group_sys_id]
os: "Red Hat Enterprise Linux 8"
operational_status: 1
Merge duplicate records:
Tool: SN-Execute-Background-Script
Parameters:
description: Merge duplicate CIs (keep newer, retire older)
script: |
var keepSysId = 'NEWER_CI_SYS_ID';
var retireSysId = 'OLDER_CI_SYS_ID';
// Move relationships from retired CI to kept CI
var rels = new GlideRecord('cmdb_rel_ci');
rels.addQuery('parent', retireSysId);
rels.query();
while (rels.next()) {
rels.parent = keepSysId;
rels.update();
}
rels = new GlideRecord('cmdb_rel_ci');
rels.addQuery('child', retireSysId);
rels.query();
while (rels.next()) {
rels.child = keepSysId;
rels.update();
}
// Retire the duplicate
var retireCi = new GlideRecord('cmdb_ci');
if (retireCi.get(retireSysId)) {
retireCi.operational_status = 6; // Retired
retireCi.install_status = 7; // Retired
retireCi.comments = 'Merged into ' + keepSysId + ' on ' + new GlideDateTime().getDisplayValue();
retireCi.update();
gs.info('Retired duplicate CI: ' + retireCi.name);
}
Retire stale records:
Tool: SN-Update-Record
Parameters:
table_name: cmdb_ci_server
sys_id: [stale_ci_sys_id]
data:
operational_status: 6
install_status: 7
comments: "Retired due to staleness - not discovered in 90+ days"
Tool Usage Summary
| Operation | MCP Tool | REST Endpoint | |-----------|----------|---------------| | Query CIs | SN-Query-Table, SN-List-CmdbCis | GET /cmdb_ci | | Update CI | SN-Update-Record |
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Happy-Technologies-LLC
- Source: Happy-Technologies-LLC/happy-platform-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.