Tips and Tricks: Backends
Terraform-Operator is a Kubernetes CRD and Controller to configure, run, and manage your Terraform resources right from your cluster. Simply configure a Kubernetes manifest, apply, and watch Terraform-Operator run the Terraform. Once it's complete, it saves the Terraform output into a Kubernetes ConfigMap which can be consumed directly by your Pods.
Welcome back! Let's talk tips and tricks about Terraform Backends. In case you missed the first article or just want to review the basics, check out Introduction to Terraform-Operator: Basics.
Terraform Backends
Terraform defines a backend this way.
A "backend" in Terraform determines how state is loaded and how an operation such as apply is executed.
That's nice... but what the heck does that mean? I think the above definition by Terraform is really referring to backend in the context of using Terraform Cloud since it's the only backend that handles remote execution.
A simpler way to think of a backend is "where Terraform stores state." Without any configuration, a Terraform module will save state on the local system running Terraform. However, since Terraform-Operator runs on pods which are ephemeral by nature, state must be stored elsewhere since data written to the pod's filesystem is lost when the pod terminates.
Terraform-Operator's Default Backend
Terraform-Operator uses Consul, a HashiCorp key/value store, as the default backend for any Terraform run. It does so by placing a
file in the module which points to the Consul service running on your cluster.
# backend_override.tf
terraform {
backend "consul" {
address = "hashicorp-consul-server.tf-system:8500"
scheme = "http"
path = "terraform/${NAMESPACE}/${RESOURCE_NAME}.tfstate"
}
}
Where:
is the name of the Kubernetes resource for your Terraform config.RESOURCE_NAME
See Terraform State with Consul from the first blog in this series.
Using a Different Backend
Terraform-Operator takes a rather simplistic approach to backends; either completely define one or completely don't. Let's do a simple exercise to see how using a different backend is accomplished.
Start by defining the Terraform-Operator resource. Let's name it
. Take close attention to the
value in the spec. What you'll notice is a multi-line YAML string of a fully defined Terraform backend.
# hello-terraform-operator.yaml
---
apiVersion: tf.isaaguilar.com/v1alpha1
kind: Terraform
metadata:
name: hello-terraform-operator
spec:
terraformVersion: 0.12.23
terraformModule:
address: https://github.com/cloudposse/terraform-aws-test-module.git
credentials:
- secretNameRef:
name: aws-session-credentials
customBackend: |-
terraform {
backend "s3" {
key = "terraform-operator/my/awesome/test.tfstate"
region = "us-east-1"
bucket = "isaaguilar-terraform"
dynamodb_table = "isaaguilar-terraform-lock"
}
}
applyOnCreate: true
applyOnUpdate: true
applyOnDelete: true
In this example, I've got the remote state going to an AWS S3 bucket. The keyword in that statement being "AWS," because as with any cloud provider, credentials are required for access to AWS (or any providers') resources.
In the last post I showed how to create a secret with credentials, and then reference those credentials using
in the resource spec. If that method is OK with you, skip to the Terraform run. For the adventurous type, I'd like to introduce you to:
The Kubectl AWS Session Plugin
The creatively named kubectl-aws-session-plugin is a
plugin to uses AWS's Default Credential Provider Chain to get credentials and push them into a Kubernetes secret. Maybe it's overkill to do such a simple task, who's to judge? All I know is that it takes my current AWS session and dumps it into a Kubernetes Secret called "aws-session-credentials". And it has a native
feel to it!
Install it by running:
$ ver=v0.1.1
$ os=$(uname -s | tr A-Z a-z)
$ wget "https://github.com/isaaguilar/kubectl-aws-session-plugin/releases/download/$ver/kubectl-aws-session-$ver-$os.tgz"
$ tar xzf kubectl-aws-session-$ver-$os.tgz
$ mv kubectl-aws-session $(dirname $(which kubectl))
Then run
to see the Secret get created (or updated if it already existed).
$ kubectl aws session --namespace default
secret aws-session-credentials created successfully
Luckily for us, the Secret that got created is named "aws-session-credentials", which just so happens to be the same value as
in the resource spec. What a happy coincidence. 😊 Now we're ready to run the Terraform
Running Terraform
Simply apply the
to install your new resource.
$ kubectl apply -f hello-terraform-operator.yaml
terraform.tf.isaaguilar.com/hello-terraform-operator created
After a small while you'll see your pod
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-terraform-operator-f5tw9 1/1 Running 0 7s
Run kubectl logs -f hello-terraform-operator-f5tw9
to see the Terraform run logs. Once the pod completes, a ConfigMap with the Terraform output content is saved. Let's check out what we've got:
$ kubectl get configmap hello-terraform-operator-output -ojson|jq '.data'
{
"example": "hello world 2401",
"id": "",
"random": "2401"
}
Finally, let's see what the state file looks like. Since I used AWS S3 as the backend for this example, I can simply go to the AWS Console and click my way to find the state file. But just for kicks, I'll try a neat trick and use an S3 pre-signed URL. This is a way to grant public access for a small duration of time to a an S3 object using an HTTP GET request.
$ PSURL=$(aws s3 presign isaaguilar-terraform/terraform-operator/my/awesome/test.tfstate)
$ curl "$PSURL"
{
"version": 4,
"terraform_version": "0.12.23",
"serial": 2,
"lineage": "46da0cb2-bac6-be2f-334f-fac25be96bec",
"outputs": {
"example": {
"value": "hello world 2401",
"type": "string"
},
"id": {
"value": "",
"type": "string"
},
"random": {
"value": "2401",
"type": "string"
}
},
...
Cleanup
To clean up the environment, run
on the Terraform resource. This will trigger a new pod, this time running
in the Terraform run.
$ kubectl delete terraform hello-terraform-operator
terraform.tf.isaaguilar.com "hello-terraform-operator" deleted
All done. If you want to see that there are no more resources in the S3 state file, you can re-use the pre-signed URL and run
.
Summary
Terraform-Operator provides an organized structure to define your infrastructure-as-code with a first-class Kubernetes experience. Using a different backend to store Terraform state is as easy as defining the backend in the resource manifest. Or leave backend definitions alone and let Terrform-Operator organize your tfstate in Consul automatically. To learn more check out the official docs at https://github.com/isaaguilar/terraform-operator/tree/master/docs.
I would love to hear your feedback and expand on the project!
Send bugs, questions, feature requests for the Terraform-Operator to isaaguilar/terraform-operator by posting a new issue!