Skip to content

CSV

Write the module and module-call tables as CSV, for a spreadsheet, a quick grep, or a tool that does not read Parquet.

The csv sink writes the same two tables as the Parquet sink, with the same columns, as plain CSV with a header row.

1. Write the files

$ tfmetrics aggregate facts/ --offline --sink csv --out tables/
aggregated 2 repos (2 with Terraform) into 45 series
$ head -3 tables/module_calls.csv
repo,org,owner,commit,scanned_at,module_path,name,source,source_kind,address,version,pinned,count,for_each
acme/infra,acme,acme/platform,61f1659ced23a89c2ce11ca7611d3661976ed800,2026-10-21T21:54:19Z,live/prod,dns,git::https://github.com/acme/tf-dns.git?ref=v1.4.0,git,github.com/acme/tf-dns,v1.4.0,exact,false,false
acme/infra,acme,acme/platform,61f1659ced23a89c2ce11ca7611d3661976ed800,2026-10-21T21:54:19Z,live/prod,eks,terraform-aws-modules/eks/aws,registry,registry.terraform.io/terraform-aws-modules/eks/aws,19.21.0,exact,false,false
  • modules.csv: one row per module directory.
  • module_calls.csv: one row per module call.

Timestamps are RFC 3339 in UTC, booleans are true and false, and empty values (a missing version or backend) are empty cells.

2. Open it

Import either file into Google Sheets or Excel as-is; the header row becomes the column names. A pivot table on module_calls.csv with address as rows and version as columns shows every module's version spread at a glance.

3. Or query it

DuckDB, csvkit and pandas all read the files directly. Modules, resources and hygiene gaps per owner:

SELECT owner, count(*) AS modules, sum(resources) AS resources,
       sum(variables_untyped) AS untyped_vars, sum(parse_errors) AS parse_errors
FROM 'tables/modules.csv' GROUP BY owner ORDER BY owner;
┌─────────────────┬─────────┬───────────┬──────────────┬──────────────┐
│      owner      │ modules │ resources │ untyped_vars │ parse_errors │
│     varchar     │  int64  │  int128   │    int128    │    int128    │
├─────────────────┼─────────┼───────────┼──────────────┼──────────────┤
│ acme/networking │       1 │         2 │            1 │            0 │
│ acme/platform   │       4 │         7 │            1 │            0 │
└─────────────────┴─────────┴───────────┴──────────────┴──────────────┘

For history across runs, prefer Parquet: it keeps column types and is much smaller.