如何让Golang脚本修改Terraform(HCL格式)文件中的值?

6vl6ewon  于 2023-05-27  发布在  Go
关注(0)|答案(1)|浏览(319)

我正在尝试对我拥有的Terraform文件进行一点自动化,该文件定义了Azure网络安全组。基本上我有一个网站& SSH访问,我只想允许我的公共IP地址,我可以从icanhazip.com获得。我希望用Golang脚本将我的IP写入.tf文件的相关部分(本质上是设置security_rule.source_address_prefixes的值)。
我尝试在Golang中使用hclsimple库,以及尝试gohclhclwrite和其他库,但我没有将HCL文件转换为Golang结构。
我的Terraform文件(我相信是HCL格式)如下:

resource "azurerm_network_security_group" "my_nsg" {
  name                = "my_nsg"
  location            = "loc"
  resource_group_name = "rgname"

  security_rule       = [
           {
               access                                     = "Deny"
               description                                = "Desc"
               destination_address_prefix                 = "*"
               destination_address_prefixes               = []
               destination_application_security_group_ids = []
               destination_port_range                     = ""
               destination_port_ranges                    = [
                   "123",
                   "456",
                   "789",
                   "1001",
                ]
               direction                                  = "Inbound"
               name                                       = "AllowInboundThing"
               priority                                   = 100
               protocol                                   = "*"
               source_address_prefix                      = "*"
               source_address_prefixes                    = [
                  # obtain from icanhazip.com
                  "1.2.3.4"
               ]
               source_application_security_group_ids      = []
               source_port_range                          = "*"
               source_port_ranges                         = []
            },
           {
               access                                     = "Allow"
               description                                = "Grant acccess to App"
               destination_address_prefix                 = "*"
               destination_address_prefixes               = []
               destination_application_security_group_ids = []
               destination_port_range                     = ""
               destination_port_ranges                    = [
                   "443",
                   "80",
                ]
               direction                                  = "Inbound"
               name                                       = "AllowIPInBound"
               priority                                   = 200
               protocol                                   = "*"
               source_address_prefix                      = ""
               source_address_prefixes                    = [
                # obtain from icanhazip.com
                   "1.2.3.4"
                ]
               source_application_security_group_ids      = []
               source_port_range                          = "*"
               source_port_ranges                         = []
            }
        ]
}

这是我使用Golang脚本所能达到的,试图将上述数据表示为结构体,然后解码.tf文件本身(我从hclsimple本地复制了几个方法,以便让它解码.tf文件,正如他们的文档中所建议的那样。

package main

import (
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "strings"

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

type Config struct {
    NetworkSecurityGroup []NetworkSecurityGroup `hcl:"resource,block"`
}

type NetworkSecurityGroup struct {
    Type              string         `hcl:"azurerm_network_security_group,label"`
    Name              string         `hcl:"mick-linux3-nsg,label"`
    NameAttr          string         `hcl:"name"`
    Location          string         `hcl:"location"`
    ResourceGroupName string         `hcl:"resource_group_name"`
    SecurityRule      []SecurityRule `hcl:"security_rule,block"`
}

type SecurityRule struct {
    Access                                 string   `hcl:"access"`
    Description                            string   `hcl:"description"`
    DestinationAddressPrefix               string   `hcl:"destination_address_prefix"`
    DestinationAddressPrefixes             []string `hcl:"destination_address_prefixes"`
    DestinationApplicationSecurityGroupIds []string `hcl:"destination_application_security_group_ids"`
    DestinationPortRange                   string   `hcl:"destination_port_range"`
    DestinationPortRanges                  []string `hcl:"destination_port_ranges"`
    Direction                              string   `hcl:"direction"`
    Name                                   string   `hcl:"name"`
    Priority                               int      `hcl:"priority"`
    Protocol                               string   `hcl:"protocol"`
    SourceAddressPrefix                    string   `hcl:"source_address_prefix"`
    SourceAddressPrefixes                  []string `hcl:"source_address_prefixes"`
    SourceApplicationSecurityGroupIds      []string `hcl:"source_application_security_group_ids"`
    SourcePortRange                        string   `hcl:"source_port_range"`
    SourcePortRanges                       []string `hcl:"source_port_ranges"`
}

func main() {
    // lets pass this in as a param?
    configFilePath := "nsg.tf"

    // create new Config struct
    var config Config

    // This decodes the TF file into the config struct, and hydrates the values
    err := MyDecodeFile(configFilePath, nil, &config)
    if err != nil {
        log.Fatalf("Failed to load configuration: %s", err)
    }
    log.Printf("Configuration is %#v", config)

    // let's read in the file contents
    file, err := os.Open(configFilePath)
    if err != nil {
        fmt.Printf("Failed to read file: %v\n", err)
        return
    }
    defer file.Close()

    // Read the file and output as a []bytes
    bytes, err := io.ReadAll(file)
    if err != nil {
        fmt.Println("Error reading file:", err)
        return
    }

    // Parse, decode and evaluate the config of the .tf file
    hclsimple.Decode(configFilePath, bytes, nil, &config)

    // iterate through the rules until we find one with
    // Description = "Grant acccess to Flask App"

    // CODE GO HERE
    for _, nsg := range config.NetworkSecurityGroup {
        fmt.Printf("security rule: %s", nsg.SecurityRule)
    }
}

// Basically copied from here https://github.com/hashicorp/hcl/blob/v2.16.2/hclsimple/hclsimple.go#L59
// but modified to handle .tf files too
func MyDecode(filename string, src []byte, ctx *hcl.EvalContext, target interface{}) error {
    var file *hcl.File
    var diags hcl.Diagnostics

    switch suffix := strings.ToLower(filepath.Ext(filename)); suffix {
    case ".tf":
        file, diags = hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1})
    case ".hcl":
        file, diags = hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1})
    case ".json":
        file, diags = json.Parse(src, filename)
    default:
        diags = diags.Append(&hcl.Diagnostic{
            Severity: hcl.DiagError,
            Summary:  "Unsupported file format",
            Detail:   fmt.Sprintf("Cannot read from %s: unrecognized file format suffix %q.", filename, suffix),
        })
        return diags
    }
    if diags.HasErrors() {
        return diags
    }

    diags = gohcl.DecodeBody(file.Body, ctx, target)
    if diags.HasErrors() {
        return diags
    }
    return nil
}

// Taken from here https://github.com/hashicorp/hcl/blob/v2.16.2/hclsimple/hclsimple.go#L89
func MyDecodeFile(filename string, ctx *hcl.EvalContext, target interface{}) error {
    src, err := ioutil.ReadFile(filename)
    if err != nil {
        if os.IsNotExist(err) {
            return hcl.Diagnostics{
                {
                    Severity: hcl.DiagError,
                    Summary:  "Configuration file not found",
                    Detail:   fmt.Sprintf("The configuration file %s does not exist.", filename),
                },
            }
        }
        return hcl.Diagnostics{
            {
                Severity: hcl.DiagError,
                Summary:  "Failed to read configuration",
                Detail:   fmt.Sprintf("Can't read %s: %s.", filename, err),
            },
        }
    }
    return MyDecode(filename, src, ctx, target)
}

当我运行代码时,实际上我正在努力定义NetworkSecurityGroup.SecurityRule,并在上面的代码中收到以下错误:

2023/05/24 11:42:11 Failed to load configuration: nsg.tf:6,3-16: Unsupported argument; An argument named "security_rule" is not expected here. Did you mean to define a block of type "security_rule"?
exit status 1

任何建议非常感谢

1tuwyuhd

1tuwyuhd1#

所以目前https://github.com/minamijoyo/hcledit还不可能(参见这里的https://github.com/minamijoyo/hcledit/issues/50-这表明hclwrite本身需要改变以促进)
因此,我按照@Martin阿特金斯的建议进行了工作:
我创建了一个包含局部变量的locals.tf文件,然后在NSG安全规则中引用该变量:

locals {
    my_ip = "1.2.3.4"
}

现在我只需获取我的IP并使用sed更新www.example.com文件中的值locals.tf

my_ip=$(curl -s -4 icanhazip.com)
sed -i "s|my_ip = \".*\"|my_ip = \"$my_ip\"|" locals.tf

相关问题