030 Project 30: Terraform Risk Reporter

030 Build a Terraform Risk Reporter

Use the parsed plan to fail CI on risky deletes/replacements.

Full main.go

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type plan struct {
    ResourceChanges []struct {
        Address string `json:"address"`
        Change  struct {
            Actions []string `json:"actions"`
        } `json:"change"`
    } `json:"resource_changes"`
}

func hasAction(actions []string, x string) bool {
    for _, a := range actions {
        if a == x {
            return true
        }
    }
    return false
}

func main() {
    if len(os.Args) != 2 {
        fmt.Println("usage: tf-risk <plan.json>")
        os.Exit(2)
    }
    b, err := os.ReadFile(os.Args[1])
    if err != nil {
        panic(err)
    }
    var p plan
    if err := json.Unmarshal(b, &p); err != nil {
        panic(err)
    }

    risky := 0
    for _, rc := range p.ResourceChanges {
        a := rc.Change.Actions
        replace := len(a) == 2 && a[0] == "delete" && a[1] == "create"
        if hasAction(a, "delete") || replace {
            risky++
            fmt.Printf("RISK: %s actions=%v\n", rc.Address, a)
        }
    }
    fmt.Printf("risky changes=%d\n", risky)
    if risky > 0 {
        os.Exit(1)
    }
}

Step-by-Step Explanation

  1. Export Terraform plan in JSON form.
  2. Parse resource_changes into typed structs.
  3. Classify actions such as create/update/delete/replace.
  4. Compute risk, policy, or cost summaries.
  5. Gate CI based on explicit thresholds.

Code Anatomy

  • Decoder layer parses JSON file.
  • Analysis layer converts raw actions into signals.
  • Reporting layer prints summary and sets exit status.

Learning Goals

  • Build deterministic IaC review tools.
  • Enforce infrastructure policy as code.
  • Reduce risky deploys with automated checks.