Compliance how-to recipes
Step-by-step workflows for common Compliance tasks. Each recipe assumes you're on the Compliance tab.
Recipe 1 — Clone CIS and tune for your fleet
Goal: run the CIS benchmark against your production servers with a few known exceptions.
- On the left rail, select CIS Audit.
- Click Clone. A new profile appears named
CIS Audit (copy). - Select the clone and open the Overview tab.
- Change the profile name intent via Description (rename in the UI isn't wired yet — see Profiles gotchas). If you actually want the row renamed:
sql -- Do this from Query Runner or SSMS UPDATE HealthCheck.Report SET ReportName = 'Acme Prod CIS' WHERE ReportName = 'CIS Audit (copy)'; UPDATE HealthCheck.ReportMetric SET ReportName = 'Acme Prod CIS' WHERE ReportName = 'CIS Audit (copy)'; UPDATE HealthCheck.MetricThreshold SET ReportName = 'Acme Prod CIS' WHERE ReportName = 'CIS Audit (copy)';Refresh the Compliance page. The rename shows up. - Fill in the Description and click Save changes.
- Open the Metrics tab. The clone starts with the full CIS metric set. Remove any that don't apply (e.g. TDE metrics if encryption is out of scope) by clicking the ✕ on each.
- Open the Thresholds tab. Add per-server tuning as needed — see Recipe 2.
- Open the Scope tab. Choose the SLA (usually
Gold), uncheck any specific hosts you want to exclude. - Open the Run & History tab and click ▶ Run now. Watch the status bar for a "Run complete" message and check the history grid for the new execution.
Recipe 2 — Add a per-server allowance
Goal: allow one server to legitimately have more sysadmins than the baseline permits.
Say the CIS baseline says Sysadmin Count <= 5, but on Prod-DB-01 you legitimately have 8 (app team + rotating DBAs).
- Open the Thresholds tab.
- In the add-row form:
- Metric: Sysadmin Count
- Instance: pick Prod-DB-01 from the dropdown
- DB: leave blank
- Op:
<=- Value:8- Measure / Note:Sysadmin Count- Violation: off - Comment:App team + 3 DBAs approved by security 2026-03-14 - Click + Add. The row appears in the grid under the
Sysadmin Countgroup with a blue Instance pill. - Any future run treats Prod-DB-01's sysadmin count as passing if it's ≤ 8. Every other server still uses the Default rule (
<= 5).
Recipe 3 — Add a per-DB exception across the fleet
Goal: accept Trustworthy = 1 on msdb on every server, but keep failing it elsewhere.
- Thresholds tab, add-row form:
- Metric: VA1102 — Trustworthy disabled
- Instance: leave blank
- DB: type
msdb(or pick it from the DB dropdown after picking any server, then clear Instance) - Op:=- Value:1- Violation: off - Comment:MSDB is legitimately Trustworthy - + Add. The row shows with an amber Database pill.
Effect: for every server, when the engine evaluates VA1102 on the msdb database, it uses this rule and passes. On every other DB, the Default rule applies.
Recipe 4 — Build a custom profile from scratch
Goal: a short weekly security spot-check with about eight metrics.
- Click + New. A profile named
New profile(or similar) appears. - Overview tab: set Description, leave IsAudit off (this isn't an external benchmark), click Save changes.
- Metrics tab, use the Available list's search box:
- Type
sa— click + Add onDisable the sa Login AccountandRename the sa Login Account. - Typesysadmin— addSysadmin Count. - Typelogin— addMUST_CHANGE ON for All SQL Logins,CHECK_POLICY ON for All SQL Logins,Login Auditing for Both failed and successful logins. - Typeweak— addWeak Password(VA1281 or similar depending on your version). - Thresholds tab: leave empty for now — each metric's default operator will be used.
- Scope tab: pick your SLA, leave the checkboxes at their defaults.
- Run & History tab: click ▶ Run now.
Recipe 5 — Compare a metric across many runs
Goal: figure out whether Sysadmin Count has been trending badly over the last month.
- Open the profile's Run & History tab.
- Drag the Metric column header into the group panel at the top of the grid. The grid regroups so each metric name becomes a band.
- Expand the
Sysadmin Countband. Every past run's row for that metric stacks together, newest first. - Scan the Fail column. A vertical run of red pills = recurring failures. A single red among greens = a one-off.
- Click a specific row to see its details. To see the actual servers that failed, run:
sql SELECT L.InstanceID, S.ServerName, L.SysadminName, L.SysadminCount, L.MetricFail FROM HealthCheck.MetricLogSysadminCount L JOIN dbo.Servers S ON S.InstanceID = L.InstanceID WHERE L.ReportExecutionDateTime = '<the run timestamp>' AND L.MetricFail = 1 ORDER BY L.SysadminCount DESC;
Recipe 6 — Investigate why a specific metric failed
Goal: last night's run says Database owner is not SA had 3 failures. Which DBs?
- Run & History tab. Find last night's run band. Expand it.
- Find the
Database owner is not SArow. Note the value in the Log table column — for this metric it'sHealthCheck.MetricLogDatabaseOwner. - Copy the run's
ReportExecutionDateTime(visible in the group header). Paste into Query Runner:sql SELECT L.InstanceID, S.ServerName, L.DBName, L.DBOwner, L.MetricFail FROM HealthCheck.MetricLogDatabaseOwner L JOIN dbo.Servers S ON S.InstanceID = L.InstanceID WHERE L.ReportExecutionDateTime = '2026-07-06 14:30:22.190' AND L.MetricFail = 1; - The three offending
(server, DB)pairs appear. Fix them (ALTER AUTHORIZATION ON DATABASE::...) or if the owner is intentional, add a Server + DB threshold rule per Recipe 2 that accepts the current owner.
Recipe 7 — Temporarily quiet a metric during maintenance
Goal: a scheduled maintenance window will legitimately trip Default Trace Enabled for two hours. You don't want to see that failure in the report.
- Metrics tab.
- Find
Default Trace Enabledin the "In this profile" list. - Click Active — the button flips to Off.
- When the maintenance is done, click Off — it flips back to Active.
The metric stays in the profile with all its threshold rules intact; runs simply skip it while it's Off. This is preferable to Remove + Re-add because the latter deletes and recreates the ReportMetric link (though thresholds persist across that too).
Recipe 8 — Reset a bad clone
Goal: you cloned CIS, made a mess of the metrics and thresholds, want to start over.
Two options.
Fast: delete the clone (Delete button on the left rail), then clone CIS again fresh.
Preserve the name:
1. On the messed-up clone, Metrics tab, click ✕ on every metric until the "In this profile" list is empty. (Threshold rules persist but become irrelevant.)
2. Optionally clear leftover threshold rows via Thresholds tab (Remove each) or from SQL:
sql
DELETE FROM HealthCheck.MetricThreshold WHERE ReportName = '<your profile>';
3. Use HealthCheck.CloneReport from SQL to copy CIS's metrics onto your existing profile without renaming:
sql
-- This uses the built-in Clone SP; the third param is a new name, so we
-- clone into a temp name and then merge back. Or simpler: just delete and re-clone.
The pragmatic path is really the Fast option above.
Recipe 9 — Two profiles for the same fleet, different aggressiveness
Goal: run a strict CIS Audit weekly, and a loose Weekly security spot-check daily on the same servers.
- Keep the shipped
CIS Auditunchanged as your reference. Do NOT run it directly — clone it toAcme CIS Weekly, tune, and schedule. - Build a fresh custom profile named
Acme Daily Securityfollowing Recipe 4. - Both profiles use the same Scope settings (Gold SLA, all servers). They're graded independently — a metric that appears in both profiles gets logged twice per run cycle (once per profile), which is fine because
ReportLog.ReportNamedisambiguates.
Recipe 10 — Export a run for an auditor
Goal: hand an auditor the results of a specific Acme Prod CIS run.
- Run & History tab. Find the run in question.
- Right-click a column header → Column chooser → make sure
Metric,Fail,Pass,Log table, andSourceare visible. - Right-click any row → depending on your MinionGrid setup you can export to XLSX via Save XLSX (see MinionGrid).
- For row-level detail, query each
MetricLog*table byReportExecutionDateTimeand export from Query Runner:sql SELECT * FROM HealthCheck.MetricLogSysadminCount WHERE ReportExecutionDateTime = '<run>'; SELECT * FROM HealthCheck.MetricLogDatabaseOwner WHERE ReportExecutionDateTime = '<run>'; -- ... one per metric the auditor cares about
Related: Compliance overview, Compliance profiles, Compliance metrics, Compliance thresholds, Compliance scope, Compliance run & history, Compliance backend.