(小ネタ) terraform graph で該当するリソースだけ絞り込むスクリプト

2020.03.18

お疲れ様です。小島です。

terraform のリソースは増えていくにつれ、関係性について把握が難しくなるのではないでしょうか。

その時、依存関係を dot 言語で出力してくれる terraform graph があります。

しかし、多くのリソースが含まれる図には多くの関係を示す線が含まれており、把握するには一苦労です。

terraform graph には module-depth という深さを指定し、出力を絞り込むことができるオプションがあります。

ですが、スモールスタートでの開発で徐々にリソースが増えていくような場合だと、 module のリファクタリングは state も考慮する必要があるので後回しになりがちです。

なので、正規表現に該当するリソースだけ吐き出すようなスクリプトを go で書きました。

スクリプト

スクリプトの使い方

# Glue に関するリソースを絞り込む
terraform graph | go run . -expr "aws_glue_" | dot -Tsvg > graph_sample1.svg

# Tempura に関するリソースを絞り込んだ後、再度 S3 に紐づくリソースを絞り込む
terraform graph | go run . -expr "tempura" | go run . -expr "aws_s3_" | dot -Tsvg > graph_sample2.svg

実行結果

絞り込む前のグラフです。

Glue に関するリソースを絞り込んだ結果です。

はい、ジョブの数は 1 つ、クローラーと S3 バケットが 1-1 になっている構成です。あっています。

Tempura と S3 に紐づくリソースを絞り込んだ結果です。

pheasant_tempura_okonomiyaki のバケットだけ aws リソースに紐づいていません。 アプリケーションでのみ使っているようです。なるほど。。。

確認した環境

# uname -a
Darwin HL00566.local 19.3.0 Darwin Kernel Version 19.3.0: Thu Jan  9 20:58:23 PST 2020; root:xnu-6153.81.5~1/RELEASE_X86_64 x86_64

# terraform version
Terraform v0.12.6

# go version
go version go1.14 darwin/amd64

# dot -V
dot - graphviz version 2.42.3 (20191010.1750)

スクリプトの解説

dot 言語の解析と出力には https://gonum.org のライブラリを使用しました。

数値計算用ライブラリ、とのことですが、グラフ言語に関するコードも入っていました。

package main

import (
    // ...

    "gonum.org/v1/gonum/graph/formats/dot"
    "gonum.org/v1/gonum/graph/formats/dot/ast"
)

dot.Parse をすることで AST に解析してくれます。

  dast, err := dot.Parse(os.Stdin)

AST を再構築しても良いですが、今回は各要素を直接書き換えることで対応しました。

    case *ast.Subgraph:
        // ...

        xs := xt.Stmts
        xsSize := len(xs)
        delCnt := 0

        for i := 0; i < xsSize; i++ {
            xtx := xs[i-delCnt]

            err := walk(xtx, f)

            // ...
        }

        xt.Stmts = xs
        // ...
type visitor struct {
    *regexp.Regexp
}

func (v *visitor) Visit(x ast.Stmt) error {
    switch xt := x.(type) {
    case *ast.NodeStmt:
        if v.MatchString(xt.Node.ID) {
            xt.Attrs = append(xt.Attrs, &ast.Attr{Key: "color", Val: "red"})
            return nil
        }

        // ...
    }
}

ast.GraphString() で dot 言語形式で出力されます。

    for _, g := range dast.Graphs {
        err := walk(g, v.Visit)
        if err != nil {
            log.Fatal(err)
            return
        }

        fmt.Println(g)
    }

感想

現状は理解するための可視化が目的だったので、この機能で満足しています。 次のアプローチとして node にアイコンを載せたり、依存関係を辿れるようにしたら楽しそうですね。