Compliance backend reference

Everything the Compliance UI reads or writes lives in the HealthCheck and Collector schemas on the Minion repository DB. This page maps each UI concept to its backing objects.

End-to-end data flow

flowchart LR
    subgraph "Every N minutes / hours"
        C1["Collectors scrape<br/>target servers"] --> C2["Collector.Logins<br/>Collector.DBProperties<br/>Collector.InstanceConfig<br/>..."]
    end

    subgraph "One time (per profile)"
        S1["User picks metrics"] --> S2["HealthCheck.ReportMetric<br/>links profile to metrics"]
        S3["User adds tuning"] --> S4["HealthCheck.MetricThreshold<br/>per-scope rules"]
    end

    subgraph "Each Run"
        R1["User clicks Run now"] --> R2["ComplianceService<br/>calls ReportMaster"]
        R2 --> R3["ReportMaster resolves<br/>cascading thresholds"]
        R3 --> R4["Load HealthCheck.Work<br/>with per-row context"]
        R4 --> R5["EXEC each metric SP"]
        R5 --> R6["Each SP joins<br/>Collector.* + Work"]
        R6 --> R7["Write to<br/>HealthCheck.MetricLog*"]
        R7 --> R8["Update HealthCheck.ReportLog<br/>with Pass/Fail counts"]
    end

    C2 -.->|"read at run time"| R6
    S2 -.->|"walked at run time"| R2
    S4 -.->|"resolved at run time"| R3
    R8 -.->|"shown in history grid"| U1["Compliance UI<br/>Run & History tab"]
    R7 -.->|"detail queries"| U2["Query Runner<br/>MetricLog inspection"]

Key property: the compliance engine never touches remote SQL Servers directly. It reads from the pre-collected Collector.* fact tables. The collectors that fill those tables are the ones that talk to production; the engine grades what they wrote.

Tables

Table Compliance UI concept Notes
HealthCheck.Report Profile (left rail row) Primary key ReportName. IsAudit drives the AUDIT/CUSTOM badge. Comment shows on Overview tab.
HealthCheck.Metric Metric library (Available pane) ~90 rows. ProcName points at the backing SP; MeasureLevel = Instance or Database.
HealthCheck.ReportMetric In-profile metrics (Metrics tab left pane) Junction: ReportName + MetricID + MetricOrder + IsActive. IsActive = 0 = "Off" button.
HealthCheck.MetricThreshold Threshold rules (Thresholds grid) Cascade key: (ReportName, MetricID, InstanceID, DBName). InstanceID = 0 means "any instance"; NULL is "unspecified" (also = any).
HealthCheck.AuditDictionary Benchmark provenance card Populated for canned audits (CIS, MVA). One row per ReportName.
HealthCheck.AuditMetricDictionary (not surfaced yet) Per-audit metric metadata: IsScored, Description. Useful for showing "CIS calls this control X".
HealthCheck.Work (transient) Per-run job packet populated by ReportMaster. Deleted at end of run.
HealthCheck.ReportLog Run & History grid rows Header rows written by each metric SP with FailCount / PassCount / ExecutionTimeInSecs per (run × metric).
HealthCheck.ReportLogCurrent (view over ReportLog) Convenience view of recent runs.
HealthCheck.MetricLog* (drill-down) One table per metric type — row-level pass/fail detail. Names line up with HealthCheck.Metric.LogTable.

Metric library snapshot

The shipped metric SPs cluster into three families.

Instance-level Metric* SPs

Per-instance checks — evaluate once per SQL Server.

  • HealthCheck.MetricSysadminCount
  • HealthCheck.MetricSpConfigureSetting (parameterized — used for many sp_configure toggles)
  • HealthCheck.MetricSARenamed
  • HealthCheck.MetricLoginDisabled (used for sa disabled check)
  • HealthCheck.MetricServerAuthentication
  • HealthCheck.MetricHideInstanceYes
  • HealthCheck.MetricTempdbFileCount
  • HealthCheck.MetricTempDBOneDataFile
  • HealthCheck.MetricLatestServicePack
  • HealthCheck.MetricSQLLoginsSecurity (MUST_CHANGE / CHECK_POLICY / CHECK_EXPIRATION)

Database-level Metric* SPs

Per-database checks — evaluate once per DB on each in-scope instance.

  • HealthCheck.MetricDatabaseOwner
  • HealthCheck.MetricTrustworthy
  • HealthCheck.MetricContainedAUTOCLOSEOff
  • HealthCheck.MetricContainedNoSQLAuth
  • HealthCheck.MetricDataLogFilesLocations
  • HealthCheck.MetricDBPrincipalPerm
  • HealthCheck.MetricDropOrphanedUsers
  • HealthCheck.MetricCLRAssemblyPermission

VA* SPs (Microsoft Vulnerability Assessment mappings)

Direct rule-number mappings to the checks SSMS's Vulnerability Assessment produces. VA1020, VA1043, VA1044, VA1048, VA1051, VA1054, VA1070, VA1095VA1099, VA1102, VA1143, VA1219, VA1221, VA1222, VA1224, VA1244VA1248, VA1256, VA1265, VA1267, VA1281, VA1282, VA2020, VA2033, VA2108, VA2109, VA2128, VA2129.

Each VA* SP is a Microsoft-defined check translated to run against Minion's Collector.* data.

Stored procedures

SP UI callsite Purpose
HealthCheck.ReportMaster ▶ Run now button on Run & History The real orchestrator. Resolves cascading thresholds, populates Work, executes each metric SP in the profile, writes ReportLog and per-metric MetricLog* rows.
HealthCheck.CloneReport Clone button on left rail Copies a Report row + its ReportMetric links into a new name. Threshold rules are NOT copied.
HealthCheck.Results (Reports tab consumes this) Retrieves per-metric results for a given (ReportName, ExecutionDateTime).
GUI.ChooseMetrics (legacy — older UI used this) Returns metrics linked to a profile. New UI queries ReportMetric directly.
GUI.HCCustomizeMetrics (legacy) Returns per-metric customization data. New UI queries MetricThreshold directly.
GUI.HCMetricOperators Op dropdown in the Thresholds add-row form Returns valid comparison operators (=, <>, <, <=, >, >=, IS, LIKE, /).
GUI.SLAList Service level dropdown on Scope tab Returns the SLA values ((All), Bronze, Silver, Gold) from dbo.Servers.ServiceLevel.

There's also HealthCheck.RunReport — this is a stale stub referencing tables (ReportElement, Element) that no longer exist. The Compliance UI intentionally does NOT call it. Use ReportMaster instead.

Per-metric log tables

Each metric writes row-level results to its own HealthCheck.MetricLog* table. The table name is stored in HealthCheck.Metric.LogTable. Examples:

Metric Log table
MetricSysadminCount HealthCheck.MetricLogSysadminCount
MetricDatabaseOwner HealthCheck.MetricLogDatabaseOwner
MetricSpConfigureSetting HealthCheck.MetricLogSpConfigureSetting (all sp_configure metrics share this table, keyed by MetricName)
VA1020 HealthCheck.MetricLogVA1020

Every log table has a common preamble of columns:

  • ReportExecutionDateTime — the run identifier
  • InstanceID (+ often ServerName via join)
  • MetricExecutionDateTime
  • ThresholdMeasure / ThresholdOperator / ThresholdValue (the resolved rule that graded this row)
  • MetricFail (bit — 1 = failed, 0 = passed)
  • CollectorExecutionDateTime (which collector snapshot the SP graded)

Metric-specific columns follow, such as SysadminName + SysadminCount on MetricLogSysadminCount.

Sample queries

Every failing row in a run

DECLARE @Run datetime = '2026-07-06 14:30:22';

SELECT ML.MetricName,
       ML.LogTable,
       ML.FailCount
FROM HealthCheck.ReportLog ML
WHERE ML.ReportExecutionDateTime = @Run
  AND ML.FailCount > 0
ORDER BY ML.FailCount DESC;

Follow up with a SELECT ... FROM <LogTable> WHERE ReportExecutionDateTime = @Run AND MetricFail = 1 for each.

Show the resolved threshold for one row

SELECT TOP 1
    L.InstanceID,
    S.ServerName,
    L.DBName,
    L.ThresholdMeasure,
    L.ThresholdOperator,
    L.ThresholdValue,
    L.MetricFail
FROM HealthCheck.MetricLogSysadminCount L
JOIN dbo.Servers S ON S.InstanceID = L.InstanceID
WHERE L.ReportExecutionDateTime = '2026-07-06 14:30:22'
  AND S.ServerName = 'Prod-DB-01';

If ThresholdOperator = <= and ThresholdValue = 8, the Instance-level override for Prod-DB-01 is what graded this row.

Profile-level pass/fail rollup for one run

SELECT MetricName,
       SUM(FailCount) AS Failed,
       SUM(PassCount) AS Passed,
       CASE WHEN SUM(FailCount) = 0 THEN 'OK' ELSE 'FAIL' END AS Result
FROM HealthCheck.ReportLog
WHERE ReportName = 'Acme Prod CIS'
  AND ReportExecutionDateTime = '2026-07-06 14:30:22'
GROUP BY MetricName
ORDER BY Failed DESC, MetricName;

Trend a metric over the last 30 days

SELECT ReportExecutionDateTime, FailCount, PassCount
FROM HealthCheck.ReportLog
WHERE ReportName = 'Acme Prod CIS'
  AND MetricName = 'Sysadmin Count'
  AND ReportExecutionDateTime >= DATEADD(day, -30, GETDATE())
ORDER BY ReportExecutionDateTime DESC;

Collector dependency

Every metric SP reads from at least one Collector.* table. The collector job's cadence therefore governs how fresh your compliance results can be. Common collector tables the metric SPs read:

Collector table Feeds metrics
Collector.Logins Sysadmin count, sa disabled, sa renamed, MUST_CHANGE, CHECK_POLICY
Collector.DBProperties Database owner, Trustworthy, Auto Close, Auto Shrink, Read Only
Collector.InstanceConfig sp_configure toggles, Server Authentication mode
Collector.DBFileProperties Data/Log file locations, TempDB file count
Collector.DBPrincipalPermissions GUEST access, DB principal permission checks
Collector.ErrorLogFiles Error log file count metric

If a metric returns Pass = 0 AND Fail = 0, that generally means the collector table for that metric is empty or hasn't been populated since the profile started referencing it.

Related: Compliance overview, Compliance thresholds, Compliance run & history, Reports.