A self-hostable, git-native dashboard for Terraform resource status.
280
████████╗███████╗██████╗ ██████╗ █████╗ ██╗ ██╗██╗███████╗██╗ ██╗
╚══██╔══╝██╔════╝██╔══██╗██╔══██╗██╔══██╗██║ ██║██║██╔════╝██║ ██║
██║ █████╗ ██████╔╝██████╔╝███████║██║ ██║██║█████╗ ██║ █╗ ██║
██║ ██╔══╝ ██╔══██╗██╔══██╗██╔══██║╚██╗ ██╔╝██║██╔══╝ ██║███╗██║
██║ ███████╗██║ ██║██║ ██║██║ ██║ ╚████╔╝ ██║███████╗╚███╔███╔╝
╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚══════╝ ╚══╝╚══╝

A self-hostable, git-native dashboard for Terraform resource status.
Parse HCL + state (+ optional plan), classify every resource, and browse it in a live web UI.
Terraview reads your Terraform project — .tf files, state backend, and optionally a JSON plan — then renders a live status grid grouped by cloud provider and service type. No SaaS account required: run a single binary locally, in Docker, or behind your CI pipeline.
Backend: Go engine + HTTP API + background poller
Frontend: Next.js dashboard (shadcn / radix-sera preset)
| Pain | Terraview's answer |
|---|---|
terraform state list is a flat text dump | Visual grid grouped by provider › service (or module) |
| GUI tools are often SaaS or enterprise-only | Self-hosted binary; optional basic auth |
| Hard to see pending vs applied at a glance | Eight lifecycle statuses with filters and summary chips |
Drift only visible after terraform plan | Plan JSON resource_drift surfaced as drifted status |
| Sharing infra status with non-engineers | Shareable filter URLs; export JSON/CSV |
.tf files and terraform.tfstateplan_file (terraform show -json) for pending changes and driftplan_action and drift_attributes on each resource when a plan is loadedstate_serial and state_modified_at on snapshots (local backend mtime)created, inactive, pending_create, pending_update, pending_destroy, drifted, unmanaged, unknowndefault, dev, staging, prod (and remote workspaces) from the UI without restarting the server; snapshots are cached per workspacedepends_on and HCL references; toggle Graph view in the dashboardterraview status prints JSON or Markdown; exit code 2 when drift is detected?status=drifted&provider=AWS)terraform.tfstate.d/<workspace>/ locally or env:/<workspace>/ on remote backends/api/status@thesvg/cli (group headers, rows, provider chips)#resource=aws_instance.web opens the detail panelCtrl+K / ⌘K to jump to any resource/ search, r refresh, Esc clear filters, ? help| Endpoint | Description |
|---|---|
GET /api/health | Liveness + version |
GET /api/snapshot | Full snapshot (resources, summary, UI config, dependency graph) |
GET /api/workspaces | List Terraform workspaces + active workspace |
POST /api/workspace | Switch active workspace ({"workspace":"dev"}) |
GET /api/graph | Dependency graph for the active workspace |
GET /api/resources | Filtered resource list (?status=&provider=&module=&category=&tag=&q=&limit=&offset=) |
GET /api/resource | Single resource by address (?address=aws_instance.web) |
GET /api/facets | Filter facet counts (optionally pre-filtered) |
GET /api/summary | Aggregate counts only |
GET /api/status | Compact headline for badges / CI |
POST /api/refresh | Force refresh |
GET /api/events | SSE stream (refreshed events) |
POST /api/login | Exchange credentials for session token (when auth enabled) |
git clone https://github.com/NotHarshhaa/terraview
cd terraview
go run ./cmd/terraview serve ./testdata/sample-project
# API + UI (if ui/out exists) → http://localhost:7777
Use two terminals — the UI proxies /api/* to the Go server via Next.js rewrites (no CORS setup needed):
# Terminal 1 — API
go run ./cmd/terraview serve ./testdata/sample-project --no-ui
# Terminal 2 — UI
cd ui && npm install && npm run dev
# → http://localhost:3000
Or use the Makefile:
make run # build + serve sample project on :7777
make ui-dev # Next.js on :3000 (requires API on :7777)
make test # go test ./...
docker compose up --build
# or pull the published image:
docker pull ghcr.io/notharshhaa/terraview:latest
docker run -p 7777:7777 -v "$(pwd):/workspace" ghcr.io/notharshhaa/terraview:latest
Images are built and published to GHCR when you:
git tag v0.1.0 && git push origin v0.1.0 (publishes v0.1.0, 0.1.0, 0.1, and latest)sha-<commit>; optionally check Also tag as latest)Use the published Terraview Status Check GitHub Action (Marketplace) or the CLI directly.
GitHub Actions:
permissions:
contents: read
pull-requests: write
jobs:
terraview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: NotHarshhaa/terraview-action@v1
with:
working-directory: ./infra
mode: status-check # also: drift-gate, destroy-guard, summary-report
plan-file: ./plan.json
# backend: s3
| Mode | Behavior |
|---|---|
status-check | Post/update PR comment with resource table (read-only) |
drift-gate | Fail when drift is detected (exit 2) |
destroy-guard | Fail when any pending_destroy resources exist |
summary-report | Write JSON/HTML report files for pipeline artifacts |
See the terraview-action README for full inputs and workflow examples.
CLI-only CI (no Action wrapper):
terraview status ./infra --format markdown
terraview status ./infra --plan-file ./plan.json # includes pending + drift
# Exit 2 if any resource is drifted
Each resource is classified from HCL declarations, state, and plan:
In state?
├── NO → in plan as create? → pending_create
│ declared in .tf? → unmanaged
│ else → unknown
└── YES → in plan?
│ ├── delete → pending_destroy
│ ├── update/replace → pending_update
│ └── create → pending_create
drift in plan? → drifted
provider inactive? → inactive
else → created
Drift detection reads the resource_drift section from a Terraform plan JSON file. Pass it via plan_file in config or --plan-file on the CLI.
Copy .terraview.yaml.example to your project root. All fields are optional.
port: 7777
poll_interval: 30s
working_dir: .
plan_file: ./plan.json # optional: terraform show -json output
backend:
type: local # local | s3 | gcs | azureblob | tfc
# S3: bucket, key, region, dynamodb_table, endpoint
# GCS: bucket, key
# Azure: storage_account, container, key
# TFC: organization, workspace, token, hostname
ui:
title: "My Project — Infrastructure"
show_cost_column: false # reserved for future Infracost integration
default_filter: status=created
auth:
enabled: false
username: admin
password_env: TV_PASSWORD
access_token: secret-token # Bearer / ?access_token= for SSE
| Variable | Default | Description |
|---|---|---|
TV_PORT | 7777 | HTTP port |
TV_POLL_INTERVAL | 30s | Snapshot refresh interval (min 5s) |
TV_WORKING_DIR | . | Terraform project root |
TV_BACKEND | local | Backend type |
TV_STATE_BUCKET | — | S3/GCS bucket |
TV_STATE_KEY | — | State object key |
TV_STATE_REGION | — | AWS region (S3) |
TV_STATE_FILE | — | Explicit local state path |
TV_PLAN_FILE | — | Plan JSON path |
TV_UI_TITLE | Terraview | Dashboard title |
TV_PASSWORD | — | Basic auth password |
TV_ACCESS_TOKEN | — | Static API token (SSE-friendly) |
TFE_TOKEN | — | Terraform Cloud token |
When auth.enabled: true, the API accepts:
username / password)access_token or session token from login)?access_token= query param (required for browser EventSource / SSE)POST /api/loginThe UI stores credentials in sessionStorage and shows a login form on 401.
| Backend | Status | Notes |
|---|---|---|
Local (terraform.tfstate) | Supported | Default; also checks .terraform/terraform.tfstate |
| Amazon S3 | Supported | AWS SDK v2; optional S3-compatible endpoint |
| Google Cloud Storage | Supported | Application Default Credentials |
| Azure Blob Storage | Supported | DefaultAzureCredential |
| Terraform Cloud / HCP | Supported | HTTP API; set TFE_TOKEN or backend.token |
See CONTRIBUTING.md. High-impact areas:
git clone https://github.com/NotHarshhaa/terraview
cd terraview
go test ./...
go vet ./...
cd ui && npm run typecheck && npm run build
devops-project-generator — scaffold DevOps project structuresterraform-cost-estimator — cost estimation for Terraform plansjenkins-plus — batteries-included Jenkins with modern UIApache 2.0 — see LICENSE
Content type
Image
Digest
sha256:502283e5e…
Size
17.2 MB
Last updated
4 months ago
docker pull harshhaareddy/terraview