A discovery schedule completes green every night, yet the network team keeps finding gear in production that has no CI. Out-of-box probes cover the obvious; the long tail of appliances, embedded controllers, and homegrown services is what custom probes are for. The trap is writing them in a way that breaks every family upgrade.
Start from the classification, not the probe
The mistake is to write a probe first and decide what to do with the data later. Begin in cmdb_ci_classification with the class you intend to populate, the identification rule, and the fields you actually need. If the class does not exist, extend cmdb_ci with a scoped child table — never add columns to base cmdb_ci. Map fields one-to-one to probe output so the sensor stays mechanical.
Probe choice has consequences
For shell-accessible targets, prefer SSH Command probes over scripted REST when possible — they are easier to debug and the MID Server retries are well-understood. For HTTPS APIs use Multi Probe with a JavaScript trigger so you can chain calls and short-circuit. Avoid JavaScript Probe for anything that hits external systems; you lose the MID Server retry semantics.
Probe: NetApp_Volumes_HTTP
Type: Multi Probe
Trigger script: extract cluster URL from CI, build /api/storage/volumes call
Parameters: ci_sys_id, target_url, cred_id
Sensor scripts must be defensive
Sensors run server-side after the MID returns payload. They will see truncated data, missing keys, type drift between firmware versions, and once in a while a complete garbage response. Wrap every field extraction in a guarded helper and log the CI sys_id with every warning. The sensor that throws an unhandled exception aborts the entire pattern.
function safeGet(obj, path, fallback) {
try { return path.split('.').reduce((o,k)=>o&&o[k], obj) ?? fallback; }
catch (e) { gs.warn('safeGet failed: '+path+' '+e); return fallback; }
}
Use Patterns over Probe/Sensor for new work
Since Quebec, Discovery Patterns are the strategic path. Old-style Probe/Sensor pairs still work but get less investment. New custom discovery should use Pattern Designer with horizontal patterns for shared logic. The migration cost from old probes to patterns is real; budget it before you accumulate a dozen one-off probes you will eventually rewrite.
Identification rules are the actual hard part
A probe that returns clean data still creates duplicate CIs if the identification rule is wrong. Use composite identifiers (serial + manufacturer, or FQDN + IP for hosts) and mark the lookup table as required. Test with a deliberate duplicate — clone a CI, change one non-identifier field, run discovery, and verify the engine reconciles instead of inserting.
Logging that survives the audit
Standardize on gs.info('[discovery:'+probeName+'] '+message+' ci='+sysId) for every meaningful event. The default ECC Queue payload is too noisy for routine triage. A grep-friendly prefix and the CI sys_id is what the operator on call at 2 AM will actually use.
Version-pin your patterns
Custom patterns should ship in a scoped app with a version in the manifest, not in Global. When the family upgrade lands, you can disable the scoped app, run upgrade, re-enable, and know exactly what changed. Custom probes loose in Global are where post-upgrade Discovery breakage hides.
What to do this week: list every active probe in discovery_probe not shipped by ServiceNow and tag the owner — anything orphaned gets the first audit pass.