Skip to content

Post-Synth Check Guide

Post-synth checks validate the serialized output after the build pipeline completes. They catch issues in the generated templates that pre-synth rules (which operate on TypeScript AST) cannot detect.

import type {
PostSynthCheck,
PostSynthContext,
PostSynthDiagnostic,
} from "@intentius/chant/lint/post-synth";
export const myCheck: PostSynthCheck = {
id: "MYD010",
description: "Human-readable description of what this check validates",
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
const diagnostics: PostSynthDiagnostic[] = [];
for (const [_lexicon, output] of ctx.outputs) {
// Parse the output (JSON, YAML, etc.)
// Iterate resources
// Push diagnostics for violations
}
return diagnostics;
},
};

The ctx parameter provides:

  • ctx.outputsMap<string, string | SerializerResult> of serialized outputs per lexicon
  • ctx.entitiesMap<string, Declarable> of all declared entities
  • ctx.buildResult — Full build result including warnings and errors

Each diagnostic requires:

{
checkId: "MYD010", // Matches the check ID
severity: "warning", // "warning" | "error"
message: "Human-readable message with resource name",
entity: "resourceName", // Optional: the resource that triggered it
lexicon: "my-lexicon", // Optional: lexicon identifier
}

Organize checks into categories for clarity:

Encryption, TLS, HTTPS, access control, identity configuration.

// Example: Missing encryption
if (!props.encryption) {
diagnostics.push({
checkId: "MYD015",
severity: "warning",
message: `Resource "${name}" has no encryption — enable encryption to protect data at rest`,
});
}

Required fields, valid values, correct dependencies.

// Example: Missing required field
if (!resource.apiVersion) {
diagnostics.push({
checkId: "MYD011",
severity: "error",
message: `Resource "${name}" is missing apiVersion`,
});
}

Naming conventions, tagging/labeling, resource configuration.

// Example: Redundant dependency
if (propertyRefs.has(depName)) {
diagnostics.push({
checkId: "MYD010",
severity: "warning",
message: `Resource "${name}" has redundant dependency on "${depName}"`,
});
}

Outdated API versions, legacy features.

// Example: Old API version
if (apiDate < deprecationThreshold) {
diagnostics.push({
checkId: "MYD012",
severity: "warning",
message: `Resource "${name}" uses outdated apiVersion`,
});
}

Every check should have both a positive (flagged) and negative (clean) test:

import { describe, test, expect } from "bun:test";
import { createPostSynthContext } from "@intentius/chant-test-utils";
import { myCheck } from "./my-check";
describe("MYD010: My Check", () => {
test("flags when condition is violated", () => {
const ctx = createPostSynthContext({
"my-lexicon": { /* template with violation */ },
});
const diags = myCheck.check(ctx);
expect(diags).toHaveLength(1);
expect(diags[0].checkId).toBe("MYD010");
});
test("passes when condition is satisfied", () => {
const ctx = createPostSynthContext({
"my-lexicon": { /* template without violation */ },
});
const diags = myCheck.check(ctx);
expect(diags).toHaveLength(0);
});
});

These patterns apply to most IaC formats:

PatternDescription
Missing encryptionData at rest should be encrypted
Overly permissive accessWildcard rules, public access, admin credentials
Deprecated versionsOld API versions, deprecated features
Missing identity/authNo managed identity, no RBAC, shared credentials
Public access enabledResources exposed to the internet unintentionally
Missing diagnosticsNo logging, monitoring, or audit trail
Missing TLS/HTTPSUnencrypted transport
Missing network isolationNo NSG, no network policy, no firewall rules

Return checks from your plugin’s postSynthChecks() method:

postSynthChecks(): PostSynthCheck[] {
const { myCheck010 } = require("./lint/post-synth/check010");
const { myCheck011 } = require("./lint/post-synth/check011");
return [myCheck010, myCheck011];
}

A mature lexicon should have at least 15 post-synth checks covering all four categories (security, correctness, best practices, deprecation). The K8s and Azure lexicons each have 20 checks.