Infrastructure health checks. Replacing manual checks of storage, SAN, and virtualization systems.

A Green Report Has to Mean Something.

I was asked to replace repeated manual console checks. Keeping the results let me spot changes over time. Reviewing my own checks also showed where a healthy score was hiding missing data.

By Scott Davis. Based on the recorded project account; wording updated September 6, 2026.

What was happening between the checks?

Executive perspective

Why does this matter?

The manual checking stopped.

A separate operations team had been logging into systems and emailing reports three times a day. I automated that work, then added saved history, change detection, capacity forecasts, and escalation. Engineers could see what had changed without digging through old emails.

A warning needed to reach somebody.

Engineers, the operations team, and management received the reports. I put critical capacity counts in the email subject so people could see trouble without opening it. Critical findings opened tickets automatically. Separate expansion tools checked that a proposed fix wouldn’t create a bigger capacity problem.

I had to check my own reports, too.

I estimated the work replaced at roughly two full-time positions of repeated manual work. That wasn’t a timed baseline or a claim that staffing changed. My own review also found healthy scores hiding missing systems, and I fixed them. Those findings mattered as much as the time saved.

Architecture perspective

Why was it designed this way?

Saving the readings changed what I could see.

At first I kept the reporting schedule people expected. Then I retained the readings so I could compare error counters with the previous run and capacity with its growth history. A lifetime error count doesn’t tell me when the errors happened. The change since the last check gives me something to investigate.

I couldn’t assume people would open the report.

A forecast buried in an email might never reach the engineer who needed it. I put severity in the subject line, and critical volume findings opened tickets automatically. Expansion stayed separate: its own checks blocked changes that would overfill the aggregate.

A failed query needed to look like a failed query.

The audit found defaults that made missing data look healthy, including a score that always read 100 percent. I fixed those. Keeping history only helps if I can trust the readings. When a query fails, the report has to say so.

Technical perspective

How does it actually work?

The checks kept their previous readings.

PowerShell and Python queried storage, SAN, and virtualization systems. Configuration files held the thresholds for each platform. SAN delta rules needed saved readings to compare. Capacity checks retained ninety days of history and flagged systems projected to fill up.

The detailed account records what I found and fixed.

The checks I’d moved onto the platform ran through Jenkins from GitLab, used controlled credentials, and archived their output. The run counts below describe the original review; they aren’t live measurements.

The detailed project account

These details come from the account written before the September 2026 site update. I’ve rewritten the wording, but kept the measurements, findings, and qualifications. “Today” and other time references describe that earlier period. I haven’t remeasured the results or assumed that open issues were fixed.

What was needed

Three times a day, a separate operations support team logged into every storage array, SAN switch, and vCenter by hand. They read the metrics off each system, assembled them into dashboards, and emailed the result out. Then four hours later they did it again.

The emails arrived, but each was a snapshot. Nobody was comparing this morning’s error counters with last night’s. The previous reading was in an older email, so looking for a developing problem meant finding and comparing those messages.

My manager asked me to replace the manual process.

What I built

I started with PowerShell on Windows Task Scheduler. It connected to each system, collected the metrics the team had been reading, applied thresholds, and emailed a formatted report. I kept the three-times-a-day schedule because that was what people expected. The automated schedule came directly from the manual one.

It worked, but I had no record of which version ran, no archived output, and no safe way for anyone else to change it. Fixing that became a separate platform project. The health checks were its first workloads. I moved them to Jenkins pipelines using GitLab, purpose-built containers, controlled credentials, and archived results.

I wrote dozens of explicit thresholds for the different platforms and put them in configuration. I wanted the rules for identifying trouble to be visible without digging through the code. They covered link errors, optics health, replication lag, and capacity, and aggregate expansion was blocked outright if a change was too large or would leave the aggregate nearly full.

The SAN checks compared counters with their previous values. A port with 4,000 lifetime CRC errors might have had them for three years. A port gaining 100 since the last run needed investigation for an optic failing now. I retained state between runs so the rules could look at change and trend, and those rules waited for at least two saved readings. The old process hadn’t made those comparisons.

What was running at the time

Workflow Production runs Average Cadence
Storage health check 1,256 4.4 min 3× daily
Virtualization health check 738 10.0 min 3× daily
SAN health check 1,269 65 sec hourly
Directory-to-IPAM sync 421 0.4 min nightly

In the retained window, 209 of 213 runs succeeded.

The reports went to storage and virtualization engineers, the operations team, and my management chain. An engineer could use them to investigate an optic. A leader could use the same report to see whether conditions across the systems were getting worse.

What it catches

Capacity was what the checks caught most often. I wanted a capacity finding to lead to action instead of sitting in an email.

Each run checked volumes, aggregates, and datastores against their thresholds. I kept ninety days of capacity history to see how fast they were filling. A volume crossing its warning threshold was flagged. So was a volume projected to fill within a month, even if it hadn’t crossed a threshold yet.

Warning-level findings showed yellow and critical findings showed red. Each red finding added to a count in the email subject. I couldn’t assume someone would keep opening a report after ninety uneventful mornings. The subject needed to tell them there was something to look at.

Critical findings opened tickets automatically, without a person deciding to open one.

Expansion was a separate step with its own checks. The tooling refused a change that was too large or would leave the aggregate nearly full. Fixing a full volume shouldn’t leave the surrounding aggregate in worse trouble.

The checks ran three times a day, forecast capacity trouble, put it in front of people, and opened tickets at the critical threshold. The separate expansion checks blocked unsafe fixes. Together, those steps helped get capacity problems handled before they became incidents.

What they watch

SAN switches More than a dozen, across multiple fabrics
vCenters More than a dozen
Storage arrays Several current, more at peak
NetApp clusters A few, current and legacy generations
Virtual machines, one discovery run Thousands found
Storage under watch Hundreds of TB committed, over a petabyte provisioned

What it replaced

I estimated the manual work replaced at roughly two full-time positions of repeated manual work.

Nobody timed the old process before it went away. This is my estimate of the repeated work people stopped doing three times a day, not a measured baseline or a claim that two jobs were eliminated.

The saved history was another change. Dozens of thresholds were evaluated every run, error counters were compared with earlier readings, and the results were archived. Engineers had more to work from than a series of emails.

Keeping it honest

What worried me most was a check saying things were healthy when it couldn’t see them. A failed monitor is obvious. A green report can leave people believing systems are fine when they haven’t been checked.

Two examples from my own systems, both fixed:

The storage check treated missing data as healthy. A component with no reported health state defaulted to OK, and a parse failure displayed All OK. I fixed that in 2025 to report Unknown and write a log entry. I also documented the paths that hid failures and classified their risk.

The virtualization health score always reported 100 percent. After I rebuilt it, the first honest run showed dozens of active alarms, dozens of snapshots past their age limit, and about half of the vCenters unreachable from the monitoring host. Half the environment had been missing behind that green score.

What I learned

  1. I put the critical count in the email subject because people could miss a finding inside a routine report. After ninety uneventful mornings, I couldn’t rely on everyone opening the message. The worst capacity findings also opened their own tickets.

  2. For cumulative counters, I needed to know what changed since the last reading. The lifetime total alone could make an old problem look urgent or hide a new one.

  3. Growth changed where I needed to pay attention. A volume at 70 percent gaining two points a week needed a different response from one that had stayed at 88 percent for two years. Keeping history let me forecast days-to-full.

  4. When a system said it succeeded, I checked what should have happened. A missing timestamp, a counter stuck at zero, or an empty result could show a failure even without an error message.

  5. I needed to test an unhealthy input and confirm the check went red. A report that stayed green for every input wasn’t useful.