Terranova
Terranova is a Go package that allows you to easily use the Terraform Go Packages instead of executing the Terraform binary. The version v0.0.2
of Terranova works with Terraform version v0.12.17
.
For more information about Terranova and how to use use it, refer to the blog post Terranova: Using Terraform from Go.
How to use the Terranova package
Terranova works better as a Go module, if you don't have a go.mod
file in your project, create it with go mod init [package full name]
. Import Terranova in the Go code:
import (
"github.com/johandry/terranova"
)
As soon as you execute a Go command such as go build
, go test
or go mod tidy
it will be included in your go.mod
file and downloaded.
If you are not using modules yet, using vendors or having the packages in $GOPATH
, please, git clone
the repository and create the vendor directory:
mkdir -p $GOPATH/src/github.com/johandry/
cd $GOPATH/src/github.com/johandry/
git clone --depth=1 https://github.com/johandry/terranova.git
GO111MODULE=on go mod vendor
After having the package, the high level use of Terranova is like follows:
- Create a Platform instance with the Terraform code to apply
- Add to the
go.mod
file, import and add (AddProvider()
) the Terraform Provider(s) used in the code - Add to the
go.mod
file, import and add (AddProvisioner()
) the Terraform Provisioner (if any) used in the Terraform code. - Add (
Var()
orBindVars()
) the variables used in the Terraform code. - (optional) Create (
NewMiddleware()
) a logger middleware with the default logger or a custom logger that implements theLogger
interface. - (optional) Create your custom Terraform Hooks and assign them to the Platform instance.
- Load the previous state of the infrastructure and keep it updated using
PersistStateToFile()
. - Apply the changes using the method
Apply()
.
The following example shows how to create, scale or terminate AWS EC2 instances:
package main
import (
"log"
"os"
"github.com/johandry/terranova"
"github.com/johandry/terranova/logger"
"github.com/terraform-providers/terraform-provider-aws/aws"
)
var code string
const stateFilename = "simple.tfstate"
func main() {
count := 0
keyName := "demo"
log := log.New(os.Stderr, "", log.LstdFlags)
logMiddleware := logger.NewMiddleware()
defer logMiddleware.Close()
platform, err := terranova.NewPlatform(code).
SetMiddleware(logMiddleware).
AddProvider("aws", aws.Provider()).
Var("c", count).
Var("key_name", keyName).
PersistStateToFile(stateFilename)
if err != nil {
log.Fatalf("Fail to create the platform using state file %s. %s", stateFilename, err)
}
terminate := (count == 0)
if err := platform.Apply(terminate); err != nil {
log.Fatalf("Fail to apply the changes to the platform. %s", err)
}
}
func init() {
code = `
variable "c" { default = 2 }
variable "key_name" {}
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "server" {
instance_type = "t2.micro"
ami = "ami-6e1a0117"
count = "${var.c}"
key_name = "${var.key_name}"
}
`
}
Read the same example at terranova-examples/aws/simple/main.go and the blog post Terranova: Using Terraform from Go for a detail explanation of the code.
The git repository terranova-examples contain more examples of how to use Terranova with different clouds or providers.
Providers version
Terranova works with the latest version of Terraform (v0.12.12
) but requires Terraform providers using the Legacy Terraform Plugin SDK instead of the newer Terraform Plugin SDK. If the required provider still uses the Legacy Terraform Plugin SDK select the latest release using the Terraform Plugin SDK. For more information read the Terraform Plugin SDK page in the Extending Terraform documentation.
These are the latest versions supported for some providers:
- AWS:
github.com/terraform-providers/terraform-provider-aws v1.60.1-0.20191003145700-f8707a46c6ec
- OpenStack:
github.com/terraform-providers/terraform-provider-openstack v1.23.0
- vSphere:
github.com/terraform-providers/terraform-provider-vsphere v1.13.0
- Azure:
github.com/terraform-providers/terraform-provider-azurerm v1.34.0
- Null:
github.com/terraform-providers/terraform-provider-null v1.0.1-0.20190430203517-8d3d85a60e20
- Template:
github.com/terraform-providers/terraform-provider-template v1.0.1-0.20190501175038-5333ad92003c
- TLS:
github.com/terraform-providers/terraform-provider-tls v1.2.1-0.20190816230231-0790c4b40281
Include the code line in the require
section of your go.mod
file for the provider you are importing, for example:
require (
github.com/hashicorp/terraform v0.12.12
github.com/johandry/terranova v0.0.3
github.com/terraform-providers/terraform-provider-openstack v1.23.0
github.com/terraform-providers/terraform-provider-vsphere v1.13.0
)
If you get an error for a provider that you are not directly using (i.e. TLS provider) include the required version in the replace
section of the go.mod
file, for example:
replace (
github.com/terraform-providers/terraform-provider-tls => github.com/terraform-providers/terraform-provider-tls v1.2.1-0.20190816230231-0790c4b40281
)
If the required provider is not in this list, you can identify the version like this:
- Go to the GitHub project of the provider
- Locate the
provider.go
file located in the directory named like the provider name (i.e.was/provider.go
) - If the file imports
terraform-plugin-sdk/terraform
go to the previous git tag. - Repeat step #3 until you find the latest tag importing
terraform/terraform
(i.e. the latest tag for AWS isv2.31.0
) - If the tag can be used in the
go.mod
file, just include it after the module name, for example:terraform-provider-vsphere v1.13.0
- If the tag cannot be used in the
go.mod
file, for examplev2.31.0
for AWS:- Find the release generated from the git tag (i.e. v2.31.0)
- Locate the hash number assigned to the release (i.e.
f8707a4
) - Include the number in the
go.mod
file and executego mod tidy
or any other go command such asgo test
orgo build
.
If this is not working and need help to identify the right version, open an issue and you'll be help as soon as possible.
Logs
Using Terranova without a Log Middleware cause to print to Stderr all the Terraform logs, a lot, a lot of lines including traces. This may be uncomfortable or not needed. To discard the Terraform logs or filter them (print only the required log entries) you have to use the Log Middleware and (optionally) a custom Logger.
To create a Log Middleware use:
logMiddleware := logger.NewMiddleware()
defer logMiddleware.Close()
...
platform.SetMiddleware(logMiddleware)
You can decide when the Log Middleware starts intercepting the standard log
with logMiddleware.Start()
, if you don't the Log Middleware will start intercepting every line printed by the standard log
when Terranova execute an action that makes Terraform to print something to the standard log
. Every line intercepted by the Log Middleware is printed by the provided logger. This hijack ends when the Log Middleware is closed. To make the platform use the middleware, add it with SetMiddleware()
to the platform.
A logger is an instance of the interface Logger
. If the Log Middleware is created without parameter the default logger will be used, it prints the INFO, WARN and ERROR log entries of Terraform. To create your own logger check the examples in the Terranova Examples repository.
IMPORTANT: It's recommended to create your own instance of log
to not use the standard log when the Log Middleware is in use. Everything that is printed using the standard log will be intercepted by the Log Middleware and processed by the Logger. So, use your own custom log or do something like this before creating the Log Middleware:
log := log.New(os.Stderr, "", log.LstdFlags)
...
log.Printf("[DEBUG] this line is not captured by the Log Middleware")
Sources
All this research was done reading the Terraform documentation and source code.
Please, feel free to comment, open Issues and Pull Requests, help us to improve Terranova.
Contribute and Support
A great way to contribute to the project is to send a detailed report when you encounter an issue/bug or when you are requesting a new feature. We always appreciate a well-written, thorough bug report, and will thank you for it!
The GitHub issue tracker is for bug reports and feature requests. If you already forked and modified Terranova with a new feature or fix, please, open a Pull Request at GitHub
General support can be found at Gopher's Slack #terraform channel or message me directly @johandry