php Xiaobian Youzi teaches you how to use Golang script to modify the value in the Terraform (HCL format) file. Terraform is an infrastructure-as-code tool that helps us manage and automate cloud infrastructure. However, if we need to frequently modify certain values in the Terraform file, manual operations will become very tedious. Therefore, using Golang scripts to modify values in Terraform files will be a more efficient method. In this article, I will show you how to write a script using Golang to achieve this goal, so that you can easily modify the values in the Terraform file.
I'm trying to do a small amount of automation on a terraform file I have that defines an azure network security group. Essentially, I have a website and ssh access, I just want to allow my public ip address, which I can get from icanhazip.com
. I want to use a golang script to write my ip to the relevant part of a .tf file (essentially setting the value of security_rule.source_address_prefixes
).
I am trying to use hclsimple
library in golang and tried gohcl
, hclwrite
etc. but essentially I am converting hcl file to golang No progress was made on the structure.
My terraform file (in hcl format I believe) is as follows:
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 = [] } ] }
This is what I got with my golang script trying to represent the above data as a structure and then decoding the .tf file itself (I copied a few methods locally from hclsimple
in order to have it as per Recommendations in its documentation decode .tf files.
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) }
When I run the code, essentially I am trying to define the networksecuritygroup.securityrule, and receive the following error using the above code:
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
Any suggestions are greatly appreciated
Therefore, currently https://www.php.cn/link/f56de5ef149cf0aedcc8f4797031e229 is not possible (see Herehttps://www.php.cn/link/f56de5ef149cf0aedcc8f4797031e229/issues/50 - This suggestionhclwrite
itself needs to be changed for convenience)
So I solved it as @martin atkins suggested:
I created a locals.tf
file containing the locals variable, which I then referenced in the nsg security rule:
locals { my_ip = "1.2.3.4" }
Now I just get my ip and update the value in the locals.tf file using sed
my_ip=$(curl -s -4 icanhazip.com) sed -i "s|my_ip = \".*\"|my_ip = \"$my_ip\"|" locals.tf
The above is the detailed content of How can I have a Golang script modify values in a Terraform (HCL format) file?. For more information, please follow other related articles on the PHP Chinese website!