Terraform
On this page
What is the Terraform Driver?
The Terraform Driver is a fully customizable Driver offered by the Humanitec Platform Orchestrator, allowing you to provision any Resource Type your Workloads depend on. It does that using Terraform and the Terraform providers of your choice.
Platform teams usually set up the Driver for the developers’ requested Resource Types. The setup involves creating
Resource Definitions
using the
Humanitec Terraform provider
, and providing the corresponding Terraform code to provision those resources. The Driver will execute the code (terraform apply
) at deployment time using standard Terraform just like in any other CI/CD setting.
The Humanitec Terraform Driver is not to be confused with the Humanitec Terraform provider .
The provider can be used to create Humanitec objects such as Resource Definitions or Applications in your Humanitec organization using Terraform.
The driver is used to provision infrastructure resources outside Humanitec such as cloud storage, databases, or networks, using Terraform.
Why use the Terraform Driver?
Using Terraform and the Terraform Driver to provision resources has a number of advantages over other Humanitec built-in drivers:
- Declarative: It is declarative, allowing true Infrastructure-as-Code (IaC) with all its associated benefits, like diffs, version control, and familiar developer workflows.
- Adaptive: Terraform is widely adopted and offers a vast range of providers , covering all major public clouds as well as many 3rd party offerings.
- Integrate: It provides an easy way to do custom code in Humanitec.
- Control: Since you control the Terraform code, you have full control of how it operates. You can test the code independently before handing it to the Driver for execution.
How it works
The Terraform Driver and its connecting components can be positioned using the planes of the Humanitec reference architectures as a backdrop.
Please note that this diagram shows just one possible setup. There are several options on how to provide Terraform code (see Inputs ), where to host the Runner, and where to store state. Detailed documentation on these topics is currently being prepared.
The Platform Orchestrator performs these steps when executing a Deployment into an Application Environment, which leads to the involving of the Terraform Driver.
- The Orchestrator performs resource matching using all relevant Resource Definitions. The matched Resource Definition uses the Terraform Driver.
- The Orchestrator passes the values of all the inputs to the Terraform Driver as configured in the Resource Definition.
- The Driver instantiates an isolated Runner instance to execute Terraform. The Runner includes the Terraform CLI.
- The Runner downloads the Terraform Code from the Git repository that is configured in the Resource Definition.
- If the Humanitec SaaS solution is used and the Terraform backend configuration uses local state, the Runner downloads an existing state file from an Orchestrator-managed storage. Otherwise, the configured backend is used as is.
- The Runner performs
terraform init
andterraform apply
in the repository directory configured in the Resource Definition. For theapply
, any variables and secrets configured in the Resource Definition are passed in as input variables on the command line . - After completion, if the Terraform state was previously downloaded, the Runner uploads the updated Terraform state to the Orchestrator-managed storage.
- The Runner finishes and returns a response to the driver.
- In particular, the Runner passes any output values of the Terraform module back to the driver, who passes them on to the Orchestrator.
- The Driver destroys the Runner instance.
- Further processing of the response inside the Orchestrator occurs like with any other driver.
Using Terraform providers
There is no restriction on which Terraform providers can be used. You may need to provide the Driver with credentials to access the target infrastructure accessed by the provider; for example, credentials for an AWS account or Azure service principal.
There must be a network line-of-sight to the target infrastructure from the physical execution environment of the Runner.
Providers generally need credentials to access the target infrastructure. See our credentials example for ways to handle them.
Using Terraform Backends
Terraform state is stored according to the backend configured in your Terraform code.
See our backends example for ways to handle backends.
Matching Terraform Modules to Resource Definitions
The purpose of a Resource Definition is to provision exactly one resource of a particular type thanks to a select driver. In contrast, the Terraform module referenced by the Driver may contain several related resources of any type, which may go beyond the type of the Resource Definition.
While this will technically work and all resources will be properly provisioned, it may make sense to review and potentially adjust the module structure to reap the full benefits of the Platform Orchestrator’s dynamic configuration management. This involves:
- Ideally creating a 1:1 matching of Resource Definitions to Terraform modules (“molecular Terraform”)
- Providing the proper module output values in case you want to reference them as Dependent Resources in your workload definitions
In doing so, you will gain maximum reusability of your code and achieve standardization intentional across applications and teams. Because the Resource Definition matches the provisioned reality, the Platform Orchestrator can act as a source of truth regarding active resources in an Application Environment.
Terraform Driver reference
This Driver runs Terraform to provision resources. The Terraform definition can be provided in-line or reference a Terraform module in a Git repository.
Running the Terraform Runner in a target cluster
The Terraform Driver can be configured to execute the Terraform scripts as part of a Kubernetes Job execution in a target Kubernetes cluster, instead of in the Humanitec infrastructure.
This mode involves the creation of Kubernetes resources:
- Some Kubernetes Secrets to cache Terraform scripts, outputs and sensitive data.
- A Kubernetes Job which executes Terraform.
Running the scripts in a cluster outside of the Humanitec infrastructure requires the configuration of a custom backend as part of the provided Terraform scripts.
Details about how to provide cluster access to the Humanitec Platform Orchestrator, and how to customize the Runner Pod and image source, are specified below .
Properties
Property | Description |
---|---|
Resource type | Any |
Account type | aws-role , aws , azure-identity , azure , gcp-identity , gcp |
Inputs
Values
Name | Type | Description |
---|---|---|
runner_mode |
string | It can be managed or custom-kubernetes . Defaults to managed . It determines where the Terraform Runner runs. |
append_logs_to_error |
boolean | If set to true , Terraform logs will be appended to error messages returned by the driver. Defaults to false . |
credentials_config |
object | [Optional] An object describing how the provider credentials should be available to the Terraform scripts. |
files |
object | [Optional] A Map of filenames to their content to create in the directory before terraform is executed. |
script |
string | [Optional] An inline terraform definition in HCL format. If specified with source , it works as override.tf |
source |
object | [Optional] A Git repository to use for the Terraform definition. |
variables |
object | A Map of variable names that are used as inputs to the Terraform definition and their values. |
runner |
object | [Optional] Required if runner_mode is custom-kubernetes , specifies the values of the cluster where the Terraform Runner will run. |
manifests_output |
string | [Optional] The name of a Terraform output that contains a list of
Manifest Location
objects in JSON format. These will be returned as the manifests property in the Driver outputs. See
Generating manifests
for more details. |
At least one of source
or script
must be specified.
Placeholders are resolved by the Platform Orchestrator before the Terraform Driver is called. This means that the content of the placeholder will already be resolved in the Driver inputs that the Terraform Driver receives. (This includes any placeholders in comments.)
Placeholders will not be resolved in Terraform code pulled from a Git source
.
credentials_config object
The credentials_config
object describes how the provider credentials should be made available to the Terraform scripts.
These
examples using dynamic credentials
utilize the credentials_config
mechanism.
Property | Type | Description |
---|---|---|
environment |
object | Map whose keys are the environment values expected by the Terraform scripts and value can be flattened credential keys (with . as delimiter) or credentials file path.If the value is * it means that the whole credentials will be available at the specified environment variable. If the value at the specified key is an object, the environment variable assumes the stringified value of it.Example: AWS_ACCESS_KEY_ID: aws.accessKeyId |
file |
string | File path for the file that will be built from credentials. The file path can’t be absolute or use dots. Example: credentials.tf |
variables |
object | Map whose keys are variables expected by the Terraform scripts and values can be flattened credential keys (with . as delimiter) or credentials file path.If the value is * it means that the whole credentials will be available at the specified variable.If the key contains . , it is considered as a sub-field of a variable of type objects (e.g. “SECRET.ID: aws_access_key_id ” generates a variable SECRET of type object which a field ID whose value is fetched by creds at path aws_access_key_id ).If the value at the specified key is an object, the variable is supplied to the scripts as an object, otherwise as a string. Example: access_key: aws.accessKeyId |
Source object
The source
object defines how the Driver uses Terraform definitions that are stored in Git. In order for the Driver to use the source-based Terraform definitions, the repository must be accessible to the Terraform Runner and credentials must be supplied if necessary.
Property | Type | Description |
---|---|---|
path |
string | [Optional] Relative path to the scripts: path/to/scripts . |
rev |
string | [Optional] Branch name, tag, or commit SHA, for example, /refs/heads/main |
url |
object | Repository URL, for example, github.com:my-org/project.git for SSH or https://github.com/my-org/project.git for HTTPS. |
username |
object | [Optional] Username to authenticate. The default is git . |
According to the value specified in the rev
property, the Terraform Runner fetches the Terraform scripts in the repository at a specific reference:
- If
rev
is a git reference (e.g.refs/heads/my-branch
,refs/tags/my-tag
,my-branch
ormy-tag
), that branch or tag is checked out. - If
rev
is a valid commit SHA, that commit is checked out. - If
rev
is empty, the HEAD of the repository is checked out (generallymain
ormaster
).
This means that if rev
is not a commit SHA, we can’t ensure that the scripts used to provision a resource match exactly the ones used to delete the same resource as a branch (or a tag) might have been updated compared to when the resource provisioning happened.
Runner object
Available only in custom-kubernetes
runner_mode
.
The runner
object defines the properties the Driver uses to authenticate to the target cluster where the Terraform Runner should run.
Property | Type | Description |
---|---|---|
cluster_type |
string | Type of the cluster, must be one of gke , eks , aks or k8s . This property affects the expected structure for cluster property. |
cluster |
object | Object which contains the non-sensitive data needed to access the target cluster. |
service_account |
string | [Optional] The Service Account the Terraform Runner Job should run with. |
account |
string | [Optional] A Fully Qualified Cloud Account ID in the format <orgId>/<accountId> (not including <> ). It must be specified if secrets.runner.credentials is empty. |
namespace |
string | [Optional] The Namespace where the Runner should run. If empty, the Driver assumes the Namespace is humanitec-terraform . |
runner_pod_template |
string | [Optional] The Pod Template Spec manifest which defines the Runner Job Pod in the target cluster. |
To be able to successfully access the target k8s cluster, the object cluster
should include specific fields, according to the value of cluster_type
:
aks
cluster type expects the non-secret AKS Cluster values .k8s
cluster type expects the non-secret Cluster values .eks
cluster type expects the non-secret EKS Cluster values .gke
cluster type expects the non-secret GKE Cluster values .
The Kubernetes service account used by the Runner in the target namespace
must be created and configured to have the permissions needed by the Runner to complete its execution.
Kubernetes Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
name: humanitec-tf-runner
namespace: humanitec-terraform
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: humanitec-terraform
name: humanitec-tf-runner
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create", "get", "delete", "list", "update", "deletecollection"]
# Only needed if the chosen Terraform Backend is kubernetes
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["create", "get", "list", "update", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: humanitec-tf-runner
namespace: humanitec-terraform
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: humanitec-tf-runner
subjects:
- kind: ServiceAccount
name: humanitec-tf-runner
namespace: humanitec-terraform
Runner Pod Template
The Pod Template Spec defined in the runner_pod_template
field will be merged to the default one defined by the Terraform Driver as specified below:
spec:
containers:
- name: worker
image: ghcr.io/humanitec/terraform-runner:<version>
imagePullPolicy: IfNotPresent
command:
- /opt/resource-driver-terraform/worker
resources:
limits:
memory: 1Gi
requests:
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsGroup: 2000
runAsNonRoot: true
runAsUser: 2000
seccompProfile:
type: RuntimeDefault
env:
- name: WORKDIR
value: /home/runneruser/workspace
...
volumeMounts:
- mountPath: /home/runneruser/workspace/inputs/inputs.json
name: inputs
subPath: inputs.json
...
restartPolicy: Never
serviceAccountName: <runner.service_account>
A common use case is specifying your own registry to use for the Runner image through a runner_pod_template
like this:
spec:
containers:
- name: worker
image: <your-private-image>
imagePullSecrets:
- name: regcred
imagePullPolicy: IfNotPresent
ghcr.io/humanitec/terraform-runner:1.1.18
as the upstream source for greatest stability. We will continuously update the available version information at this place.As the Pod defined via runner.runner_pod_template
is still supposed to work in combination with the Humanitec Terraform Driver and run the Runner image built by Humanitec, not all the fields in the manifest can be overriden:
metadata.namespace
can’t be overriden as it is already defined at Job level and specified inrunner.namespace
value.- Some properties in the
worker
container can’t be overriden:args
,command
,env
,envFrom
,volumeMounts
andworkingDir
as defined by the Humanitec Terraform Driver.
Secrets
Name | Type | Description |
---|---|---|
files |
object | [Optional] A Map of filenames to their content to create in the directory before terraform is executed. |
source |
object | [Optional] Credentials for the Git repository. |
terraform |
object | [Optional] Secrets to be used by Terraform CLI Configuration. |
variables |
object | A Map of variable names that are used as sensitive inputs to the Terraform definition. |
runner |
object | [Optional] It can be used only when runner_mode is custom-kubernetes . Specifies the credentials to gain access to the target cluster. |
Source object
Credentials to be used to access the Git repository. The choice of credentials depends on the url
format.
Property | Type | Description |
---|---|---|
password |
string | [Optional] Password or Personal Account Token - for HTTPS. |
ssh_key |
string | [Optional] SSH Private key - for connections over SSH. |
Runner object
Available only in custom-kubernetes
runner_mode
.
The runner
object defines the credentials the Driver needs to authenticate to the target cluster where the Terraform Runner should run. It can be empty if an account
is specified in the
runner object
.
Property | Type | Description |
---|---|---|
credentials |
object | Provider’s credentials object. |
agent_url |
object | [Optional] The signed URL produced by the humanitec/agent driver. It is expected to be a reference to the url output of a Agent resource. |
The content of the credentials
property depends on the value of cluster_type
in the
runner object
:
aks
cluster type expects the AKS Service Principal Secrets .k8s
cluster type expects the credentials needed to access a Kubernetes cluster using vanillakubeconfig
parameters.eks
cluster type expects AWS credentials .gke
cluster type expects a GCP Service Account Key .
Terraform object
Secrets to be used by Terraform CLI Configuration.
Property | Type | Description |
---|---|---|
tokens |
object | Map of domain - bearer tokens to be used to authenticate http requests made by Terraform. |
Terraform has a feature allowing bearer tokens for requests to specific domains to be defined via environment variables. The Environment variables start with the prefix TF_TOKEN_
followed by the domain name with all .
replaced with _
and all non-ASCII characters converted to their punycode equivalent. See
Terraform CLI: Environment Variable Credentials
.
The keys of the token
object should be the transformed domain names without the TF_TOKEN_
prefix. The value should the bearer token. For example, to provide a token for Terraform Cloud, app.terraform.io
, the following entry could be used:
tokens:
app_terraform_io: my-personal-access-token-for-terraform-cloud
or when using a secret reference :
tokens:
app_terraform_io:
store: my-secret-store
ref: my-personal-access-token-for-terraform-cloud
Notes
Interaction with Humanitec Resources
Resource Types in Humanitec have a specified Resource Output Schema. In order for a resource to be usable by the Platform Orchestrator, the Terraform module must specify output
variables that exactly match this schema.
For example, the
s3
Resource Type
has the following output schema:
Name | Type | Description |
---|---|---|
bucket |
string | The bucket name |
region |
string | The region the bucket is hosted in |
aws_access_key_id |
string, secret | The AWS access key id |
aws_secret_access_key |
string, secret | The AWS secret access key |
Therefore, the Terraform module should have outputs defined similar to:
output "region" {
value = module.aws_s3.s3_bucket_region
}
output "bucket" {
value = module.aws_s3.s3_bucket_bucket_domain_name
}
output "aws_access_key_id" {
value = var.credentials.access_key
sensitive = true
}
output "aws_secret_access_key" {
value = var.credentials.secret_key
sensitive = true
}
Examples
See the Terraform Driver examples page for a collection of examples.
FAQ
Where is Terraform state stored?
State is stored according to the backend configured in your Terraform code. Use the driver variables and secrets to provide configuration data for your backend.
If you want to access your state, provide a Terraform backend
configuration which instructs the runner to store the state in a storage you control, such as
s3
,
gcs
, or
azurerm
.
If a backend
configuration is not provided, the state is stored in an isolated secured storage owned by us and you cannot access it. Supplying a local
backend configuration is discouraged as it will be ignored and our storage will be used instead.
For a Kubernetes Terraform backend, the runner should be supplied with a Service Account with permission to manipulate K8s Secrets. Specify the required values through the driver variables and secrets.
What happens if my Terraform state is broken?
If the state breaks during Terraform execution by the driver, it depends on where your state is stored.
If you store your state in a self-managed backend (recommended), make sure to set up regular backups to a secondary, secure location. Restore a previous state revision from the backup.
If Humanitec is storing your state, contact Humanitec support to get the state file sent to you. Analyze and fix the state, then have it replaced via support.
Is my Terraform state encrypted?
If you are using your own backend (recommended), the encryption of the selected storage service applies, which we do not control.
If the Orchestrator stores your state, then it is encrypted at rest.
What happens when the credentials for Terraform expire?
Expired credentials either to a Git source or a target infrastructure will lead to a failed Deployment. Renew the credentials and restart the deployment.
Where can I see the Terraform plan?
Right now, you cannot see the plan. We are working on ways to increase the visibility into the Terraform process.
When does the driver destroy resources?
Destruction of the resources (terraform destroy
) occurs when the Orchestrator Resource is deprovisioned according to the
lifecycle of the resource
. In particular, it is not sufficient to remove a resource dependency in order for the resource to be deprovisioned to allow for rollbacks without losing data in stateful resources.
Do I need to split up my Terraform modules?
The Humanitec Platform Orchestrator is all about standardization by design. Fine-grained Terraform modules allow maximum reusability and maximum standardization across applications and teams, making your life a lot easier. This tutorial provides guidance on how to smoothly adopt existing Terraform code.
How do I deploy a Resource of a type without a matching Resource Type definition?
To provision anything foundational such as networks or Azure Resource Groups use the “ base environment ” Resource Type (applies to all workloads of an Application) or “workload” Resource Type (applies to a specific workload). You do not need a Resource Type definition in that case.
For any other type of resource, Humanitec currently must add the Resource Definition first. Please contact Humanitec support.
Can I use any Terraform provider in the Terraform registry?
Yes.
Do I need a Terraform Cloud account to use the driver?
No, unless you wish to utilize Terraform’s remote backend. In that case you need to supply configuration data and credentials to access your Terraform cloud workspace.
What Terraform version is the runner using?
For the Humanitec SaaS system we are currently operating the runner with a recent MPL licensed version of Terraform <1.6.0.
For a self-hosted Platform Orchestrator, the runner image is updated along with platform installation following the same version constraint.
What is the public IP address of the SaaS runner?
The runner in the Humanitec SaaS system uses one of the public IPs listed here .
Can I use a Cloud Account configured in my Humanitec organization to provide credentials to the driver?
Yes. You will need to use the
credentials_config
object
to map the Cloud Account credentials to either Terraform variables or Environment Variables. See
Dynamic Credentials with the Terraform Driver
for examples of using Cloud Accounts.
I need to reference Terraform modules stored in private git repos - how do I inject custom git configuration?
Terraform can use modules from various sources including git . The documentation states : Terraform installs modules from Git repositories by running git clone, and so it will respect any local Git configuration set on your system, including credentials. To access a non-public Git repository, configure Git with suitable credentials for that repository.
Custom git configuration can be provided by including a file with name .gitconfig
in the files
input. This file can be either a value or a secret depending on whether it contains sensitive credentials or not.
Example Resource Definition using git config
example-def.yaml
(
view on GitHub
)
:
apiVersion: entity.humanitec.io/v1b1
metadata:
id: example-git-config
entity:
criteria: {}
driver_inputs:
values:
files:
.gitconfig: |
[url "https://github.com/Invicton-Labs/"]
insteadOf = https://example.com/replace-with-git-config/
script: |
module "uuid" {
# We rely on the git-config above to rewrite this URL into one that will work
source = "git::https://example.com/replace-with-git-config/terraform-random-uuid.git?ref=v0.2.0"
}
output "bucket" {
value = module.uuid.uuid
}
driver_type: humanitec/terraform
name: example-git-config
type: s3
kind: Definition