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.

  1. On the left rail, select CIS Audit.
  2. Click Clone. A new profile appears named CIS Audit (copy).
  3. Select the clone and open the Overview tab.
  4. 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.
  5. Fill in the Description and click Save changes.
  6. 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.
  7. Open the Thresholds tab. Add per-server tuning as needed — see Recipe 2.
  8. Open the Scope tab. Choose the SLA (usually Gold), uncheck any specific hosts you want to exclude.
  9. 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).

  1. Open the Thresholds tab.
  2. 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
  3. Click + Add. The row appears in the grid under the Sysadmin Count group with a blue Instance pill.
  4. 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.

  1. 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
  2. + 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.

  1. Click + New. A profile named New profile (or similar) appears.
  2. Overview tab: set Description, leave IsAudit off (this isn't an external benchmark), click Save changes.
  3. Metrics tab, use the Available list's search box: - Type sa — click + Add on Disable the sa Login Account and Rename the sa Login Account. - Type sysadmin — add Sysadmin Count. - Type login — add MUST_CHANGE ON for All SQL Logins, CHECK_POLICY ON for All SQL Logins, Login Auditing for Both failed and successful logins. - Type weak — add Weak Password (VA1281 or similar depending on your version).
  4. Thresholds tab: leave empty for now — each metric's default operator will be used.
  5. Scope tab: pick your SLA, leave the checkboxes at their defaults.
  6. 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.

  1. Open the profile's Run & History tab.
  2. 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.
  3. Expand the Sysadmin Count band. Every past run's row for that metric stacks together, newest first.
  4. Scan the Fail column. A vertical run of red pills = recurring failures. A single red among greens = a one-off.
  5. 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?

  1. Run & History tab. Find last night's run band. Expand it.
  2. Find the Database owner is not SA row. Note the value in the Log table column — for this metric it's HealthCheck.MetricLogDatabaseOwner.
  3. 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;
  4. 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.

  1. Metrics tab.
  2. Find Default Trace Enabled in the "In this profile" list.
  3. Click Active — the button flips to Off.
  4. 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.

  1. Keep the shipped CIS Audit unchanged as your reference. Do NOT run it directly — clone it to Acme CIS Weekly, tune, and schedule.
  2. Build a fresh custom profile named Acme Daily Security following Recipe 4.
  3. 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.ReportName disambiguates.

Recipe 10 — Export a run for an auditor

Goal: hand an auditor the results of a specific Acme Prod CIS run.

  1. Run & History tab. Find the run in question.
  2. Right-click a column header → Column chooser → make sure Metric, Fail, Pass, Log table, and Source are visible.
  3. Right-click any row → depending on your MinionGrid setup you can export to XLSX via Save XLSX (see MinionGrid).
  4. For row-level detail, query each MetricLog* table by ReportExecutionDateTime and 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.