Repository navigation
Releases
v0.1.25
Breaking
-expect-from-baselineno longer skips baseline scopes of the wrong shape (empty or over-long value, malformed key, too many keys) silently: they are counted and add a warning, so the run is not complete and-strictexits 1. Write a new baseline if the old one holds such scopes.
Changes
- The integration tests, and the CI matrix, now cover Terraform 1.14.9 as well as 1.15 and 1.16 (README updated). Product code for 1.14 is unchanged.
Changelog
- 8abcb68: Run integration tests on Terraform 1.14, warn on baseline scopes that cannot be expected (#65) (@NikitaPuglachenko)
v0.1.24
Plugin stderr is passed on as before but bounded: up to 4 MiB, the rest is dropped with one line saying so. The Vault example plugin adds VAULT_NAMESPACE to the target, so the same policy name in two namespaces of one server is two objects for -baseline. -expect-from-baseline scopes that did not fit are a warning, so -strict fails instead of passing on scopes that were never expected.
Breaking:
- Plugin stderr beyond 4 MiB is dropped.
- A baseline whose scopes did not fit (more than 10000, or together with
-expect) now makes the run incomplete. - Vault baselines written with
VAULT_NAMESPACEset need to be written again (their target had no namespace).
Changelog
- 748a6b2: Bound plugin stderr, tell Vault namespaces apart, warn on dropped baseline scopes (#64) (@NikitaPuglachenko)
v0.1.23
Covers the unpublished v0.1.22 as well.
-state reads S3: s3://BUCKET/KEY for one state, s3://BUCKET/PREFIX/ for every *.tfstate under a prefix, with the usual AWS credentials. A listing's scope now follows the list block's arguments, so -expect and -baseline no longer confirm the provider's own scope for a listing that ran elsewhere.
Baselines tell servers apart: findings carry a target (host and port, never a URL or credentials; set by plugins, vault.sh from VAULT_ADDR, -scan proxmox from PROXMOX_VE_ENDPOINT), and the same ID on another server is not the same object. -expect-from-baseline expects the scopes the baseline's complete listings checked. summary.coverage_level, summary.unqueried_types, summary.refresh_failed and summary.unclassified_terragrunt say how complete a run was; new flags -strict-unlisted, -terragrunt-parent DIR and -fail-unverified. -scan output and the temporary state are size-bounded.
Breaking:
-strictfails when-refreshfell back to the saved state, andadoptrefuses after such a fallback (-allow-incompletelifts it).-strictwith-terragruntneeds-terragrunt-parent.-source-timeoutis 30m by default (was unlimited; 0 disables it); an automatic config can only lower it.- A missing Vault or Proxmox finding is
unverified(target_changed) after the source moved to another server, instead of resolved. - After a failed
terraform query, list blocks that never started are reported infailed_queries("did not start"). - A missing baseline finding of a type left out by
-typesor-exclude-typesstays unverified (type_filtered). - Listing addresses and for_each keys show
-argvalues containing://by position (endpoint=#1); more than 10000 combinations of-argvalues for one list block is an error.
Changelog
- 7703ecc: Reduce CI flakes: retry terragrunt download, wait for stderr tail in test (#58) (@NikitaPuglachenko)
- 4449173: Retry provider downloads in the integration tests (#59) (@NikitaPuglachenko)
- a940771: Build with Go 1.27.4 (GO-2026-6859) (#60) (@NikitaPuglachenko)
- 733d879: Bound -scan output and state, fail -refresh fallback, add -fail-unverified (#61) (@NikitaPuglachenko)
- ea739af: Add strict modes for unlisted types and unclassified Terragrunt parents (#62) (@NikitaPuglachenko)
- eb824c8: Tell servers apart in a baseline, expect baseline scopes, default source timeout (#63) (@NikitaPuglachenko)
v0.1.21
New -expect "KEY=V[,V...] [KEY=V...]": name the accounts, projects, regions or zones a run must reach. A scope that no complete listing confirms makes coverage incomplete (exit 1 with -strict); see summary.expected and the Expected scopes block. No breaking changes.
Changelog
- 420372c: Add -expect: scopes a run must confirm (#55) (@NikitaPuglachenko)
v0.1.20
Fixes from a UX audit: help, error messages, text output, adopt, init, doctor, diff and the docs.
-hand-versionprint to stdout (tforphans -h | less).-hlists flags in groups, with examples.- A wrong flag prints one error line and
Run "tforphans -h" for usage.instead of the whole usage, for every command.difftreats everything after--as paths. - A current directory without Terraform configuration and no other source (
ROOT_DIR,-state,-state-roots,-scan, plugin) is an error instead of a run that lists nothing. - A
ROOT_DIRor-state-rootsdirectory that does not exist is named before terraform runs; a misplaced or misspelt command gets a hint (did you mean tforphans doctor?). -max-roots,-timeoutand-source-timeoutreject negative values (0means no limit).No unmanaged resources found.is printed only with complete coverage; otherwise the line says coverage is incomplete. A run that checked nothing prints no result on stdout.- With
-baselinethe findings block isNew unmanaged resources, and theBaselineline counts not verified findings by reason. - Zero counts are no longer colored; the summary counts plugins run; warnings in the coverage line point to Diagnostics.
- Names sort in natural order (
policy-2beforepolicy-10). - Write errors of the text output and of
-print-queryfail the run. adoptexplains an existing-ofile and repeats an incomplete coverage warning next to what it wrote.initnames directories that have configuration but no backend and shows the command to pass them.doctorshows why the ignore file could not be parsed (file and line).docs/json.mdexplains the terms (orphansare unmanaged resources); JSON keys andschema_version1 are unchanged.
Breaking (text output and input errors only):
- Help and
-versiongo to stdout instead of stderr. - An empty current directory, a missing
ROOT_DIRor-state-rootsdirectory and negative limits are errors (exit 1). - With
-baseline, the findings heading and the not verified line inBaselinechanged. - The natural sort order of names can change which object gets an
_2suffix inadopt.
Changelog
- 3c9dddb: Fix findings of the 2026-10-30 UX audit (#54) (@NikitaPuglachenko)
v0.1.19
Fixes from a project audit, and an internal restructuring of the CLI package.
- Config files (
.tforphans.yaml,-config,init -update) are read only when they are regular files of at most 1 MiB. A named pipe or a device in their place hung the run before-timeoutapplied. - Secret
-argvalues (names with token, secret, password, credential or a key component) no longer appear in the instance keys of generated list blocks, and so not insummary.listings[].addressorlist_address: the key names the value by its position (api_token=#2). - Temporary files written into a root are created with mode 0600: list blocks hold the
-argvalues. doctorignores aformatfrom a shared config that only the audit has (summary), instead of failing.adopt -oremoves the file it created when writing fails.init -updatenames astate-rootsdirectory that no longer exists and suggests passing the directories as arguments.- The release job no longer passes the tap token to GoReleaser.
- The CLI moved to
internal/cliand was split into files and packages (#47-#52).go install github.com/5xxeu/tforphans@latestand the binaries are unchanged.
Breaking: for list blocks with a secret -arg, the listing address changes, so a -baseline from an earlier version reports their resolved findings as not verified once.
Changelog
- 602f788: Move the CLI package to internal/cli (#47) (@NikitaPuglachenko)
- b10e9f5: Split internal/cli main.go into files by stage (#48) (@NikitaPuglachenko)
- e661578: Group options by the commands that read them (#49) (@NikitaPuglachenko)
- d08a392: Move the diff command to its own package (#50) (@NikitaPuglachenko)
- 5f2b5d9: Split doctor.go into files by concern (#51) (@NikitaPuglachenko)
- 64d7387: Split output.go: hints, sorting and discovery checks (#52) (@NikitaPuglachenko)
- 0e2b1ec: Fix findings of the 2026-10-30 audit (#53) (@NikitaPuglachenko)
v0.1.18
Readable verdicts in doctor and the audit report.
doctor: the result comes first (READY,BLOCKED,UNKNOWN) with counts; without-vonly problems (in the order they are fixed) and warnings are listed, and the same check in several roots is printed once. With failed checks the proposed command istforphans doctor ...again (keeping-offlineand-check-auth), not the audit. Roots used only for state count for the-scansuggestion; a root with no listings is a warning.- Without
ROOT_DIR, a current directory without Terraform configuration is no longer read as a root when-state,-state-rootsor-terragrunt-unitis given. - Text report: summary and coverage come before the findings; findings are grouped by scope, with the shared
zone_id/account_idin the heading instead of every row, and a source shared by all findings is named once. Reason links only with-v; coverage separates what these sources cannot check from the gaps. initrecommends the scan adapter the roots' provider needs and prints theinit -update -scan ...command that enables it.- Cloudflare 403 bodies that name a missing entitlement or quota are
request_rejected. The Cloudflare provider 5.26 data sources return a bare 403 without the body, so there they staypermission_denied.
Breaking: text output only. The summary and coverage moved above the findings table, the table is grouped by scope, and the doctor text layout changed. Some Cloudflare failures move from permission_denied to request_rejected when the provider passes the API body. JSON and exit codes are unchanged.
Changelog
- b928a33: Put the verdict first in doctor and audit output (#46) (@NikitaPuglachenko)
v0.1.17
Fixes for init -update and the order of the text report.
init -update: search trees that explicit directory arguments leave out are listed asRemoved from state-rootsinstead of being dropped silently; a savedmax-rootsis read as the audit reads it ("5"no longer rejected); the config check and rename run under.tforphans.yaml.lock, so two updates at the same time cannot both write.- Text report:
Baselineand, with-v,Result scopescome beforeCoverage, which is the last line again; theDiagnosticsheading appears only with diagnostics. doctorreports a failed write of its headings.
Breaking: in the text report on stderr, the baseline: line and the scopes of -v moved above Coverage under their own headings. JSON is unchanged.
Changelog
- fbd4edc: Fix minor findings of the init, output and reason-codes reviews (#45) (@NikitaPuglachenko)
v0.1.16
tforphans init prepares .tforphans.yaml for a repository, and terminal reports are easier to read.
init: discovers roots from local configuration and lock files, shows provider sources, locked versions, aliases and backend types, and writesroots,state-rootsand the scan list to.tforphans.yaml.-updaterefreshes an existing file and keeps its scan list andmax-rootsunless given again,-dry-runprints the proposed file without writing it. Seedocs/init.md.- Text output: sections with headings, a two-line summary, blank lines between groups, and color on a terminal (off with
NO_COLOR,TERM=dumb, a pipe or Windows).
Breaking: scripts that parse the text output break: the summary is now two lines (... 1 unmanaged and N states read, ... instead of ...; N states read), stdout has section headings and blank lines between groups. Use -format json for machine-readable output.
Breaking: init as the first argument is now the command; audit a root named init with tforphans ./init or tforphans -- init.
Breaking: reasons of unread configuration (audit and init) no longer include the parser detail, which quoted the offending source and could print a secret from a malformed file; file, line and summary remain.
Changelog
- ec56e9c: Improve readability of terminal reports (#43) (@NikitaPuglachenko)
- fcce255: Add init: discover roots and create the config (#44) (@NikitaPuglachenko)
v0.1.15
JSON reports now carry stable machine-readable reasons next to the human-readable ones: reason_code, an optional action and missing_keys (key names only, no values) in explanations, skipped types and unverified baseline findings; coverage adds reason_details. Codes are listed in docs/reasons.md, and text output links to them. The change is additive, schema_version stays 1.
Changelog
- dd30e40: Add stable reason codes to audit reports (#42) (@NikitaPuglachenko)