Weekly network compliance regression report with n8n and rConfig MCP
A compliance dashboard shows where you are. The Monday question is different: what got worse since last week, what got fixed, and what quietly stopped being checked? This n8n workflow takes a snapshot of rConfig’s compliance results every week, stores it, compares it with the previous snapshot, and posts a short report that leads with regressions.
Best for: team leads, security and compliance owners, weekly operations reviews, showing remediation progress to auditors.
Tools: rConfig, self-hosted n8n with an AI Agent and the MCP Client Tool, a Postgres database, Slack or Microsoft Teams.
rConfig tools used: get_devices, get_compliance_status.
rConfig docsPolicy Compliance ResultsComprehensive guide to understanding, interpreting, and acting on policy compliance results in rConfig.Read the docs
How it flows
flowchart LR
T["Schedule Trigger<br/>Mon 07:00"] --> A["AI Agent<br/>+ rConfig MCP Client Tool"]
A --> I["Postgres<br/>insert this week"]
I --> L["Postgres<br/>load last week"]
L --> C["Code<br/>compare snapshots"]
C --> F{"Anything to report?"}
F -- "yes" --> P["Slack / Teams post"]
F -- "no" --> Q["Post: no regressions"]
The snapshot table
CREATE TABLE compliance_snapshots ( taken_on date NOT NULL, device text NOT NULL, device_group text NOT NULL, policy text NOT NULL, result text NOT NULL, -- pass | fail | not_evaluated failed_rules text[] NOT NULL DEFAULT '{}', PRIMARY KEY (taken_on, device, policy));The workflow
-
Schedule Trigger: weekly, Monday 07:00 in your timezone.
-
AI Agent, with a chat model and an MCP Client Tool connected to rConfig as in the n8n guide. Prompt:
Using rConfig, get the compliance status for each of these groups: Core, Distribution, Access, WAN. For every device and every policy assigned to it, return one row. Output only a JSON array:
[{"device", "device_group", "policy", "result": "pass" | "fail" | "not_evaluated", "failed_rules": [<rule names>]}]. If rConfig’s tools fail, output{"error": "<detail>"}.Turn on Require Specific Output Format with a matching schema, so a malformed answer fails the run instead of reaching the database.
-
Postgres (Insert), named Insert this week: write each row with
taken_onset to today. -
Postgres (Execute Query), named Load last week: load the previous snapshot.
SELECT * FROM compliance_snapshotsWHERE taken_on = (SELECT max(taken_on) FROM compliance_snapshots WHERE taken_on < CURRENT_DATE); -
Code: compare the two snapshots.
Compare snapshots (Code node) const key = (r) => `${r.device}|${r.policy}`;const now = new Map($("Insert this week").all().map((i) => [key(i.json), i.json]));const prev = new Map($("Load last week").all().map((i) => [key(i.json), i.json]));const regressions = [], fixed = [], stoppedEvaluating = [], newRows = [];for (const [k, r] of now) {const p = prev.get(k);if (!p) { newRows.push(r); continue; }if (p.result === "pass" && r.result === "fail") regressions.push(r);if (p.result === "fail" && r.result === "pass") fixed.push(r);if (p.result !== "not_evaluated" && r.result === "not_evaluated") stoppedEvaluating.push(r);}const removed = [...prev.keys()].filter((k) => !now.has(k));return [{ json: { regressions, fixed, stoppedEvaluating, newRows, removed: removed.length } }]; -
If: any regressions or devices that stopped being evaluated.
-
Slack or Microsoft Teams: post the report. Lead with regressions, then devices no longer evaluated, then fixes.
An illustrative post:
Compliance, week of 9 Nov (vs 2 Nov)Regressions (3)• dub-acc-14 Access baseline: "No telnet on VTY" now failing• lon-dist-02 SNMP hardening: "SNMPv3 only" now failing• lon-dist-03 SNMP hardening: "SNMPv3 only" now failingNo longer evaluated (1)• ams-wan-01: no backup since 4 NovFixed (5): dub-acc-02, dub-acc-07, dub-acc-09 (Access baseline), ams-core-01, ams-core-02 (NTP)Limits to know
- Compliance is only as fresh as the last evaluation. A device that wasn’t backed up or evaluated this week shows as “not evaluated”, which is why that list is in the report.
- Large groups mean large answers. A group of several hundred devices with several policies each produces a long JSON array. Run one workflow per group if the model’s output limit cuts it short, and check the row count against the device count.
- Policy renames look like removals. Renaming a policy produces a block of “removed” and “new” rows in the first week. Expect it, and say so in the post.
Guardrails
- The workflow’s rConfig token belongs to a service user whose role covers the reported groups only.
- The AI Agent has the rConfig MCP Client Tool and nothing else. Posting is a normal n8n node, not a tool the agent can call.
- The database holds compliance results and rule names, not configuration text.
Variations
- Per-customer reports. For an MSP, one workflow per customer, each with a token for a user scoped to that customer’s devices, posting to that customer’s channel.
- Quarterly evidence. The table is a history of weekly compliance. A quarterly query over it gives the trend an auditor asks for, without re-running anything.
- Escalate stubborn failures. Add a query for rows failing for four consecutive weeks, and open a ticket for each.
Related reference
- Policy Compliance Results
- Policy Assignments
- Compliance overview
- Connect an AI agent over MCP
- n8n
- Audit evidence pack
Frequently asked questions
Doesn't rConfig already report compliance?
Yes, as of now. It doesn't tell you what got worse since last week. This workflow keeps a weekly snapshot and reports the difference, which is what a team lead or auditor usually asks about.
Why does a Code node compare the snapshots, not the AI agent?
Comparing two lists is exact work that a script does reliably every time. The agent collects the data and, optionally, writes the summary; the comparison that decides what is a regression stays deterministic.
Can this run on n8n Cloud?
Only if rConfig's /mcp endpoint is reachable from the internet, because n8n Cloud connects from n8n's infrastructure. A self-hosted n8n inside your network reaches a private rConfig directly.