1 tf文件内容

$ cat string.tf 
variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."
  sensitive   = false
}

variable "other_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."
  sensitive   = true
}

2 实现代码

package main

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

    "github.com/hashicorp/hcl/v2"
    "github.com/hashicorp/hcl/v2/gohcl"
    "github.com/hashicorp/hcl/v2/hclsyntax"
)

var (
    configFileSchema = &hcl.BodySchema{
        Blocks: []hcl.BlockHeaderSchema{
            {
                Type:       "variable",
                LabelNames: []string{"name"},
            },
        },
    }

    variableBlockSchema = &hcl.BodySchema{
        Attributes: []hcl.AttributeSchema{
            {
                Name: "description",
            },
            {
                Name: "type",
            },
            {
                Name: "sensitive",
            },
        },
    }
)

type Config struct {
    Variables []*Variable
}

type Variable struct {
    Name        string
    Description string
    Type        string
    Sensitive   bool
}

func main() {
    config := configFromFile("string.tf")
    conf,_ := json.Marshal(config)
    fmt.Printf("conf:%+v\n", string(conf))
    for _, v := range config.Variables {
        a,_ := json.Marshal(v)
        fmt.Printf("%+v\n", string(a))
    }
}

func configFromFile(filePath string) *Config {
    content, err := os.ReadFile(filePath) // go 1.16
    if err != nil {
        log.Fatal(err)
    }

    file, diags := hclsyntax.ParseConfig(content, filePath, hcl.Pos{Line: 1, Column: 1})
    if diags.HasErrors() {
        log.Fatal("ParseConfig", diags)
    }

    bodyCont, diags := file.Body.Content(configFileSchema)
    if diags.HasErrors() {
        log.Fatal("file content", diags)
    }

    res := &Config{}

    for _, block := range bodyCont.Blocks {
        v := &Variable{
            Name: block.Labels[0],
        }

        blockCont, diags := block.Body.Content(variableBlockSchema)
        if diags.HasErrors() {
            log.Fatal("block content", diags)
        }

        if attr, exists := blockCont.Attributes["description"]; exists {
            diags := gohcl.DecodeExpression(attr.Expr, nil, &v.Description)
            if diags.HasErrors() {
                log.Fatal("description attr", diags)
            }
        }

        if attr, exists := blockCont.Attributes["sensitive"]; exists {
            diags := gohcl.DecodeExpression(attr.Expr, nil, &v.Sensitive)
            if diags.HasErrors() {
                log.Fatal("sensitive attr", diags)
            }
        }

        if attr, exists := blockCont.Attributes["type"]; exists {
            v.Type = hcl.ExprAsKeyword(attr.Expr)
            if v.Type == "" {
                log.Fatal("type attr", "invalid value")
            }
        }

        res.Variables = append(res.Variables, v)
    }
    return res
}

3 执行结果

# go run read.go     
conf:{"Variables":[{"Name":"image_id","Description":"The id of the machine image (AMI) to use for the server.","Type":"string","Sensitive":false},{"Name":"other_id","Description":"The id of the machine image (AMI) to use for the server.","Type":"string","Sensitive":true}]}
{"Name":"image_id","Description":"The id of the machine image (AMI) to use for the server.","Type":"string","Sensitive":false}
{"Name":"other_id","Description":"The id of the machine image (AMI) to use for the server.","Type":"string","Sensitive":true}
03-16 01:18