Skip to content

Output Formats

Wes Key edited this page Sep 16, 2026 · 2 revisions

Choose a format with -format. Every format goes to stdout, so redirect it to a file in a pipeline.

Format For
text (default) people, and the console log. Coloured with -color
json the tf-snag run tab in Azure DevOps, or your own tooling
markdown a section on the pipeline run's Summary tab (##vso[task.uploadsummary])
sarif the SARIF SAST Scans Tab extension, and the baseline for the next run
junit the built-in Tests tab (PublishTestResults@2). Drift only
teams the Microsoft Teams card, printed instead of posted, to inspect or diff it

To produce several formats, run tf-snag once per format with -exit-code=false, then once more for the console and the exit code.

JSON

The report carries a schema version, currently 3: drift, deprecations, retirements and pending changes, ignore-rule suppression, per-finding file and line with -source, and with -baseline a baseline_state (new or updated) and first_seen timestamp on every finding. Findings tracked by a work item carry work_item and work_item_url.

Schema 3 added retirements — one object per catalogue entry, with its retires_on, urgency, days and the instances it matched — and retirement_catalogue, which says where the catalogue came from and how old it is. Both are present only when -check retirements ran. See Retirements.

JSON lists live findings only. Absent ones appear in SARIF.

SARIF

Drift is one result per resource under the rule resource-drift. The message leads with the kind:

Scans tab column From e.g.
severity icon level ⛔ delete or create · ⚠ update
Result message Deleted — name=kv01 · Updated — tags.Owner: null → "Wes"
Path physicalLocation, needs -source terraform/modules/kv/main.tf
Baseline baselineState see Baselines and Provenance

One changed attribute stays on the first line; two or more get a line each. Without a -source match, the Path is the resource address as plain text.

Deprecations are one result per distinct notice under the rule deprecation: summary on line one, detail on line two, then one address (file:line) line per location. The first location is the result's location, the rest are relatedLocations. The Scans tab shows the two rules as collapsible groups, ordered by result count.

Stable identity. Every result carries a deterministic guid (a UUID v5 of the finding's kind and identity) and a partialFingerprints entry (driftAddress/v1 or deprecation/v1). A finding keeps its guid from run to run, which is what baselines, work items and the run tab all match on.

Ignored findings stay in the file as result.suppressions, with kind inSource for inline comments or external for the ignore file. The Scans tab hides them unless you change its Suppression filter.

Text, markdown and JUnit

Ignored findings are listed separately (an ignored section in text, an ### Ignored table in markdown, a <skipped> case in junit) and never trip -exit-code.

Clone this wiki locally