Keeping the terraform state in a Kubernetes Secret

terraform state Kubernetes Secret

2 min read | by Jordi Prats

Storing the terraform state into a S3 bucket with dynamoDB for locking has become the de facto standard for being able to share the state across an organization. Nevertheless, there are interesting alternatives: We can use a Kubernetes Secret

By using a Kubernetes Secret we no longer need dynamoDB since it uses a Lease for handling locking.

We can configure the backend as follows:

terraform {
  backend "kubernetes" {
    config_path   = "~/.kube/config"
    secret_suffix = "tfstate"
    namespace     = "default"
  }
}

provider "kubernetes" {
  config_path    = "~/.kube/config"
  config_context = "minikube"
}

resource "kubernetes_namespace" "demo_ns" {
  metadata {
    name = "testing-k8s-tfstate"
  }
}

There are many other options to be able to configure the backend to select the appropriate Kubernetes cluster

We can run this code using the usual workflow:

$ terraform init
$ terraform plan
$ terraform apply

Checking the secret that it has created we will see that it is a regular

$ kubectl get secret -n default
NAME                      TYPE                                  DATA   AGE
default-token-t6662       kubernetes.io/service-account-token   3      6m55s
tfstate-default-tfstate   Opaque                                1      63s
$ kubectl describe secret tfstate-default-tfstate
Name:         tfstate-default-tfstate
Namespace:    default
Labels:       app.kubernetes.io/managed-by=terraform
              tfstate=true
              tfstateSecretSuffix=tfstate
              tfstateWorkspace=default
Annotations:  encoding: gzip

Type:  Opaque

Data
====
tfstate:  511 bytes

It is stored as a compressed json, so we can read it's data using the following command:

$ kubectl get secret tfstate-default-tfstate -o jsonpath="{.data.tfstate}" | base64 -d | gzip -d -
{
  "version": 4,
  "terraform_version": "1.1.3",
(...)

As you might already noticed, the workspace name is part of the secret's name, so if we rerun the code using a new namespace (deleting the namespace to make it work):

$ kubectl delete ns testing-k8s-tfstate
$ terraform workspace new demo
$ terraform apply -auto-approve

We'll find a new Secret for each of the workspaces we are using:

$ kubectl get secret -n default
NAME                      TYPE                                  DATA   AGE
default-token-t6662       kubernetes.io/service-account-token   3      9m29s
tfstate-default-tfstate   Opaque                                1      3m37s
tfstate-demo-tfstate      Opaque                                1      89s

Posted on 19/04/2022