tfgen

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

README

tfgen

Go Reference License

Generate Terraform configuration from Go.

tfgen is a small library for building Terraform JSON configuration programmatically in Go. You describe your infrastructure with plain Go values and tfgen emits *.tf.json that any Terraform-compatible CLI (Terraform or OpenTofu) can apply.

Unlike Hashicorp's now unsupported CDKTF, configuration is entirely flexible and does not depend on generating strong types based off provider versions. This means you can reliably and quickly generate Terraform-compatible JSON using easy to understand Go.

Quick start

Keep a small generator in your project and wire it to go generate:

package main

//go:generate go run .

import (
	"log"
	"os"

	"github.com/ably/tfgen"
)

func main() {
	stack := tfgen.NewStack("example")

	bucket := &tfgen.Resource{
		Type: "aws_s3_bucket",
		Name: "example",
		Config: tfgen.Config{
			"bucket": "my-example-bucket",
		},
	}
	stack.AddResource(bucket)

	// Ref builds a Terraform interpolation referencing an attribute of the
	// resource, so you never hand-write "${...}" strings.
	stack.AddOutput(&tfgen.Output{
		Name:  "bucket_arn",
		Value: bucket.Ref("arn"),
	})

	f, err := os.Create("main.tf.json")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	if err := stack.Write(f); err != nil {
		log.Fatal(err)
	}
}

Run go generate ./... to write main.tf.json, ready to apply with terraform or tofu:

{
  "output": {
    "bucket_arn": {
      "value": "${aws_s3_bucket.example.arn}"
    }
  },
  "resource": {
    "aws_s3_bucket": {
      "example": {
        "bucket": "my-example-bucket"
      }
    }
  }
}

See examples/generate for a complete, runnable example.

Concepts

A Stack is one Terraform configuration. You add building blocks to it, a Resource, Data source, Module, Provider, Output, Variable, or the terraform block, then serialise it to JSON. Config is a map[string]any, so any attribute maps directly onto Terraform's JSON syntax, and helpers such as Resource.Ref and Variable.Ref build interpolation references so you never hand-write "${...}" strings.

Write a single stack to any io.Writer with Stack.Write, or write a set of stacks to their conventional files at {baseDir}/stacks/{name}/main.tf.json with Stacks.Write. See the Go documentation for the full API.

Provider helpers

The providers/* subpackages provide optional, typed constructors for common providers. They are thin sugar over tfgen types, so you can always drop down to a plain tfgen.Provider or tfgen.Resource.

Contributing

Contributions are welcome, see CONTRIBUTING.md.

License

Licensed under the Apache License 2.0.

Documentation

Overview

Package tfgen generates Terraform JSON configuration from Go.

You describe infrastructure with plain Go values. A Stack holds one Terraform configuration, to which you add resources, data sources, modules, providers, outputs, and variables. Stack.Write then serialises it to Terraform JSON that any Terraform-compatible CLI (Terraform or OpenTofu) can apply.

The providers subpackages offer optional, typed constructors for common providers.

Example

Example builds a minimal stack using only the core API and writes it as Terraform JSON.

package main

import (
	"bytes"
	"fmt"

	"github.com/ably/tfgen"
)

func main() {
	stack := tfgen.NewStack("example")

	file := &tfgen.Resource{
		Type: "local_file",
		Name: "hello",
		Config: tfgen.Config{
			"content":  "hello, world",
			"filename": "hello.txt",
		},
	}
	stack.AddResource(file)

	// Reference an attribute of the resource in an output.
	stack.AddOutput(&tfgen.Output{
		Name:  "path",
		Value: file.Ref("filename"),
	})

	var buf bytes.Buffer
	if err := stack.Write(&buf); err != nil {
		panic(err)
	}

	fmt.Print(buf.String())
}
Output:
{
  "output": {
    "path": {
      "value": "${local_file.hello.filename}"
    }
  },
  "resource": {
    "local_file": {
      "hello": {
        "content": "hello, world",
        "filename": "hello.txt"
      }
    }
  }
}
Example (Ably)

Example_ably provisions an Ably app with a namespace and an API key, wiring the child resources to the app via references.

package main

import (
	"bytes"
	"fmt"

	"github.com/ably/tfgen"
	"github.com/ably/tfgen/providers/ably"
)

func main() {
	stack := tfgen.NewStack("ably")

	block := &tfgen.TerraformBlock{RequiredVersion: ">= 1.9"}
	block.AddRequiredProvider("ably", ably.ProviderVersion())
	stack.SetTerraformBlock(block)

	stack.AddProvider(ably.Provider())

	app := ably.App("main", ably.AppOptions{TLSOnly: true})
	stack.AddResource(app)

	stack.AddResource(ably.Namespace("chat", ably.NamespaceOptions{
		AppID:     app.Ref("id"),
		Persisted: true,
	}))

	stack.AddResource(ably.APIKey("publisher", ably.APIKeyOptions{
		AppID:        app.Ref("id"),
		Capabilities: map[string][]string{"chat:*": {"publish", "subscribe"}},
	}))

	var buf bytes.Buffer
	if err := stack.Write(&buf); err != nil {
		panic(err)
	}

	fmt.Print(buf.String())
}
Output:
{
  "provider": {
    "ably": {}
  },
  "resource": {
    "ably_api_key": {
      "publisher": {
        "app_id": "${ably_app.main.id}",
        "capabilities": {
          "chat:*": [
            "publish",
            "subscribe"
          ]
        },
        "name": "publisher"
      }
    },
    "ably_app": {
      "main": {
        "name": "main",
        "tls_only": true
      }
    },
    "ably_namespace": {
      "chat": {
        "app_id": "${ably_app.main.id}",
        "id": "chat",
        "persisted": true
      }
    }
  },
  "terraform": {
    "required_providers": {
      "ably": {
        "source": "ably/ably",
        "version": "1.0.0"
      }
    },
    "required_version": ">= 1.9"
  }
}
Example (Aws)

Example_aws builds a stack using the AWS provider helpers.

package main

import (
	"bytes"
	"fmt"

	"github.com/ably/tfgen"
	"github.com/ably/tfgen/providers/aws"
)

func main() {
	stack := tfgen.NewStack("aws")

	block := &tfgen.TerraformBlock{RequiredVersion: ">= 1.9"}
	block.AddRequiredProvider("aws", aws.ProviderVersion("~> 5.0"))
	stack.SetTerraformBlock(block)

	stack.AddProvider(aws.Provider("primary", "eu-west-1", tfgen.Config{
		"managed_by": "tfgen",
	}))

	stack.AddResource(&tfgen.Resource{
		Type:   "aws_s3_bucket",
		Name:   "example",
		Config: tfgen.Config{"bucket": "my-example-bucket"},
	})

	var buf bytes.Buffer
	if err := stack.Write(&buf); err != nil {
		panic(err)
	}

	fmt.Print(buf.String())
}
Output:
{
  "provider": {
    "aws": {
      "alias": "primary",
      "default_tags": [
        {
          "tags": {
            "managed_by": "tfgen"
          }
        }
      ],
      "region": "eu-west-1"
    }
  },
  "resource": {
    "aws_s3_bucket": {
      "example": {
        "bucket": "my-example-bucket"
      }
    }
  },
  "terraform": {
    "required_providers": {
      "aws": {
        "source": "hashicorp/aws",
        "version": "~> 5.0"
      }
    },
    "required_version": ">= 1.9"
  }
}
Example (Cloudflare)

Example_cloudflare builds a stack using the Cloudflare provider helper.

package main

import (
	"bytes"
	"fmt"

	"github.com/ably/tfgen"
	"github.com/ably/tfgen/providers/cloudflare"
)

func main() {
	stack := tfgen.NewStack("cloudflare")

	block := &tfgen.TerraformBlock{RequiredVersion: ">= 1.9"}
	block.AddRequiredProvider("cloudflare", cloudflare.ProviderVersion("~> 4.0"))
	stack.SetTerraformBlock(block)

	stack.AddProvider(cloudflare.Provider())

	stack.AddResource(&tfgen.Resource{
		Type: "cloudflare_record",
		Name: "www",
		Config: tfgen.Config{
			"zone_id": "${var.zone_id}",
			"name":    "www",
			"type":    "CNAME",
			"content": "example.com",
			"proxied": true,
		},
	})

	var buf bytes.Buffer
	if err := stack.Write(&buf); err != nil {
		panic(err)
	}

	fmt.Print(buf.String())
}
Output:
{
  "provider": {
    "cloudflare": {}
  },
  "resource": {
    "cloudflare_record": {
      "www": {
        "content": "example.com",
        "name": "www",
        "proxied": true,
        "type": "CNAME",
        "zone_id": "${var.zone_id}"
      }
    }
  },
  "terraform": {
    "required_providers": {
      "cloudflare": {
        "source": "cloudflare/cloudflare",
        "version": "~> 4.0"
      }
    },
    "required_version": ">= 1.9"
  }
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func MarshalJSON

func MarshalJSON(v any) ([]byte, error)

MarshalJSON encodes v to indented JSON with HTML escaping disabled.

func OutputFile

func OutputFile(path string) (*os.File, error)

OutputFile creates the parent directories and file at the given path.

func StackOutputWriter

func StackOutputWriter(baseDir, stackName string) (*os.File, error)

StackOutputWriter creates the output directory and file for a stack. The file is written to {baseDir}/stacks/{stackName}/main.tf.json. For a custom filename or layout, use OutputFile directly.

Types

type Backend

type Backend struct {
	Type   string // backend type, e.g. "s3"
	Config Config // backend settings
}

Backend represents the backend configuration in the terraform block, which includes the type of backend and its configuration.

type Config

type Config map[string]any

Config is a free-form set of Terraform configuration attributes, used for the body of resources, data sources, modules, providers, and backends. The keys and values map directly onto Terraform's JSON syntax.

Its underlying type is map[string]any, so a plain map literal works anywhere a Config is expected, and vice versa.

type Data

type Data struct {
	Type   string // data source type, e.g. "aws_ami"
	Name   string // local name, unique per type within the stack
	Config Config // the data source's arguments
}

Data represents a data source block in a Terraform configuration.

func (*Data) Ref

func (d *Data) Ref(attribute string) string

Ref returns a Terraform expression referencing an attribute of this data source, e.g. "${data.aws_acm_certificate.acm_certificate.arn}".

type DataSources

type DataSources []*Data

DataSources is a collection of Data configurations.

type Generator

type Generator interface {
	// Validate checks that the generator's configuration is valid. This should
	// be called before GenerateStacks to catch config errors early.
	Validate() error

	// GenerateStacks generates one or more Terraform stacks based on the
	// generator's configuration. The generated stacks can then be written to
	// files or applied using a Terraform-compatible CLI.
	GenerateStacks() (Stacks, error)
}

Generator produces one or more Terraform stacks from some configuration. Implement it to organise larger configuration-generation logic behind a single, testable entry point.

type Module

type Module struct {
	Name    string // local name of the module instance
	Source  string // module source address
	Version string // optional version constraint (registry modules only)
	Config  Config // input variables passed to the module
}

Module represents a module block in a Terraform configuration.

func (*Module) OutputRef

func (m *Module) OutputRef(output string) string

OutputRef returns a Terraform expression referencing an output of this module, e.g. "${module.my_module.vpc_id}".

func (*Module) Outputs

func (m *Module) Outputs(names []string) []*Output

Outputs creates Output blocks for each named output, referencing this module.

type ModuleSource

type ModuleSource struct {
	Name     string // module name, e.g. "vpc", "eks-cluster"
	Provider string // provider namespace, e.g. "aws", "google"
	SubDir   string // optional subdirectory within {provider}/
}

ModuleSource derives a module's Source from a consistent naming convention, whether the module lives in a registry or on the local filesystem. It follows Terraform's terraform-<PROVIDER>-<NAME> convention:

  • Registry: {registryPrefix}/{name}/{provider}
  • Local: {modulesDir}/{provider}/terraform-{provider}-{name}

SubDir inserts an additional directory between the provider and the module directory when computing the local path:

  • Local with SubDir: {modulesDir}/{provider}/{subDir}/terraform-{provider}-{name}

Using ModuleSource is optional; you can always set Module.Source directly.

func (ModuleSource) LocalSource

func (ms ModuleSource) LocalSource(modulesDir string) string

LocalSource returns the local module path rooted at modulesDir.

func (ModuleSource) RegistrySource

func (ms ModuleSource) RegistrySource(registryPrefix string) string

RegistrySource returns the registry source path for the given registry prefix. The prefix is the leading portion of a registry module address, such as the host and namespace (e.g. "app.terraform.io/example-org").

type Modules

type Modules []*Module

Modules is a collection of Module configurations.

type Output

type Output struct {
	Name        string // output name
	Value       any    // exported value, often a Ref to another block
	Description string // optional human-readable description
}

Output represents an output block in a Terraform configuration.

type Outputs

type Outputs []*Output

Outputs is a collection of Output configurations.

type Provider

type Provider struct {
	Name   string // provider name, e.g. "aws"
	Config Config // provider settings; include "alias" for additional configurations
}

Provider represents a provider block in a Terraform configuration.

type ProviderRequirement

type ProviderRequirement struct {
	Source  string // provider source address, e.g. "hashicorp/aws"
	Version string // version constraint, e.g. "~> 5.0"
}

ProviderRequirement represents the required provider configuration in the terraform block, which includes the source and version of the provider.

type Providers

type Providers []*Provider

Providers is a collection of Provider configurations.

type RequiredProviders

type RequiredProviders map[string]ProviderRequirement

RequiredProviders maps a provider's local name to its source and version requirement.

type Resource

type Resource struct {
	Type   string // resource type, e.g. "aws_s3_bucket"
	Name   string // local name, unique per type within the stack
	Config Config // the resource's arguments
}

Resource represents a resource block in a Terraform configuration.

func (*Resource) Ref

func (r *Resource) Ref(attribute string) string

Ref returns a Terraform expression referencing an attribute of this resource, e.g. "${aws_s3_bucket.example.arn}".

type Resources

type Resources []*Resource

Resources is a collection of Resource configurations.

type Stack

type Stack struct {
	// Name is the name of the stack
	Name string
	// contains filtered or unexported fields
}

Stack represents a Terraform stack which can eventually be written to be applied by a Terraform-compatible CLI, such as Terraform or Tofu.

func NewStack

func NewStack(name string) *Stack

NewStack creates a new Stack with the given name and an empty Terraform configuration.

func (*Stack) AddData

func (s *Stack) AddData(data *Data)

AddData appends a data source to the stack.

func (*Stack) AddModule

func (s *Stack) AddModule(module *Module)

AddModule appends a module to the stack.

func (*Stack) AddOutput

func (s *Stack) AddOutput(output *Output)

AddOutput appends an output to the stack.

func (*Stack) AddProvider

func (s *Stack) AddProvider(provider *Provider)

AddProvider appends a provider to the stack.

func (*Stack) AddResource

func (s *Stack) AddResource(resource *Resource)

AddResource appends a resource to the stack.

func (*Stack) AddVariable

func (s *Stack) AddVariable(variable *Variable)

AddVariable appends a variable to the stack.

func (*Stack) JSON

func (s *Stack) JSON() ([]byte, error)

JSON returns the JSON representation of the stack's Terraform configuration. This correctly marshals the stack elements into the correct Terraform specification.

func (*Stack) SetData

func (s *Stack) SetData(data []*Data)

SetData replaces the stack's data sources.

func (*Stack) SetModules

func (s *Stack) SetModules(modules []*Module)

SetModules replaces the stack's modules.

func (*Stack) SetOutputs

func (s *Stack) SetOutputs(outputs []*Output)

SetOutputs replaces the stack's outputs.

func (*Stack) SetProviders

func (s *Stack) SetProviders(providers []*Provider)

SetProviders replaces the stack's providers.

func (*Stack) SetResources

func (s *Stack) SetResources(resources []*Resource)

SetResources replaces the stack's resources.

func (*Stack) SetTerraformBlock

func (s *Stack) SetTerraformBlock(block *TerraformBlock)

SetTerraformBlock sets the stack's terraform block.

func (*Stack) SetVariables

func (s *Stack) SetVariables(variables []*Variable)

SetVariables replaces the stack's variables.

func (*Stack) Write

func (s *Stack) Write(w io.Writer) error

Write writes the stack's Terraform JSON configuration to w.

type Stacks

type Stacks []*Stack

Stacks is a collection of Stack values.

func (Stacks) Write

func (s Stacks) Write(baseDir string) error

Write writes each stack to its conventional file at {baseDir}/stacks/{name}/main.tf.json, creating directories as needed.

type TerraformBlock

type TerraformBlock struct {
	RequiredVersion   string            // Terraform version constraint, e.g. ">= 1.9"; omitted when empty
	RequiredProviders RequiredProviders // provider source and version requirements
	Backend           *Backend          // optional state backend configuration
}

TerraformBlock represents the terraform block in a Terraform configuration, which includes required_version, required_providers, and backend configuration.

func (*TerraformBlock) AddRequiredProvider

func (block *TerraformBlock) AddRequiredProvider(name string, req ProviderRequirement)

AddRequiredProvider records a provider requirement under the given local name, allocating the map if necessary.

type TerraformConfig

type TerraformConfig struct {
	Data           DataSources     // data source blocks
	Modules        Modules         // module blocks
	Outputs        Outputs         // output blocks
	Providers      Providers       // provider blocks
	Resources      Resources       // resource blocks
	TerraformBlock *TerraformBlock // the terraform block, if any
	Variables      Variables       // variable blocks
}

TerraformConfig holds the building blocks of a single Terraform configuration: the terraform block, providers, resources, data sources, modules, variables, and outputs.

type Variable

type Variable struct {
	Name        string // variable name
	Type        string // Terraform type constraint, e.g. "string", "bool", "map(string)"
	Description string // optional human-readable description
	Default     any    // default value; nil marks the variable as required (no default emitted)
}

Variable represents a variable block in a Terraform configuration.

func (*Variable) CountRef

func (v *Variable) CountRef() string

CountRef returns a Terraform conditional count expression based on this boolean variable, e.g. "${var.deploy_ipam ? 1 : 0}".

func (*Variable) Ref

func (v *Variable) Ref() string

Ref returns a Terraform expression referencing this variable, e.g. "${var.name_prefix}".

type Variables

type Variables []*Variable

Variables is a collection of Variable configurations.

Directories

Path Synopsis
examples
generate command
Command generate shows how to drive tfgen from `go generate`.
Command generate shows how to drive tfgen from `go generate`.
providers
ably
Package ably provides convenience constructors for the Terraform Ably provider (https://github.com/ably/terraform-provider-ably).
Package ably provides convenience constructors for the Terraform Ably provider (https://github.com/ably/terraform-provider-ably).
aws
Package aws provides convenience constructors for the Terraform AWS provider.
Package aws provides convenience constructors for the Terraform AWS provider.
cloudflare
Package cloudflare provides convenience constructors for the Terraform Cloudflare provider.
Package cloudflare provides convenience constructors for the Terraform Cloudflare provider.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL