Hclwrite(v2) a object with a reference value

Hi,

We works on golang programm to generate terraform files and I am having difficulty generating a local of type object with a value reference, like this :

locals {
  servers = {
    "admin@server-1" = {
      hostname = "srv1"
      login    = "admin"
      password = data.vault_generic_secret.srv1.data["password"]
      port     = 22
    }
    "admin@server-2" = {
      hostname = "srv2"
      login    = "admin"
      password = data.vault_generic_secret.srv2.data["password"]
      port     = 22
    }
  }
}

An example of code to generate this file :

package main

import (
	"fmt"

	"github.com/hashicorp/hcl/v2/hclwrite"
	"github.com/zclconf/go-cty/cty"
)

func main() {
	f := hclwrite.NewEmptyFile()
	rootBody := f.Body()
	localsBlock := rootBody.AppendNewBlock("locals", nil).Body()
	localsBlock.SetAttributeValue("servers",
		cty.ObjectVal(map[string]cty.Value{
			"admin@server-1": cty.ObjectVal(map[string]cty.Value{
				"hostname": cty.StringVal("srv1"),
				"login":    cty.StringVal("admin"),
				"password": <?>,
				"port":     cty.NumberIntVal(22),
			}),
			"admin@server-2": cty.ObjectVal(map[string]cty.Value{
				"hostname": cty.StringVal("srv2"),
				"login":    cty.StringVal("admin"),
				"password": <?>,
				"port":     cty.NumberIntVal(22),
			}),
		}))
	fmt.Printf("%s", f.Bytes())
}

but I don’t know what to do for the reference in password

Have you an idea ?

Hi @jeremmfr,

hclwrite doesn’t currently have a helper function for generating this sort of expression with a mixture of literal and reference parts. Its existing helpers are only for setting entirely-literal values or single references.

I think therefore you’d need to use SetAttributeRaw to meet this use-case with today’s hclwrite. That’ll require constructing the necessary tokens yourself, because the AST layer doesn’t have any rule for doing that automatically.

Alternatively, you could generate this in a .tf.json file instead, which means you can use a normal JSON encoder rather than hclwrite. The JSON syntax equivalent of what you shared would be the following:

{
  "locals": {
    "servers": {
      "admin@server-1": {
        "hostname": "srv1",
        "login": "admin",
        "password": "${data.vault_generic_secret.srv1.data[\"password\"]}",
        "port": 22
      },
      "admin@server-2": {
        "hostname": "srv2",
        "login": "admin",
        "password": "${data.vault_generic_secret.srv2.data[\"password\"]}",
        "port": 22
      }
    }
  }
}

The Terraform language’s JSON mode exists specifically to make it easier to do this sort of dynamic generation. You can see more about how it works in JSON Configuration Syntax.

Thanks for your quick reply

Hi @apparentlymart,

I have a similar use case, but unfortunately I’m in a position where using the JSON Configuration Syntax is not possible.

How would I go on about constructing such an object using SetAttributeRaw?

I need to create the following custom_data object attribute:

post-processor "manifest" {
  custom_data = {
    subscription        = "${var.subscription_id}"
    resource_group      = "${var.resource_group}"
    gallery_name        = "${var.gallery_name}"
    image_name          = "${var.image_name}"
    image_version       = "${var.versionToBuild}"
    source_version      = "${var.info_source_version}"
    source_branch       = "${var.info_source_branch}"
    source_repo         = "${var.info_source_repo}"
    gallery_artifact_id = "/subscriptions/${var.subscription_id}/resourceGroups/${var.resource_group}/providers/Microsoft.Compute/galleries/${var.gallery_name}/images/${var.image_name}/versions/${var.versionToBuild}"
    managed_artifact_id = "/subscriptions/${var.subscription_id}/resourceGroups/${var.resource_group}/providers/Microsoft.Compute/images/${var.image_name}-${var.versionToBuild}"
  }
}

I can for instance use the following:

customDataTokens := hclwrite.Tokens{
	{
		Type: hclsyntax.ObjectConsItem
	},
	{
		Type:  hclsyntax.TokenOBrace,
		Bytes: []byte(`{`),
	},
	{
		Type:  hclsyntax.TokenCBrace,
		Bytes: []byte(`}`),
	},
}

body.SetAttributeRaw("custom_data", customDataTokens)

Which will result in:
custom_data = {}

But how should I go on about creating an attribute object which contains multiple attributes such as the custom_data object above?

Since as you’re pointing out, I’d need to use SetAttributeRaw to meet this use-case with today’s hclwrite, I’m all for that but I have no idea how to do it - an example would be super helpful and really appreciated!

Or, perhaps sine it’s been 2 years since this was posted, perhaps hclwrite now have a helper function for generating this sort of expression? :slight_smile: