Writing my own terraform template (data) provider
Tags: go, terraform, devops, automation
Published: 2019-09-02 20:21:32

---
title: Writing my own terraform template (data) provider
published: true
tags: go,terraform,devops,automation
canonical_url: 
---

As a follow up to my previous post about how to [use terrafrom’s built-in looping mechanisms](https://dev.to/ineedale/terraform-templating-and-loops-479p), I set out to write my own custom “data” provider, in this case a new template renderer that would allow me to use Golang’s text/template instead of terraform’s own.

TL;DR — the code is here..

[alex-leonhardt/terraform-provider-gotemplate](https://github.com/alex-leonhardt/terraform-provider-gotemplate)

I started by trying to follow Hashicorp’s [_Writing Custom Providers_](https://www.terraform.io/docs/extend/writing-custom-providers.html) documentation, however that does not seem to explain how to write a **_data_**  **_provider_** , and although I had a basic Hello World working, I didn’t get too close to what I actually wanted to do.

So, since there is an existing template provider, I chose to use that as a kind of starting point and hack my way around all the bits I didn’t want/need. Which probably means I didn’t follow _“best practice”_. What I wanted was simple, I pass through a json encoded variable to the go-template provider and it will read a template file, render it (or fail), and I should be able to access the result, using the .rendered method, just like the original template provider does.

To use it, it’d roughly look like this …

```
variable "data" {
 default = {
 "msg" = "Hello World"
 "msg2" = [1, 2, 3, 4]
 }
}

data "gotemplate" "gotmpl" {
template = "${path.module}/file.tmpl"
data = "${jsonencode(var.data)}"
}

output "tmpl" {
value = "${data.gotemplate.gotmpl.rendered}"
}
```

As you can see, we have a map variable "data" with a mix of values of string type keys, other types are not allowed it seems — see [https://stackoverflow.com/a/8758771](https://stackoverflow.com/a/8758771)

I decided, 2 variables should be sufficient, the path to the template file (template) and the json encoded data (data) itself. I probably won’t go into details of how a provider should be structured as TF’s template provider seems a bit different from the _“writing custom providers”_ instructions — probably as it’s a bit older and possibly one of the first. So, since I followed that example (more or less), my new provider also doesn’t follow it, at least not closely.

To use my new provider, all that’s needed is to compile it into a file called terraform-provider-gotemplate and I can use the gotemplate provider, this is specified in the main.go file here

```
// Provider is a TF provider
func Provider() *schema.Provider {
 return &schema.Provider{
 DataSourcesMap: map[string]*schema.Resource{
 "gotemplate": dataSourceFile(),
 },
 }
}
```

the important part really is in dataSourceFile() which specifies what variables can be accessed using the provider, in my case that was

- template (the path to the go template file)
- data (the json encoded data)
- rendered (the finished render)

other than setting the schema, I also had to make it actually do something, this is done with

```
func dataSourceFile() *schema.Resource {
 return &schema.Resource{
 Read: dataSourceFileRead,
```

which passes dataSourceFileRead function to the schema.Resource‘s Read field (or property? I like to think of it as a property) which must match this function signature

```
type ReadFunc func(*ResourceData, interface{}) error
```

which is done with

```
func dataSourceFileRead(d *schema.ResourceData, meta interface{}) error {
 rendered, err := renderFile(d)
 if err != nil {
 return err
 }
 d.Set("rendered", rendered)
 d.SetId(hash(rendered))
 return nil
}
```

d.Set is important to set the rendered variable with the finished rendered template, so it can be used with output or other providers as a input.

Before setting rendered we retrieved the rendered output by calling the renderFile(d) function, which looks like this

{% gist https://gist.github.com/alex-leonhardt/d29d708d556eb5d885ac61d443d2f66f %}

The final result is available on Github, use it, make PRs if you like, I have learned quite a bit doing this, although still a bit confused and probably not following _the standard way_ of writing providers, but it was fun getting this to work.

[alex-leonhardt/terraform-provider-gotemplate](https://github.com/alex-leonhardt/terraform-provider-gotemplate)

References:

- [https://www.terraform.io/docs/extend/writing-custom-providers.html](https://www.terraform.io/docs/extend/writing-custom-providers.html)
- [https://github.com/terraform-providers/terraform-provider-template](https://github.com/terraform-providers/terraform-provider-template)
- [https://stackoverflow.com/a/8758771](https://stackoverflow.com/a/8758771)
- [https://www.calhoun.io/intro-to-templates-p3-functions/](https://www.calhoun.io/intro-to-templates-p3-functions/)
- [https://medium.com/ovni/terraform-templating-and-loops-9a88c0786c5c](https://dev.to/ineedale/terraform-templating-and-loops-382d-temp-slug-9478107)

* * *