Offline, read-only comparison of exported, normalized provider schemas against an exported configuration inventory. It never invokes Terraform/OpenTofu, loads a provider plugin, reads live state, plans, applies, provisions, or deletes anything. The exporter must produce complete evidence; this tool cannot verify completeness against a provider.
Requires Node.js 22 or later; no dependencies. Start from a fresh checkout:
git clone https://github.com/edilec/terraform-provider-schema-guard.git
cd terraform-provider-schema-guard
node bin/terraform-provider-schema-guard.mjs --root examples --input passing.json
node bin/terraform-provider-schema-guard.mjs --root examples --input failing.json
npm run check--root is the realpath-confined input directory. --input is a relative path within it. Optional --human writes one summary to stderr; JSON report only on stdout. No output file is written. Invalid usage/options: exit 2 with empty stdout. Unreadable/undecodable/unparseable/unsupported evidence: incomplete JSON and exit 2. Evaluated harmful changes: fail and exit 1. Fully evaluated compatible evidence: pass and exit 0. --help prints usage.
Top level: schemaVersion:"1", complete:true, before, after, configurations. Each schema side has complete:true, version (major.minor.patch with optional ASCII suffix), and resources. Each resource has type and fields; each field has name, type, required, computed, optional description. The configuration export has complete:true and items; each item has opaque id, type, and a list of configured attribute names. No configuration values are accepted. description is non-semantic and ignored; only field behavior is compared. Valid normalized types are string, number, bool, and list, set, or map of one of those primitives, e.g. list(string). Nested blocks, dynamic types, other provider-specific semantics, extra properties, duplicate names or IDs, and incomplete exports are incomplete, never pass. This is a normalized export, not raw terraform providers schema -json; an explicit trusted exporter must transform that format and mark completeness.
| Change | Outcome |
|---|---|
| New required field missing from a matching configuration | required-config-impact, fail 1, points to configuration ordinal |
| Removed resource used by a configuration | resource-config-impact, fail 1 |
| Removed or type-changed field referenced by a configuration | field-config-impact, fail 1 |
| Computed behavior change with configured reference | behavior-config-impact, fail 1 |
| Required field provided by all matching configurations, unused type/field changes, required-to-optional relaxation | informational pass 0 |
| Unsupported or incomplete schema/configuration, missing resource evidence, limits | incomplete 2 |
Renames are not inferred: a rename appears as removal plus addition. Provider versions are recorded in the provider report object. @export is a fixed logical source role for the --input file; JSON pointers identify schema/configuration ordinals. No raw resource names, field names, configuration IDs, values, or host paths are emitted. Findings sort by (location.file, location.pointer, ruleId) in UTF-16 code-unit order. A pass means the supported normalized shapes showed no evaluated harmful change; it does not guarantee a cloud plan is unchanged.
At most 1,048,576 input/document UTF-8 bytes; 100 resources per side; 1,000 fields total per side; 500 configuration items; JSON depth 6 from root depth 0; 5,000 ms using an injectable library clock. Exact N is accepted, N+1 incomplete. CLI file read has a 5-second abort. Strict UTF-8 and duplicate-key rejection (including escaped spellings) prevent partial/ambiguous evidence. No network, live provider discovery, external execution, automatic remediation, or semantic equivalence inference.
For the wider review workflow around modules, state, plans, and change control, see Edilec's infrastructure-as-code standards guide. This tool checks only the normalized exports described above.