Use Cases
The manual mode: plan and manual apply
For the plan & manual approval workflow, please either set .spec.approvePlan
to be the blank value, or omit the field.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
+ approvePlan: "" # or you can omit this field
- approvePlan: "auto"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
Then the controller will tell you how to use field .spec.approvePlan
to approve the plan.
After making change and push, it will apply the plan to create real resources.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: hello-world
namespace: flux-system
spec:
+ approvePlan: "plan-main-b8e362c206" # first 8 digits of a commit hash is enough
- approvePlan: ""
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
The drift detection only mode: plan and apply will be skipped
To only run drift detection, skipping the plan and apply stages, set .spec.approvePlan
to disable
.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: hello-world
namespace: flux-system
spec:
approvePlan: "disable"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
Disable Drift Detection
Drift detection is enabled by default. Use the .spec.disableDriftDetection
field to disable:
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
disableDriftDetection: true
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
Use with AWS EKS IRSA
AWS Elastic Kubernetes Service (EKS) offers IAM Roles for Service Accounts (IRSA) as a mechanism by which to provide credentials for the Terraform controller.
You can use eksctl
to associate an OIDC provider with your EKS cluster, for example:
eksctl utils associate-iam-oidc-provider --cluster CLUSTER_NAME --approve
Then follow the instructions here
to add a trust policy to the IAM role which grants the necessary permissions for Terraform.
Please note that if you have installed the controller following the README, then the namespace:serviceaccountname
will be flux-system:tf-controller
. You'll obtain a Role ARN to use in the next step.
Finally, annotate the ServiceAccount with the obtained Role ARN in your cluster:
kubectl annotate -n flux-system serviceaccount tf-controller eks.amazon.com/role-arn=ROLE_ARN
Setting Terraform Variables
This is a breaking change of the v1alpha1
API.
Users who are upgrading from TF-controller <= 0.7.0 require updating varsFrom
,
from a single object:
varsFrom:
kind: ConfigMap
name: cluster-config
to be an array of object, like this:
varsFrom:
- kind: ConfigMap
name: cluster-config
You can pass variables to Terraform using the vars
and varsFrom
fields.
Inline variables can be set using vars
. The varsFrom
field accepts a list of ConfigMaps / Secrets.
You may use the varsKeys
property of varsFrom
to select specific keys from the input or omit this field
to select all keys from the input source.
Note that in the case of the same variable key being passed multiple times, the controller will use
the lattermost instance of the key passed to varsFrom
.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
vars:
- name: region
value: us-east-1
- name: env
value: dev
- name: instanceType
value: t3-small
varsFrom:
- kind: ConfigMap
name: cluster-config
varsKeys:
- nodeCount
- instanceType
- kind: Secret
name: cluster-creds
The vars
field supports HCL string, number, bool, object and list types. For example, the following variable can be populated using the accompanying Terraform spec:
variable "cluster_spec" {
type = object({
region = string
env = string
node_count = number
public = bool
})
}
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
vars:
- name: cluster_spec
value:
region: us-east-1
env: dev
node_count: 10
public: false
Managing Terraform State
By default, tf-controller
will use the Kubernetes backend to store the Terraform statefile in cluster.
The statefile is stored in a secret named: tfstate-default-${secretSuffix}
. The default suffix
will be the name of the Terraform resource, however you may override this setting using .spec.backendConfig.secretSuffix
.
You can disable the backend
Backup the statefile
For the following terraform
resources:
$ kubectl get terraform
NAME READY STATUS AGE
my-stack Unknown Initializing 28s
We can export the state like this:
kubectl get secret tfstate-default-my-stack -ojsonpath='{.data.tfstate}' | base64 -d | gzip -d > terraform.tfstate
Restore the statefile
To restore the statefile or import an existing statefile we can use the following operation:
gzip terraform.tfstate
NAME=my-stack
kubectl create secret \
generic tfstate-default-${NAME} \
--from-file=tfstate=terraform.tfstate.gz \
--dry-run=client -o=yaml \
| yq e '.metadata.annotations["encoding"]="gzip"' - > tfstate-default-${NAME}.yaml
kubectl apply -f tfstate-default-${NAME}.yaml
Health Checks
For some resources, it may be useful to perform health checks on them to verify that they are ready to accept connection before the terraform goes into Ready
state:
# main.tf
output "rdsAddress" {
value = "mydb.xyz.us-east-1.rds.amazonaws.com"
}
output "rdsPort" {
value = "3306"
}
output "myappURL" {
value = "https://example.com/"
}
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
healthChecks:
- name: rds
type: tcp
address: "{{.rdsAddress}}:{{.rdsPort}}" # uses standard Go package template format to parse outputs to url
timeout: 10s # optional, defaults to 20s
- name: myapp
type: http
url: "{{.myappURL}}"
timeout: 5s
- name: url_not_from_output
type: http
url: "https://example.org"
Destroy resources on deletion
The resources created by terraform are not defaulted to destroyed after the object is deleted from the cluster. To enable destroy resources on object deletion, set .spec.destroyResourcesOnDeletion
to true
.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
destroyResourcesOnDeletion: true
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
Write outputs to a secret
Outputs created by Terraform can be written to a secret using .spec.writeOutputsToSecret
.
Write all outputs
We can specify a target secret, and the controller will write all outputs to the secret by default.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
writeOutputsToSecret:
name: helloworld-output
Write outputs selectively
We can choose only a subset of outputs by specify output names we'd like to write in outputs
array.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
writeOutputsToSecret:
name: helloworld-output
outputs:
- hello_world
- my_sensitive_data
Output name mapping
Some time we'd like to use rename an output, so that it can be consumed by other Kubernetes controllers.
For example, we might retrieve a key from a Secret manager, and it's an AGE key, which must be ending with ".agekey" in the secret.
In this case, we need to rename the output. TF-controller supports mapping output name using the "old_name:new_name" format.
In the following example, we write age_key
output as age.agekey
entry in the helloworld-output
Secret's data.
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
name: helloworld
namespace: flux-system
spec:
approvePlan: "auto"
interval: 1m
path: ./
sourceRef:
kind: GitRepository
name: helloworld
namespace: flux-system
writeOutputsToSecret:
name: helloworld-output
outputs:
- age_key:age.agekey