Terraform Driver
Resource Definitions using the Terraform Driver
This section contains example Resource Definitions using the Terraform Driver.
Azure blob
Use the Terraform Driver to provision Azure Blob storage resources.
ssh-secret-refs.tf
: uses secret references to obtain an SSH key from a secret store to connect to the Git repo providing the Terraform code.
ssh-secret-refs.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "azure-blob" {
driver_type = "humanitec/terraform"
id = "azure-blob"
name = "azure-blob"
type = "azure-blob"
driver_inputs = {
# All secrets are read from a secret store using secret references
secret_refs = jsonencode({
variables = {
client_id = {
ref = var.client_id_secret_reference_key
store = var.secret_store
}
client_secret = {
ref = var.client_secret_secret_reference_key
store = var.secret_store
}
}
source = {
# Using an SSH key to authenticate against the Git repo providing the Terraform module
ssh_key = {
ref = var.ssh_key_secret_reference_key
store = var.secret_store
}
}
})
values_string = jsonencode({
source = {
path = "azure-blob/terraform/"
rev = "refs/heads/main"
url = "[email protected]:my-org/my-repo.git"
}
variables = {
# Variables for the Terraform module located in the Git repo
tenant_id = var.tenant_id
subscription_id = var.subscription_id
resource_group_name = var.resource_group_name
name = var.name
prefix = var.prefix
account_tier = var.account_tier
account_replication_type = var.account_replication_type
container_name = var.container_name
container_access_type = var.container_access_type
}
})
}
}
Backends
Backends
Humanitec manages the state file for the local
backend. This is the backend that is used if no backend is specified.
In order to manage your own state, you will need to define your own backend. We recommend that the backend configuration is defined in the script
part of the Resource Definition - i.e. as an override.tf
file (see the Inputs of the Terraform Driver). This allows the backend to be tuned per resource instance.
In order to centralize configuration, it is also recommended to create a config
resource that can be used to centrally manage the backend configuration.
In this example, there are two config
resources defined. Both are using the Template Driver to generate outputs for use in the example Resource Definition:
backend-config.yaml
which provides shared backend configuration that can be used across Resource Definitions.account-config-aws.yaml
which provides credentials used by the provider.
The example Resource Definition s3-backend.yaml
does the following:
- Configures a backend using the
backend-config.yaml
. - Configures the provider using a different set of credentials from
account-config-aws.yaml
. - Provisions an s3 bucket.
account-config-aws.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "account-config-aws" {
driver_type = "humanitec/template"
id = "account-config-aws"
name = "account-config-aws"
type = "config"
driver_account = "aws-credentials"
driver_inputs = {
values_string = jsonencode({
"templates" = {
"secrets" = <<END_OF_TEXT
aws_access_key_id: {{ .driver.secrets.account.aws_access_key_id }}
aws_secret_access_key: {{ .driver.secrets.account.aws_secret_access_key }}
credentials_file: |
[default]
aws_access_key_id = {{ .driver.secrets.account.aws_access_key_id }}
aws_secret_access_key = {{ .driver.secrets.account.aws_secret_access_key }}
END_OF_TEXT
}
})
}
}
resource "humanitec_resource_definition_criteria" "account-config-aws_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.account-config-aws.id
res_id = "aws-account"
}
account-config-aws.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: account-config-aws
entity:
criteria:
# This res_id is used in the resource reference in the s3-backend Resource Definition.
- res_id: aws-account
# The driver_account references a Cloud Account configured in the Platform Orchestrator.
# Replace with the name your AWS Cloud Account.
driver_account: aws-credentials
driver_inputs:
values:
templates:
secrets: |
aws_access_key_id: {{ .driver.secrets.account.aws_access_key_id }}
aws_secret_access_key: {{ .driver.secrets.account.aws_secret_access_key }}
credentials_file: |
[default]
aws_access_key_id = {{ .driver.secrets.account.aws_access_key_id }}
aws_secret_access_key = {{ .driver.secrets.account.aws_secret_access_key }}
driver_type: humanitec/template
name: account-config-aws
type: config
backend-config.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "tf-backend-config" {
driver_type = "humanitec/template"
id = "tf-backend-config"
name = "tf-backend-config"
type = "config"
driver_account = "aws-credentials"
driver_inputs = {
values_string = jsonencode({
"templates" = {
"outputs" = <<END_OF_TEXT
bucket: my-terraform-state-bucket
key_prefix: "tf-state/"
region: us-east-1
END_OF_TEXT
"secrets" = <<END_OF_TEXT
credentials_file: |
[default]
aws_access_key_id = {{ .driver.secrets.account.aws_access_key_id }}
aws_secret_access_key = {{ .driver.secrets.account.aws_secret_access_key }}
END_OF_TEXT
}
})
}
}
resource "humanitec_resource_definition_criteria" "tf-backend-config_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.tf-backend-config.id
res_id = "tf-backend"
}
backend-config.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: tf-backend-config
entity:
criteria:
# This res_id is used in the resource reference in the s3-backend Resource Definition.
- res_id: tf-backend
# The driver_account references a Cloud Account configured in the Platform Orchestrator.
# Replace with the name of your AWS Cloud Account.
driver_account: aws-credentials
driver_inputs:
values:
templates:
outputs: |
bucket: my-terraform-state-bucket
key_prefix: "tf-state/"
region: us-east-1
secrets: |
credentials_file: |
[default]
aws_access_key_id = {{ .driver.secrets.account.aws_access_key_id }}
aws_secret_access_key = {{ .driver.secrets.account.aws_secret_access_key }}
driver_type: humanitec/template
name: tf-backend-config
type: config
s3-backend.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "s3-backend-example" {
driver_type = "humanitec/terraform"
id = "s3-backend-example"
name = "s3-backend-example"
type = "s3"
driver_inputs = {
values_string = jsonencode({
"script" = <<END_OF_TEXT
variable "region" {}
terraform {
backend {
bucket = "$${resources.config#tf-backend.outputs.bucket}"
key = "$${resources.config#tf-backend.outputs.prefix}$${context.app.id}/$${context.env.id}/$${context.res.type}.$${context.res.class}/$${context.res.id}"
region = "$${resources.config#tf-backend.outputs.region}"
shared_credentials_files = ["backend_creds"]
}
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
region = var.region
# The file is defined above. The provide will read the creds from this file.
shared_credentials_files = ["aws_creds"]
}
output "bucket" {
value = aws_s3_bucket.bucket.bucket
}
output "region" {
value = var.region
}
resource "aws_s3_bucket" "bucket" {
bucket = "$\{replace("$${context.res.id}", "^.*\.", "")}-standard-$${context.env.id}-$${context.app.id}-$${context.org.id}"
tags = {
Humanitec = true
}
}
END_OF_TEXT
"variables" = {
"region" = "us-east-1"
}
})
secret_refs = jsonencode({
"files" = {
"aws_creds" = {
"value" = "$${resources.config#aws-account.outputs.credentials_file}"
}
"backend_creds" = {
"value" = "$${resources.config#tf-backend.outputs.credentials_file}"
}
}
})
}
}
s3-backend.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: s3-backend-example
entity:
driver_inputs:
# We are using secret references to write the credentials using their "value" element.
# Using "secrets" instead would work too, but due to the placeholders in the values, the
# Platform Orchestrator will resolve them to the exact secret references used here
# in the resulting Resource Definition.
# This structure therefore represents the way the Platform Orchestrator manages the Resource Definition
# and is better suited to any round-trip engineering, if needed.
secret_refs:
files:
# Credentials for the AWS provider
aws_creds:
# Using the resource ID "#aws-account" to fulfill the matching criteria of the "account-config-aws" config resource.
value: ${resources.config#aws-account.outputs.credentials_file}
# In general, the credentials for the backend should be different from those of the provider
backend_creds:
# Using the resource ID "#tf-backend" to fulfill the matching criteria of the "tf-backend-config" config resource.
value: ${resources.config#tf-backend.outputs.credentials_file}
values:
script: |
variable "region" {}
terraform {
backend {
bucket = "${resources.config#tf-backend.outputs.bucket}"
key = "${resources.config#tf-backend.outputs.prefix}${context.app.id}/${context.env.id}/${context.res.type}.${context.res.class}/${context.res.id}"
region = "${resources.config#tf-backend.outputs.region}"
shared_credentials_files = ["backend_creds"]
}
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
region = var.region
# The file is defined above. The provide will read the creds from this file.
shared_credentials_files = ["aws_creds"]
}
output "bucket" {
value = aws_s3_bucket.bucket.bucket
}
output "region" {
value = var.region
}
resource "aws_s3_bucket" "bucket" {
bucket = "$\{replace("${context.res.id}", "^.*\.", "")}-standard-${context.env.id}-${context.app.id}-${context.org.id}"
tags = {
Humanitec = true
}
}
variables:
region: us-east-1
driver_type: humanitec/terraform
name: s3-backend-example
type: s3
# Supply matching criteria
criteria: []
Co provision
Resource co-provisioning
This section contains an example of Resource Definitions using the Terraform Driver and illustrating the co-provisioning concept.
Scenario: For each AWS S3 bucket resource an AWS IAM policy resource must be created. The bucket properties (region, ARN) should be passed to the policy resource. In other words, an IAM Policy resource depends on a S3 resource, but it needs to be created automatically.
Any time a Workload references a S3 resource using this Resource Definition, an IAM Policy resource will be co-provisioned and reference the S3 resource. The resulting Resource Graph will look like this:
flowchart LR
R1(Workload) --->|references| R2(S3)
N1(AWS Policy) --->|references| R2
classDef pClass stroke-width:1px
classDef rClass stroke-width:2px
classDef nClass stroke-width:2px,stroke-dasharray: 5 5
class R1 pClass
class R2 rClass
class N1 nClass
aws-policy-co-provision.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "aws-policy-co-provision" {
driver_type = "humanitec/terraform"
id = "aws-policy-co-provision"
name = "aws-policy-co-provision"
type = "aws-policy"
driver_account = "aws"
driver_inputs = {
values_string = jsonencode({
"variables" = {
"REGION" = "$${resources.s3.outputs.region}"
"BUCKET" = "$${resources.s3.outputs.bucket}"
"BUCKET_ARN" = "$${resources.s3.outputs.arn}"
}
"credentials_config" = {
"variables" = {
"ACCESS_KEY_ID" = "AccessKeyId"
"ACCESS_KEY_VALUE" = "SecretAccessKey"
}
}
"script" = <<END_OF_TEXT
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "aws" {
region = var.REGION
access_key = var.ACCESS_KEY_ID
secret_key = var.ACCESS_KEY_VALUE
}
# ... Terraform code reduced for brevity
resource "aws_iam_policy" "bucket" {
name = "$\{var.BUCKET}-policy"
policy = data.aws_iam_policy_document.main.json
}
data "aws_iam_policy_document" "main" {
statement {
effect = "Allow"
actions = [
"s3:GetObject",
"s3:ListBucket",
]
resources = [
var.BUCKET_ARN,
]
}
}
END_OF_TEXT
})
}
}
aws-policy-co-provision.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: aws-policy-co-provision
entity:
name: aws-policy-co-provision
type: aws-policy
driver_type: humanitec/terraform
# Use the credentials injected via the driver_account to set variables as expected by your Terraform code
driver_account: aws
driver_inputs:
values:
variables:
REGION: ${resources.s3.outputs.region}
BUCKET: ${resources.s3.outputs.bucket}
BUCKET_ARN: ${resources.s3.outputs.arn}
credentials_config:
variables:
ACCESS_KEY_ID: AccessKeyId
ACCESS_KEY_VALUE: SecretAccessKey
script: |
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "aws" {
region = var.REGION
access_key = var.ACCESS_KEY_ID
secret_key = var.ACCESS_KEY_VALUE
}
# ... Terraform code reduced for brevity
resource "aws_iam_policy" "bucket" {
name = "$\{var.BUCKET}-policy"
policy = data.aws_iam_policy_document.main.json
}
data "aws_iam_policy_document" "main" {
statement {
effect = "Allow"
actions = [
"s3:GetObject",
"s3:ListBucket",
]
resources = [
var.BUCKET_ARN,
]
}
}
s3-co-provision.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "s3-co-provision" {
driver_type = "humanitec/terraform"
id = "s3-co-provision"
name = "s3-co-provision"
type = "s3"
driver_account = "aws"
driver_inputs = {
values_string = jsonencode({
"variables" = {
"REGION" = "eu-central-1"
}
"credentials_config" = {
"variables" = {
"ACCESS_KEY_ID" = "AccessKeyId"
"ACCESS_KEY_VALUE" = "SecretAccessKey"
}
}
"script" = <<END_OF_TEXT
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "aws" {
region = var.REGION
access_key = var.ACCESS_KEY_ID
secret_key = var.ACCESS_KEY_VALUE
}
# ... Terraform code reduced for brevity
resource "aws_s3_bucket" "bucket" {
bucket = my-bucket
}
output "bucket" {
value = aws_s3_bucket.main.id
}
output "arn" {
value = aws_s3_bucket.main.arn
}
output "region" {
value = aws_s3_bucket.main.region
}
END_OF_TEXT
})
}
provision = {
"aws-policy" = {
is_dependent = false
}
}
}
s3-co-provision.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: s3-co-provision
entity:
name: s3-co-provision
type: s3
driver_type: humanitec/terraform
# Use the credentials injected via the driver_account to set variables as expected by your Terraform code
driver_account: aws
driver_inputs:
values:
variables:
REGION: eu-central-1
credentials_config:
variables:
ACCESS_KEY_ID: AccessKeyId
ACCESS_KEY_VALUE: SecretAccessKey
script: |
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "aws" {
region = var.REGION
access_key = var.ACCESS_KEY_ID
secret_key = var.ACCESS_KEY_VALUE
}
# ... Terraform code reduced for brevity
resource "aws_s3_bucket" "bucket" {
bucket = my-bucket
}
output "bucket" {
value = aws_s3_bucket.main.id
}
output "arn" {
value = aws_s3_bucket.main.arn
}
output "region" {
value = aws_s3_bucket.main.region
}
# Co-provision aws-policy resource
provision:
aws-policy:
is_dependent: false
Credentials
Credentials
General credentials configuration
Different Terraform providers have different ways of being configured. Generally, there are 3 ways that providers can be configured:
- Directly using parameters on the provider. We call this “provider” credentials.
- Using a credentials file. The filename is supplied to the provider. We call this “file” credentials.
- Via environment variables that the provider reads. We call this “environment” credentials.
A powerful approach for working with different cloud accounts for the same resource definition is to reference the credentials from a config
resource. By using matching criteria on the config
resource, it is possible to specialize the account used in the terraform to different contexts. For example, there might be different AWS Accounts for test
and production
environments. The same resource definition can be used to manage the terraform and 2 config
resources can be created matching to the staging
and production
environments respectively.
In this set of examples, we provide two config
Resource Definitions for AWS and GCP.
AWS
Account config (
account-config-aws.yaml)
Provider Credentials (
aws-provider-credentials.yaml)
Environment Credentials (
aws-environment-credentials.yaml)
GCP
Account config (
account-config-gcp.yaml)
File Credentials (
gcp-file-credentials.yaml)
Temporary credentials
Using a Cloud Account type that supports temporary credentials, those credentials can be easily injected into a Resource Definition using the Terraform Driver. Use a driver_account
referencing the Cloud Account in the Resource Definition, and access its the credentials through the supplied values as shown in the examples.
AWS
S3 bucket (
s3-temporary-credentials.yaml)
GCP
Cloud Storage bucket (
gcs-temporary-credentials.yaml)
Azure
Blob Storage container (
azure-blob-storage-temporary-credentials.yaml)
account-config-aws.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "account-config-aws" {
driver_type = "humanitec/echo"
id = "account-config-aws"
name = "account-config-aws"
type = "config"
driver_account = "aws-credentials"
driver_inputs = {
values_string = jsonencode({
"region" = "us-east-1"
})
}
}
resource "humanitec_resource_definition_criteria" "account-config-aws_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.account-config-aws.id
res_id = "aws-account"
}
account-config-aws.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: account-config-aws
entity:
criteria:
# This res_id is used in the resource reference in the s3-backend Resource Definition.
- res_id: aws-account
# The driver_account references a Cloud Account configured in the Platform Orchestrator.
# Replace with the name your AWS Cloud Account.
driver_account: aws-credentials
driver_inputs:
values:
region: us-east-1
driver_type: humanitec/echo
name: account-config-aws
type: config
account-config-gcp.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "account-config-gcp" {
driver_type = "humanitec/echo"
id = "account-config-gcp"
name = "account-config-gcp"
type = "config"
driver_account = "gcp-credentials"
driver_inputs = {
values_string = jsonencode({
"location" = "US"
"project_id" = "my-gcp-prject"
})
}
}
resource "humanitec_resource_definition_criteria" "account-config-gcp_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.account-config-gcp.id
res_id = "gcp-account"
}
account-config-gcp.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: account-config-gcp
entity:
criteria:
# This res_id is used in the resource reference in the gcp-file-credentials Resource Definition.
- res_id: gcp-account
# The driver_account references a Cloud Account configured in the Platform Orchestrator.
# Replace with the name your GCP Cloud Account.
driver_account: gcp-credentials
driver_inputs:
values:
location: US
project_id: my-gcp-prject
driver_type: humanitec/echo
name: account-config-gcp
type: config
aws-environment-credentials.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "aws-environment-credentials" {
driver_type = "humanitec/terraform"
id = "aws-environment-credentials"
name = "aws-environment-credentials"
type = "s3"
driver_account = "$${resources['config.default#aws-account'].account}"
driver_inputs = {
values_string = jsonencode({
"credentials_config" = {
"environment" = {
"AWS_ACCESS_KEY_ID" = "AccessKeyId"
"AWS_SECRET_ACCESS_KEY" = "SecretAccessKey"
"AWS_SESSION_TOKEN" = "SessionToken"
}
}
"script" = <<END_OF_TEXT
variable "region" {}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
region = var.region
}
output "bucket" {
value = aws_s3_bucket.bucket.bucket
}
output "region" {
value = var.region
}
resource "aws_s3_bucket" "bucket" {
bucket = "$\{replace("$${context.res.id}", "/^.*\\./", "")}-standard-$${context.env.id}-$${context.app.id}-$${context.org.id}"
tags = {
Humanitec = true
}
}
END_OF_TEXT
"variables" = {
"region" = "$${resources['config.default#aws-account'].outputs.region}"
}
})
}
}
aws-environment-credentials.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: aws-environment-credentials
entity:
# Use the account provided by the config resource
driver_account: ${resources['config.default#aws-account'].account}
driver_inputs:
values:
credentials_config:
environment:
AWS_ACCESS_KEY_ID: "AccessKeyId"
AWS_SECRET_ACCESS_KEY: "SecretAccessKey"
AWS_SESSION_TOKEN: "SessionToken"
script: |
variable "region" {}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
region = var.region
}
output "bucket" {
value = aws_s3_bucket.bucket.bucket
}
output "region" {
value = var.region
}
resource "aws_s3_bucket" "bucket" {
bucket = "$\{replace("${context.res.id}", "/^.*\\./", "")}-standard-${context.env.id}-${context.app.id}-${context.org.id}"
tags = {
Humanitec = true
}
}
variables:
region: ${resources['config.default#aws-account'].outputs.region}
driver_type: humanitec/terraform
name: aws-environment-credentials
type: s3
# Supply matching criteria
criteria: []
aws-provider-credentials.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "aws-provider-credentials" {
driver_type = "humanitec/terraform"
id = "aws-provider-credentials"
name = "aws-provider-credentials"
type = "s3"
driver_account = "$${resources['config.default#aws-account'].account}"
driver_inputs = {
values_string = jsonencode({
"credentials_config" = {
"variables" = {
"access_key_id" = "AccessKeyId"
"secret_access_key" = "SecretAccessKey"
"session_token" = "SessionToken"
}
}
"script" = <<END_OF_TEXT
variable "access_key_id" {
sensitive = true
}
variable "secret_access_key" {
sensitive = true
}
variable "session_token" {
sensitive = true
}
variable "region" {}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
region = var.region
access_key = var.access_key_id
secret_key = var.secret_access_key
token = var.session_token
}
output "bucket" {
value = aws_s3_bucket.bucket.bucket
}
output "region" {
value = var.region
}
resource "aws_s3_bucket" "bucket" {
bucket = "$\{replace("$${context.res.id}", "/^.*\\./", "")}-standard-$${context.env.id}-$${context.app.id}-$${context.org.id}"
tags = {
Humanitec = true
}
}
END_OF_TEXT
"variables" = {
"region" = "$${resources['config.default#aws-account'].outputs.region}"
}
})
}
}
aws-provider-credentials.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: aws-provider-credentials
entity:
# Use the account provided by the config resource
driver_account: ${resources['config.default#aws-account'].account}
driver_inputs:
values:
credentials_config:
variables:
access_key_id: "AccessKeyId"
secret_access_key: "SecretAccessKey"
session_token: "SessionToken"
script: |
variable "access_key_id" {
sensitive = true
}
variable "secret_access_key" {
sensitive = true
}
variable "session_token" {
sensitive = true
}
variable "region" {}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
region = var.region
access_key = var.access_key_id
secret_key = var.secret_access_key
token = var.session_token
}
output "bucket" {
value = aws_s3_bucket.bucket.bucket
}
output "region" {
value = var.region
}
resource "aws_s3_bucket" "bucket" {
bucket = "$\{replace("${context.res.id}", "/^.*\\./", "")}-standard-${context.env.id}-${context.app.id}-${context.org.id}"
tags = {
Humanitec = true
}
}
variables:
region: ${resources['config.default#aws-account'].outputs.region}
driver_type: humanitec/terraform
name: aws-provider-credentials
type: s3
# Supply matching criteria
criteria: []
azure-blob-storage-temporary-credentials.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "blob-storage-temporary-credentials" {
driver_type = "humanitec/terraform"
id = "blob-storage-temporary-credentials"
name = "blob-storage-temporary-credentials"
type = "azure-blob"
driver_account = "azure-temporary-creds"
driver_inputs = {
values_string = jsonencode({
"variables" = {
"location" = "eastus"
"resource_group_name" = "my-test-resources"
"tenant_id" = "3987ae5f-008f-4265-a6ee-e9dcedce4742"
"subscription_id" = "742f6d8b-1b7b-4c6a-9f37-90bdd5aeb996"
"client_id" = "c977c44d-3003-464c-b163-03920d4a390b"
}
"credentials_config" = {
"variables" = {
"oidc_token" = "oidc_token"
}
}
"script" = <<END_OF_TEXT
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "azurerm" {
features {}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
client_id = var.client_id
use_oidc = true
oidc_token = var.oidc_token
}
# ... Terraform code reduced for brevity
resource "azurerm_storage_account" "example" {
name = "mystorageaccount"
resource_group_name = var.resource_group_name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "example" {
name = "mystorage"
storage_account_name = azurerm_storage_account.example.name
container_access_type = "private"
}
END_OF_TEXT
})
}
}
azure-blob-storage-temporary-credentials.yaml
(view on GitHub)
:
# Create Azure Blob Storage container using temporary credentials defined via a Cloud Account
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: blob-storage-temporary-credentials
entity:
name: blob-storage-temporary-credentials
type: azure-blob
driver_type: humanitec/terraform
# The driver_account references a Cloud Account of type "azure-identity"
# which needs to be configured for your Organization.
driver_account: azure-temporary-creds
driver_inputs:
values:
variables:
location: eastus
resource_group_name: my-test-resources
tenant_id: 3987ae5f-008f-4265-a6ee-e9dcedce4742
subscription_id: 742f6d8b-1b7b-4c6a-9f37-90bdd5aeb996
# Managed Identity Client ID used in the Cloud Account
client_id: c977c44d-3003-464c-b163-03920d4a390b
# Use the credentials injected via the driver_account
# to set `oidc_token` variable as expected by your Terraform code
credentials_config:
variables:
oidc_token: oidc_token
script: |
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "azurerm" {
features {}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
client_id = var.client_id
use_oidc = true
oidc_token = var.oidc_token
}
# ... Terraform code reduced for brevity
resource "azurerm_storage_account" "example" {
name = "mystorageaccount"
resource_group_name = var.resource_group_name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "example" {
name = "mystorage"
storage_account_name = azurerm_storage_account.example.name
container_access_type = "private"
}
gcp-file-credentials.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "gcp-file-credentials" {
driver_type = "humanitec/terraform"
id = "gcp-file-credentials"
name = "gcp-file-credentials"
type = "gcs"
driver_account = "$${resources['config.default#gcp-account'].account}"
driver_inputs = {
values_string = jsonencode({
"credentials_config" = {
"file" = "credentials.json"
}
"script" = <<END_OF_TEXT
variable "project_id" {}
variable "location" {}
terraform {
required_providers {
google = {
source = "hashicorp/google"
}
}
}
provider "google" {
project = var.project_id
# The file is defined above. The provider will read a service account token from this file.
credentials = "credentials.json"
}
output "name" {
value = google_storage_bucket.bucket.name
}
resource "google_storage_bucket" "bucket" {
name = "$\{replace("$${context.res.id}", "/^.*\\./", "")}-standard-$${context.env.id}-$${context.app.id}-$${context.org.id}"
location = var.location
force_destroy = true
}
END_OF_TEXT
"variables" = {
"location" = "$${resources.config#gcp-account.outputs.location}"
"project_id" = "$${resources.config#gcp-account.outputs.project_id}"
}
})
}
}
gcp-file-credentials.yaml
(view on GitHub)
:
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: gcp-file-credentials
entity:
driver_account: ${resources['config.default#gcp-account'].account}
driver_inputs:
values:
credentials_config:
file: credentials.json
script: |
variable "project_id" {}
variable "location" {}
terraform {
required_providers {
google = {
source = "hashicorp/google"
}
}
}
provider "google" {
project = var.project_id
# The file is defined above. The provider will read a service account token from this file.
credentials = "credentials.json"
}
output "name" {
value = google_storage_bucket.bucket.name
}
resource "google_storage_bucket" "bucket" {
name = "$\{replace("${context.res.id}", "/^.*\\./", "")}-standard-${context.env.id}-${context.app.id}-${context.org.id}"
location = var.location
force_destroy = true
}
variables:
location: ${resources.config#gcp-account.outputs.location}
project_id: ${resources.config#gcp-account.outputs.project_id}
driver_type: humanitec/terraform
name: gcp-file-credentials
type: gcs
# Supply matching criteria
criteria: []
gcs-temporary-credentials.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "gcs-temporary-credentials" {
driver_type = "humanitec/terraform"
id = "gcs-temporary-credentials"
name = "gcs-temporary-credentials"
type = "gcs"
driver_account = "gcp-temporary-creds"
driver_inputs = {
values_string = jsonencode({
"variables" = {
"location" = "europe-west3"
"project_id" = "my-gcp-project"
}
"credentials_config" = {
"variables" = {
"access_token" = "access_token"
}
}
"script" = <<END_OF_TEXT
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "google" {
project = var.project_id
access_token = var.access_token
}
# ... Terraform code reduced for brevity
resource "google_storage_bucket" "bucket" {
name = my-bucket
location = var.location
}
END_OF_TEXT
})
}
}
gcs-temporary-credentials.yaml
(view on GitHub)
:
# Create Google Cloud Storage bucket using temporary credentials defined via a Cloud Account
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: gcs-temporary-credentials
entity:
name: gcs-temporary-credentials
type: gcs
driver_type: humanitec/terraform
# The driver_account references a Cloud Account of type "gcp-identity"
# which needs to be configured for your Organization.
driver_account: gcp-temporary-creds
driver_inputs:
values:
variables:
location: europe-west3
project_id: my-gcp-project
# Use the credentials injected via the driver_account
# to set variables as expected by your Terraform code
credentials_config:
variables:
access_token: access_token
script: |
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "google" {
project = var.project_id
access_token = var.access_token
}
# ... Terraform code reduced for brevity
resource "google_storage_bucket" "bucket" {
name = my-bucket
location = var.location
}
s3-temporary-credentials.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "s3-temporary-credentials" {
driver_type = "humanitec/terraform"
id = "s3-temporary-credentials"
name = "s3-temporary-credentials"
type = "s3"
driver_account = "aws-temp-creds"
driver_inputs = {
values_string = jsonencode({
"variables" = {
"REGION" = "eu-central-1"
}
"credentials_config" = {
"variables" = {
"ACCESS_KEY_ID" = "AccessKeyId"
"ACCESS_KEY_VALUE" = "SecretAccessKey"
"SESSION_TOKEN" = "SessionToken"
}
}
"script" = <<END_OF_TEXT
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "aws" {
region = var.REGION
access_key = var.ACCESS_KEY_ID
secret_key = var.ACCESS_KEY_VALUE
token = var.SESSION_TOKEN
}
# ... Terraform code reduced for brevity
resource "aws_s3_bucket" "bucket" {
bucket = my-bucket
}
END_OF_TEXT
})
}
}
s3-temporary-credentials.yaml
(view on GitHub)
:
# Create S3 bucket using temporary credentials defined via a Cloud Account
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: s3-temporary-credentials
entity:
name: s3-temporary-credentials
type: s3
driver_type: humanitec/terraform
# The driver_account references a Cloud Account of type "aws-role"
# which needs to be configured for your Organization.
driver_account: aws-temp-creds
driver_inputs:
values:
variables:
REGION: eu-central-1
# Use the credentials injected via the driver_account
# to set variables as expected by your Terraform code
credentials_config:
variables:
ACCESS_KEY_ID: AccessKeyId
ACCESS_KEY_VALUE: SecretAccessKey
SESSION_TOKEN: SessionToken
script: |
# This provider block is using the Terraform variables
# set through the credentials_config.
# Variable declarations omitted for brevity.
provider "aws" {
region = var.REGION
access_key = var.ACCESS_KEY_ID
secret_key = var.ACCESS_KEY_VALUE
token = var.SESSION_TOKEN
}
# ... Terraform code reduced for brevity
resource "aws_s3_bucket" "bucket" {
bucket = my-bucket
}
Custom git config
Custom git-config for sourcing Terraform modules
This section contains an example of providing a custom git-config to be used by Terraform when accessing modules sources from private git repositories.
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.
In this example we add a git-config
that re-writes URLs.
example-def.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "example-git-config" {
driver_type = "humanitec/terraform"
id = "example-git-config"
name = "example-git-config"
type = "s3"
driver_inputs = {
values_string = jsonencode({
"files" = {
".gitconfig" = <<END_OF_TEXT
[url "https://github.com/Invicton-Labs/"]
insteadOf = https://example.com/replace-with-git-config/
END_OF_TEXT
}
"script" = <<END_OF_TEXT
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
}
END_OF_TEXT
})
}
}
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
Private git repo
The Terraform Driver can access Terraform definitions stored in a Git repository. In the case that this repository requires authentication, you must supply credentials to the Driver. The examples in this section show how to provide those as part of the secrets in the Resource Definition based on the Terraform Driver.
ssh-secret-refs.tf
: uses secret references to obtain an SSH key from a secret store to connect to the Git repo providing the Terraform code.https-secret-refs.tf
: uses secret references to obtain an HTTPS password from a secret store to connect to the Git repo providing the Terraform code.
https-secret-refs.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "example-resource" {
driver_type = "humanitec/terraform"
id = "example-resource"
name = "example-resource"
type = "some-resource-type"
driver_inputs = {
# This examples uses secret references, pointing at a secret store
# to obtain the actual values
secret_refs = jsonencode({
source = {
# Using the password for a connection to the Git repo via HTTPS
password = {
ref = var.password_secret_reference_key
store = var.secret_store
}
}
variables = {
# ...
}
})
values_string = jsonencode({
# Connection information to the target Git repo
source = {
path = "some-resource-type/terraform"
rev = "refs/heads/main"
url = "https://my-domain.com/my-org/my-repo.git"
}
# ...
})
}
}
ssh-secret-refs.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "example-resource" {
driver_type = "humanitec/terraform"
id = "example-resource"
name = "example-resource"
type = "some-resource-type"
driver_inputs = {
# This examples uses secret references, pointing at a secret store
# to obtain the actual values
secret_refs = jsonencode({
source = {
# Using the ssh_key for a connection to the Git repo via SSH
ssh_key = {
ref = var.ssh_key_secret_reference_key
store = var.secret_store
}
}
variables = {
# ...
}
})
values_string = jsonencode({
# Connection information to the target Git repo
source = {
path = "some-resource-type/terraform"
rev = "refs/heads/main"
url = "[email protected]:my-org/my-repo.git"
}
# ...
})
}
}
Runner
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. In this case, you must supply access data to the cluster to the Humanitec Platform Orchestrator.
The examples in this section show how to provide those data by referencing a k8s-cluster
Resource Definition as part of the non-secret and secret fields of the runner
object in the s3
Resource Definition based on the Terraform Driver.
k8s-cluster-refs.tf
: provides a connection to an EKS cluster.s3-ext-runner-refs.tf
: uses runner configuration to run the Terraform Runner in the external cluster specified byk8s-cluster-refs.tf
and provision an S3 bucket. It configures the Runner to run Terraform scripts from a private Git repository which initializes a Terraform s3 backend via Environment Variables.
k8s-cluster-refs.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "eks_resource_cluster" {
id = "eks-cluster"
name = "eks-cluster"
type = "k8s-cluster"
driver_type = "humanitec/k8s-cluster-eks"
driver_inputs = {
secrets_string = jsonencode({
credentials = {
aws_access_key_id = var.aws_access_key_id
aws_secret_access_key = var.aws_secret_access_key
}
}
)
values_string = jsonencode({
loadbalancer = "10.10.10.10"
name = "my-cluster"
region = "eu-central-1"
loadbalancer = "x111111xxx111111111x1xx1x111111x-x111x1x11xx111x1.elb.eu-central-1.amazonaws.com"
loadbalancer_hosted_zone = "ABC0DEF5WYYZ00"
})
}
}
resource "humanitec_resource_definition_criteria" "eks_resource_cluster" {
resource_definition_id = humanitec_resource_definition.eks_resource_cluster.id
class = "runner"
}
s3-ext-runner-refs.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "aws_terraform_external_runner_resource_s3_bucket" {
id = "aws-terrafom-ext-runner-s3-bucket"
name = "aws-terrafom-ext-runner-s3-bucket"
type = "s3"
driver_type = "humanitec/terraform"
# The driver_account references a Cloud Account configured in the Platform Orchestrator.
# Replace with the name of your AWS Cloud Account.
# The account is used to provide credentials to the Terraform script via environment variables to access the TF state.
driver_account = "my-aws-account"
driver_inputs = {
secrets_string = jsonencode({
# Secret info of the cluster where the Terraform Runner should run.
# This references a k8s-cluster resource that will be matched by class `runner`.
runner = {
credentials = "$${resources['k8s-cluster.runner'].outputs.credentials}"
}
source = {
ssh_key = var.ssh_key
}
}
)
values_string = jsonencode({
# This instructs the driver that the Runner must run in an external cluster.
runner_mode = "custom-kubernetes"
# Non-secret info of the cluster where the Terraform Runner should run.
# This references a k8s-cluster resource that will be matched by class `runner`.
runner = {
cluster_type = "eks"
cluster = {
region = "$${resources['k8s-cluster.runner'].outputs.region}"
name = "$${resources['k8s-cluster.runner'].outputs.name}"
loadbalancer = "$${resources['k8s-cluster.runner'].outputs.loadbalancer}"
loadbalancer_hosted_zone = "$${resources['k8s-cluster.runner'].outputs.loadbalancer_hosted_zone}"
}
# Service Account created following: https://developer.humanitec.com/integration-and-extensions/drivers/generic-drivers/terraform/#runner-object
service_account = "humanitec-tf-runner-sa"
namespace = "humanitec-tf-runner"
}
# Configure the way we provide account credentials to the Terraform scripts in the referenced repository.
# These credentials are related to the `driver_account` configured above.
credentials_config = {
# Terraform script Variables.
variables = {
ACCESS_KEY_ID = "AccessKeyId"
SECRET_ACCESS_KEY = "SecretAccessKey"
}
# Environment Variables.
environment = {
AWS_ACCESS_KEY_ID = "AccessKeyId"
AWS_SECRET_ACCESS_KEY = "SecretAccessKey"
}
}
# Connection information to the Git repo containing the Terraform code.
# It will provide a backend configuration initialized via Environment Variables.
source = {
path = "s3/terraform/bucket/"
rev = "refs/heads/main"
url = "my-domain.com:my-org/my-repo.git"
}
variables = {
# Provide a separate bucket per Application and Environment
bucket = "my-company-my-app-$${context.app.id}-$${context.env.id}"
region = var.region
}
})
}
}
Runner pod configuration
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. In this case, you must supply access data to the cluster to the Humanitec Platform Orchestrator.
The examples in this section show how to provide those data by referencing a k8s-cluster
Resource Definition as part of the non-secret and secret fields of the runner
object in the azure-blob-account
Resource Definition based on the Terraform Driver.
They also provide an example of how to apply labels to the Runner Pod and make it able to run with an Azure Workload Identity getting rid of the need of explicitly setting Azure credentials in the Resource Definition or using a Driver Account.
k8s-cluster-refs.tf
: provides a connection to an AKS cluster.azure-blob-account.tf
: uses runner configuration to run the Terraform Runner in the external cluster specified byk8s-cluster-refs.tf
and provision an azure blob account. It configures the Runner to run Terraform scripts from a private Git repository where an Terraform azurerm backend. Neither a driver account or secret credentials are used here as the runner pod is configured to run with a workload identity associated to the specify service account viarunner.runner_pod_template
property.
azure-blob-account.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "azure_blob_account" {
driver_type = "humanitec/terraform"
id = "azure-blob-account-basic"
name = "azure-blob-account-basic"
type = "azure-blob-account"
driver_inputs = {
secrets_string = jsonencode({
# Secret info of the cluster where the Terraform Runner should run.
# This references a k8s-cluster resource that will be matched by class `runner`.
runner = jsonencode({
credentials = "$${resources['k8s-cluster.runner'].outputs.credentials}"
})
source = {
ssh_key = var.ssh_key
}
})
values_string = jsonencode({
append_logs_to_error = true
# This instructs the driver that the Runner must be run in an external cluster.
runner_mode = "custom-kubernetes"
# Non-secret info of the cluster where the Terraform Runner should run.
# This references a k8s-cluster resource that will be matched by class `runner`.
runner = {
cluster_type = "aks"
cluster = {
region = "$${resources['k8s-cluster.runner'].outputs.region}"
name = "$${resources['k8s-cluster.runner'].outputs.name}"
loadbalancer = "$${resources['k8s-cluster.runner'].outputs.loadbalancer}"
loadbalancer_hosted_zone = "$${resources['k8s-cluster.runner'].outputs.loadbalancer_hosted_zone}"
}
# Service Account created following: https://developer.humanitec.com/integration-and-extensions/drivers/generic-drivers/terraform/#runner-object
# In this example, the Service Account needs to be annotated to specify the Microsoft Entra application client ID to be used with the pod: https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet#service-account-labels-and-annotations
service_account = "humanitec-tf-runner-sa"
namespace = "humanitec-tf-runner"
# This instructs the driver that the Runner pod must run with a workload identity.
runner_pod_template = <<EOT
metadata:
labels:
azure.workload.identity/use: "true"
EOT
}
# Connection information to the Git repo containing the Terraform code.
# It will provide a backend configuration initialized via Environment Variables.
source = {
path = "modules/azure-blob-account/basic"
rev = var.resource_packs_azure_rev
url = var.resource_packs_azure_url
}
variables = {
res_id = "$${context.res.id}"
app_id = "$${context.app.id}"
env_id = "$${context.env.id}"
subscription_id = var.subscription_id
resource_group_name = var.resource_group_name
name = var.name
prefix = var.prefix
account_tier = var.account_tier
account_replication_type = var.account_replication_type
}
})
}
}
k8s-cluster-refs.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "aks_aad_resource_cluster" {
id = "aad-enabled-cluster"
name = "aad-enabled-cluster"
type = "k8s-cluster"
driver_type = "humanitec/k8s-cluster-aks"
driver_inputs = {
secrets_string = jsonencode({
credentials = {
appId = var.app_id
displayName = var.display_name
password = var.password
tenant = var.tenant
}
}
)
values_string = jsonencode({
name = "my-cluster"
resource_group = "my-azure-resource-group"
subscription_id = "123456-1234-1234-1234-123456789"
server_app_id = "6dae42f8-4368-4678-94ff-3960e28e3630"
})
}
}
resource "humanitec_resource_definition_criteria" "aks_aad_resource_cluster" {
resource_definition_id = humanitec_resource_definition.aks_aad_resource_cluster.id
class = "runner"
}
S3
Use the Terraform Driver to provision Amazon S3 bucket resources.
public-git-repo.tf
: uses a publicly accessible Git repo to find the Terraform code.private-git-repo.tf
: uses a private Git repo requiring authentication to find the Terraform code.
private-git-repo.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "aws_terraform_resource_s3_bucket" {
id = "aws-terrafom-s3-bucket"
name = "aws-terrafom-s3-bucket"
type = "s3"
driver_type = "humanitec/terraform"
driver_inputs = {
secrets_string = jsonencode({
variables = {
access_key = var.access_key
secret_key = var.secret_key
}
source = {
# Provide either an SSH key (for SSH connection) or password (for HTTPS).
ssh_key = var.ssh_key
password = var.password
}
}
)
values_string = jsonencode({
# Connection information to the Git repo containing the Terraform code
source = {
path = "s3/terraform/bucket/"
rev = "refs/heads/main"
url = "https://my-domain.com/my-org/my-repo.git"
# url = "[email protected]:my-org/my-repo.git" # For SSH access instead of HTTPS
}
variables = {
# Provide a separate bucket per Application and Environment
bucket = "my-company-my-app-$${context.app.id}-$${context.env.id}"
region = var.region
assume_role_arn = var.assume_role_arn
}
})
}
}
public-git-repo.tf
(view on GitHub)
:
resource "humanitec_resource_definition" "aws_terraform_resource_s3_bucket" {
id = "aws-terrafom-s3-bucket"
name = "aws-terrafom-s3-bucket"
type = "s3"
driver_type = "humanitec/terraform"
driver_inputs = {
secrets_string = jsonencode({
variables = {
access_key = var.access_key
secret_key = var.secret_key
}
}
)
values_string = jsonencode({
# Connection information to the Git repo containing the Terraform code
# The repo must not require authentication
source = {
path = "s3/terraform/bucket/"
rev = "refs/heads/main"
url = "https://my-domain.com/my-org/my-repo.git"
}
variables = {
# Provide a separate bucket per Application and Environment
bucket = "my-company-my-app-$${context.app.id}-$${context.env.id}"
region = var.region
assume_role_arn = var.assume_role_arn
}
})
}
}