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

When a terraform apply is executed, data and metadata is generated that must be stored to be able to keep track of the resources that have been created. For example, the ID of an S3 bucket might be generated. This ID is needed in order to update or destroy the bucket on future invocations. This data is stored in a Terraform State file . The state file should be considered sensitive data as it can contain secrets such as credentials or keys.

Terraform provides various ways storing the state file in different places. These are called Backends .

Humanitec recommends that you configure a backend for all Resource Definitions using the Terraform Driver. That way you maintain control over the state file and any sensitive data it may contain.

Configuring a backend

Terraform block

Backend configuration can be defined in the terraform block . Terraform imposes limitations on the terraform block. For example, Terraform variables cannot be used to parameterize the terraform block. This means that the terraform block must be generated ahead of time.

Backends can be configured via a backend block inside the terraform block. An example block would be:

terraform {
  backend "s3" {
    bucket = "mybucket"
    key    = "path/to/my/key"
    region = "us-east-1"
  }
}

Unique state key

The key used to identify the terraform state needs to be unique to all instances of a resource being created. A resource is uniquely described by its context. That is the application ID, environment ID, Type and Class of the resource and the Resource ID.

Here is an example string of placeholders that will uniquely define a resource.

${context.app.id}_${context.env.id}_${context.res.type}_${context.res.class}_${context.res.id}

A less descriptive, but equally unique key would be the Globally Unique RESource ID (GUResID). This is available for the current resource via this placeholder:

${context.res.guresid}

Credentials

State files often contain sensitive data. Therefore they should be stored on backend that support authorization. This means that the backend should be configured with credentials in Terraform.

There are 2 broad types of credentials that could be used:

  • Temporary credentials
  • Long lived credentials

The Platform Orchestrator supports Temporary Credentials via Cloud Accounts . All backends can be configured via environment variables, so it is recommended to use the credentials_config object to specify credentials via the appropriate environment variable for the backend.

For Long lived credentials, it is recommended to store the credentials in a config resource definition and inject them using a placeholder.

Examples

The following examples of configuring backends are provided:

  • S3 backend using temporary credentials
  • GitLab HTTP backed using long lived credentials

Gitlab

GitLab HTTP Backend using Long Lived credentials

GitLab implements the Terraform HTTP backend . In order to use the Terraform backend in GitLab, the following is needed:

  • A Personal Access Token with api scope
  • A GitLab project that the token has access to.

This example has a simple resource definition using the Terraform Driver. The backend configuration is generated via a config resource and then injected as a file in the terraform resource definition using a placeholder.

The following needs to be defined in the config for this example to work:

  • .entity.driver_inputs.values.gitlab_project_id - Should be the numerical ID of the GitLab project being used to store the state
  • .entity.driver_inputs.secret_refs.username - The username that the Personal Access token is associated with
  • .entity.driver_inputs.secret_refs.password - The value of the Personal Access token

gitlab-backend.tf ( view on GitHub ) :

resource "humanitec_resource_definition" "example-terraform-gitlab-backend-s3" {
  driver_type = "humanitec/terraform"
  id          = "example-terraform-gitlab-backend-s3"
  name        = "example-terraform-gitlab-backend-s3"
  type        = "s3"
  driver_inputs = {
    values_string = jsonencode({
      "files" = {
        "main.tf" = <<END_OF_TEXT
resource "random_id" "thing" {
  byte_length = 8
}

output "bucket" {
  value = random_id.thing.hex
}
END_OF_TEXT
      }
    })
    secrets_string = jsonencode({
      "files" = {
        "backend.tf" = "$${resources['config.tf-runner'].outputs.backend_tf}"
      }
    })
  }
}




gitlab-backend.yaml ( view on GitHub ) :

# This Resource Definition uses GitLab as the Terraform backend to store Terraform state
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: example-terraform-gitlab-backend-s3
entity:
  driver_inputs:
    values:
      files:
        main.tf: |
          resource "random_id" "thing" {
            byte_length = 8
          }

          output "bucket" {
            value = random_id.thing.hex
          }
    secrets:
      files:
        # We don't supply the res_id so that it can be passed through to build the state key
        backend.tf: ${resources['config.tf-runner'].outputs.backend_tf}
  driver_type: humanitec/terraform
  name: example-terraform-gitlab-backend-s3
  type: s3

  # Supply matching criteria
  criteria: []
  

tf-be-config.tf ( view on GitHub ) :

resource "humanitec_resource_definition" "example-terraform-gitlab-backend-config" {
  driver_type = "humanitec/template"
  id          = "example-terraform-gitlab-backend-config"
  name        = "example-terraform-gitlab-backend-config"
  type        = "config"
  driver_inputs = {
    values_string = jsonencode({
      "gitlab_project_id" = ""
      "state_name"        = "$${context.app.id}_$${context.env.id}_$${context.res.id}"
      "templates" = {
        "init"    = "address: https://gitlab.com/api/v4/projects/{{ .driver.values.gitlab_project_id }}/terraform/state/{{ .driver.values.state_name | replace \".\" \"_\" }}\n"
        "outputs" = <<END_OF_TEXT
# Useful for debugging to output the address as an output
address: {{ .init.address }}
END_OF_TEXT
        "secrets" = <<END_OF_TEXT
backend_tf: |
  terraform {
    # https://developer.hashicorp.com/terraform/language/v1.5.x/settings/backends/configuration
    # https://developer.hashicorp.com/terraform/language/v1.5.x/settings/backends/http
    backend "http" {
      address = "{{ .init.address }}"
      lock_address = "{{ .init.address }}/lock"
      lock_method = "POST"
      unlock_address = "{{ .init.address }}/lock"
      unlock_method = "DELETE"
      username = "{{ .driver.secrets.username }}"
      password = "{{ .driver.secrets.password }}"
      retry_wait_min = 5
    }
  }
END_OF_TEXT
      }
    })
    secret_refs = jsonencode({
      "username" = {
        "store" = null
        "ref"   = null
      }
      "password" = {
        "store" = null
        "ref"   = null
      }
    })
  }
}

resource "humanitec_resource_definition_criteria" "example-terraform-gitlab-backend-config_criteria_0" {
  resource_definition_id = resource.humanitec_resource_definition.example-terraform-gitlab-backend-config.id
  class                  = "tf-runner"
}


tf-be-config.yaml ( view on GitHub ) :

# This Resource Definition provides backend configuration for using GitLab to store the Terraform state.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: example-terraform-gitlab-backend-config

entity:
  criteria:
  - class: tf-runner
  driver_type: humanitec/template
  driver_inputs:
    values:
      # Provide the ID of the GitLab Project - it should be a long number as a string
      gitlab_project_id: ""
      state_name: ${context.app.id}_${context.env.id}_${context.res.id}
      templates:
        init: |
          address: https://gitlab.com/api/v4/projects/{{ .driver.values.gitlab_project_id }}/terraform/state/{{ .driver.values.state_name | replace "." "_" }}
        outputs: |
          # Useful for debugging to output the address as an output
          address: {{ .init.address }}
        secrets: |
          backend_tf: |
            terraform {
              # https://developer.hashicorp.com/terraform/language/v1.5.x/settings/backends/configuration
              # https://developer.hashicorp.com/terraform/language/v1.5.x/settings/backends/http
              backend "http" {
                address = "{{ .init.address }}"
                lock_address = "{{ .init.address }}/lock"
                lock_method = "POST"
                unlock_address = "{{ .init.address }}/lock"
                unlock_method = "DELETE"
                username = "{{ .driver.secrets.username }}"
                password = "{{ .driver.secrets.password }}"
                retry_wait_min = 5
              }
            }
    secret_refs:
      # The Username associated with your Personal Access Token
      username:
        store:
        ref:
      # The Personal Access Token
      password:
        store:
        ref:
  type: config
  name: example-terraform-gitlab-backend-config
  

S3

S3 Backend using Temporary Credentials

The Backend is configured using the backend block. A config resource holds the key configuration for the backend.

The Credentials for the backend are automatically read in via the AWS_ environment variables defined in credentials_config.

The following needs to be defined in tf-be-config.yaml resource definition:

  • .entity.driver_account - Should be the ID of the Cloud Account that was configured.
  • .entity.driver_inputs.values.bucket - Should be the ID of the S3 bucket.
  • .entity.driver_inputs.values.prefix - Should be the prefix for state path
  • .entity.driver_inputs.values.region - The region that the bucket is in.

It is critical that the Identity defined in the driver account has access to the S3 bucket.

For example, using this policy document, replacing my-terraform-state-bucket with your bucket ID:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::my-terraform-state-bucket",
                "arn:aws:s3:::my-terraform-state-bucket/*"
            ]
        }
    ]
}

s3-backend.tf ( view on GitHub ) :

resource "humanitec_resource_definition" "example-terraform-s3-backend-s3" {
  driver_type    = "humanitec/terraform"
  id             = "example-terraform-s3-backend-s3"
  name           = "s3-backend-example"
  type           = "s3"
  driver_account = "$${resources.config#tf-backend.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
terraform {
  backend "s3" {
    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}"
  }
}

resource "random_id" "thing" {
  byte_length = 8
}

output "bucket" {
  value = "$\{random_id.thing.hex}"
}
END_OF_TEXT
    })
  }
}




s3-backend.yaml ( view on GitHub ) :

# This Resource Definition uses an S3 bucket as the Terraform backend to store Terraform state
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: example-terraform-s3-backend-s3
entity:
  driver_account: ${resources.config#tf-backend.account}
  driver_inputs:
    values:
      credentials_config:
        environment:
          AWS_ACCESS_KEY_ID: "AccessKeyId"
          AWS_SECRET_ACCESS_KEY: "SecretAccessKey"
          AWS_SESSION_TOKEN: "SessionToken"
      script: |
        terraform {
          backend "s3" {
            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}"
          }
        }

        resource "random_id" "thing" {
          byte_length = 8
        }

        output "bucket" {
          value = "$\{random_id.thing.hex}"
        }

  driver_type: humanitec/terraform
  name: s3-backend-example
  type: s3

  # Supply matching criteria
  criteria: []
  

tf-be-config.tf ( view on GitHub ) :

resource "humanitec_resource_definition" "tf-backend-config" {
  driver_type    = "humanitec/echo"
  id             = "tf-backend-config"
  name           = "tf-backend-config"
  type           = "config"
  driver_account = "aws-ref-arch"
  driver_inputs = {
    values_string = jsonencode({
      "bucket" = "my-terraform-state-bucket"
      "prefix" = "tf-state/"
      "region" = "us-east-1"
    })
  }
}

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"
}


tf-be-config.yaml ( view on GitHub ) :

# This Resource Definition provides configuration for using an S3 bucket to store the Terraform state.
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

  driver_account: aws-ref-arch
  driver_inputs:
    values:
      bucket: my-terraform-state-bucket
      prefix: tf-state/
      region: us-east-1

  driver_type: humanitec/echo
  name: tf-backend-config
  type: config

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 by k8s-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 by k8s-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 via runner.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
      }
    })
  }
}

Top