Container Driver
Resource Definitions using the Container Driver
This section contains example Resource Definitions using the Container Driver .
The requirements to make these Resource Definitions work with the Orchestrator are:
- The image supplied in the Container Driver Definitions in
values.job.image
should adhere to the interface between Driver and Runner Image . - The cluster chosen to run the Kubernetes Job should be properly configured .
Inline terraform
The Container Driver executes a container supplied as input as part of a Kubernetes Job execution in a target Kubernetes cluster.
The example in this section shows:
- How to reference a
config
Resource Definition to provide the data needed to create a Kubernetes Job in the desired cluster . - How to reference a
config
Resource Definition to create the job with the proper configuration. - How to make the Kubernetes Job able to pull an image from a private registry .
- How to inject the cloud account credentials into the IaC code running in the container via the credentials_config object.
The example is made up out of these files:
k8s-cluster-runner-config.yaml
: provides a connection to a GKE cluster .agent-runner.yaml
: provides the configuration to access a private cluster via the Humanitec Agent.s3.yaml
: in addition to referencing theconfig
Resource Definition, it defines the Terraform scripts to run to provision an S3 bucket whose name is produced appending a random postfix to the application and the environment name. The supplied scripts provide an AWS S3 bucket as place where to store the resource state.
agent-runner.tf
(
view on GitHub
)
:
resource "humanitec_resource_definition" "agent-runner" {
driver_type = "humanitec/agent"
id = "agent-runner"
name = "agent-runner"
type = "agent"
driver_inputs = {
values_string = jsonencode({
"id" = "my-agent"
})
}
}
resource "humanitec_resource_definition_criteria" "agent-runner_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.agent-runner.id
env_type = "development"
class = "runner"
}
agent-runner.yaml
(
view on GitHub
)
:
# This Resource Definition specifies the Humanitec Agent to use for the Runner.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: agent-runner
entity:
driver_type: humanitec/agent
name: agent-runner
type: agent
driver_inputs:
values:
id: my-agent
criteria:
# Change to match the name of the development type you want this to apply to
- env_type: development
class: runner
k8s-cluster-runner-config.tf
(
view on GitHub
)
:
resource "humanitec_resource_definition" "config-container-driver" {
driver_type = "humanitec/echo"
id = "config-container-driver"
name = "config-container-driver"
type = "config"
driver_inputs = {
values_string = jsonencode({
"job" = {
"image" = "ghcr.io/my-registry/container-driver-runner:1.0.1"
"command" = [
"/opt/container"
]
"shared_directory" = "/home/runneruser/workspace"
"namespace" = "humanitec-runner"
"service_account" = "humanitec-runner-job"
"pod_template" = <<END_OF_TEXT
spec:
imagePullSecrets:
- name: ghcr-private-registry
END_OF_TEXT
}
"cluster" = {
"account" = "my-org/my-aws-cloud-account"
"cluster" = {
"cluster_type" = "eks"
"loadbalancer" = "10.10.10.10"
"name" = "my-demo-cluster"
"region" = "eu-west-3"
}
}
})
secret_refs = jsonencode({
"agent_url" = {
"value" = "$${resources['agent.default#agent'].outputs.url}"
}
})
}
}
resource "humanitec_resource_definition_criteria" "config-container-driver_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.config-container-driver.id
env_type = "development"
class = "runner"
}
k8s-cluster-runner-config.yaml
(
view on GitHub
)
:
# This Resource Definition provides configuration values for the Container Driver.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: config-container-driver
entity:
name: config-container-driver
type: config
driver_type: humanitec/echo
driver_inputs:
values:
job:
# Change to match the image you built to run the IaC of your choice
image: ghcr.io/my-registry/container-driver-runner:1.0.1
# Change to match the command to run your image or remove it if you want to use the image entrypoint
command: ["/opt/container"]
# Change to match the mount point of your shared directory
shared_directory: /home/runneruser/workspace
# Change to the namespace name you created to host the Kubernetes Job created by the Driver.
namespace: humanitec-runner
# Change to the service account name with permissions to create secrets/configmaps in the Kubernetes Job namespace you created.
service_account: humanitec-runner-job
# This assumes a secret with the given name exists in the desired namespace and it contains the credentials to pull the job image from the private registry.
pod_template: |
spec:
imagePullSecrets:
- name: ghcr-private-registry
# Change to match the configuration of your target cluster
cluster:
account: my-org/my-aws-cloud-account
cluster:
cluster_type: eks
loadbalancer: 10.10.10.10
name: my-demo-cluster
region: eu-west-3
# Change to match the desired agent (if any)
secret_refs:
agent_url:
value: ${resources['agent.default#agent'].outputs.url}
criteria:
# Change to match the name of the development type you want this to apply to
- env_type: development
class: runner
s3.tf
(
view on GitHub
)
:
resource "humanitec_resource_definition" "aws-s3" {
driver_type = "humanitec/container"
id = "aws-s3"
name = "aws-s3"
type = "s3"
driver_account = "my-aws-cloud-account"
driver_inputs = {
values_string = jsonencode({
"job" = "$${resources['config.runner'].outputs.job}"
"cluster" = "$${resources['config.runner'].outputs.cluster}"
"credentials_config" = {
"environment" = {
"AWS_ACCESS_KEY_ID" = "AccessKeyId"
"AWS_SECRET_ACCESS_KEY" = "SecretAccessKey"
}
}
"files" = {
"terraform.tfvars.json" = "{\"REGION\": \"eu-west-3\", \"BUCKET\": \"$${context.app.id}-$${context.env.id}\"}\n"
"backend.tf" = <<END_OF_TEXT
terraform {
backend "s3" {
bucket = "my-s3-to-store-tf-state"
key = "$${context.res.guresid}/state/terraform.tfstate"
region = "eu-west-3"
}
}
END_OF_TEXT
"providers.tf" = <<END_OF_TEXT
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.72.0"
}
}
}
END_OF_TEXT
"vars.tf" = <<END_OF_TEXT
variable "REGION" {
type = string
}
variable "BUCKET" {
type = string
}
END_OF_TEXT
"main.tf" = <<END_OF_TEXT
provider "aws" {
region = var.REGION
default_tags {
tags = {
CreatedBy = "Humanitec"
}
}
}
resource "random_string" "bucket_suffix" {
length = 5
special = false
upper = false
}
module "aws_s3" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = format("%s-%s", var.BUCKET, random_string.bucket_suffix.result)
acl = "private"
force_destroy = true
control_object_ownership = true
object_ownership = "BucketOwnerPreferred"
}
output "region" {
value = module.aws_s3.s3_bucket_region
}
output "bucket" {
value = module.aws_s3.s3_bucket_id
}
END_OF_TEXT
}
})
secret_refs = jsonencode({
"cluster" = {
"agent_url" = {
"value" = "$${resources['config.runner'].outputs.agent_url}"
}
}
})
}
}
resource "humanitec_resource_definition_criteria" "aws-s3_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.aws-s3.id
env_type = "development"
}
s3.yaml
(
view on GitHub
)
:
# This Resource Definition specifies an `s3` Resource to be provisioned through inline Terraform code.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: aws-s3
entity:
name: aws-s3
type: s3
driver_type: humanitec/container
driver_account: my-aws-cloud-account
driver_inputs:
values:
job: ${resources['config.runner'].outputs.job}
cluster: ${resources['config.runner'].outputs.cluster}
# Needed to authenticate to aws TF provider in the TF code passed via files inputs
credentials_config:
environment:
AWS_ACCESS_KEY_ID: AccessKeyId
AWS_SECRET_ACCESS_KEY: SecretAccessKey
files:
terraform.tfvars.json: |
{"REGION": "eu-west-3", "BUCKET": "${context.app.id}-${context.env.id}"}
# Change to match the backend of your choice.
backend.tf: |
terraform {
backend "s3" {
bucket = "my-s3-to-store-tf-state"
key = "${context.res.guresid}/state/terraform.tfstate"
region = "eu-west-3"
}
}
providers.tf: |
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.72.0"
}
}
}
vars.tf: |
variable "REGION" {
type = string
}
variable "BUCKET" {
type = string
}
main.tf: |
provider "aws" {
region = var.REGION
default_tags {
tags = {
CreatedBy = "Humanitec"
}
}
}
resource "random_string" "bucket_suffix" {
length = 5
special = false
upper = false
}
module "aws_s3" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = format("%s-%s", var.BUCKET, random_string.bucket_suffix.result)
acl = "private"
force_destroy = true
control_object_ownership = true
object_ownership = "BucketOwnerPreferred"
}
output "region" {
value = module.aws_s3.s3_bucket_region
}
output "bucket" {
value = module.aws_s3.s3_bucket_id
}
secret_refs:
cluster:
agent_url:
value: ${resources['config.runner'].outputs.agent_url}
criteria:
# Change to match the name of the development type you want this to apply to
- env_type: development
Private git repo
The Container Driver executes a container supplied as input as part of a Kubernetes Job execution in a target Kubernetes cluster.
The example in this section shows:
- How to reference a
config
Resource Definition to provide the data needed to create a Kubernetes Job in the desired cluster . - How to reference a
config
Resource Definition to create the job with the proper configuration. - How to make the Kubernetes Job able to pull an image from a private registry .
- How to inject the cloud account credentials into the IaC code running in the container via the credentials_config object.
- How to fetch the IaC scripts from a private Repository, via non-secret and secret fields.
The example is made up out of these files:
k8s-cluster-runner-config.yaml
: provides a connection to a GKE cluster .agent-runner.yaml
: provides the configuration to access a private cluster via the Humanitec Agent.s3.yaml
: in addition to referencing theconfig
Resource Definition, it defines how to fetch the Terraform scripts from a private Github Repository to provision an S3 bucket. This also provides via file an AWS S3 bucket as place where to store the resource state.
agent-runner.tf
(
view on GitHub
)
:
resource "humanitec_resource_definition" "agent-runner" {
driver_type = "humanitec/agent"
id = "agent-runner"
name = "agent-runner"
type = "agent"
driver_inputs = {
values_string = jsonencode({
"id" = "my-agent"
})
}
}
resource "humanitec_resource_definition_criteria" "agent-runner_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.agent-runner.id
env_type = "development"
class = "runner"
}
agent-runner.yaml
(
view on GitHub
)
:
# This Resource Definition specifies the Humanitec Agent to use for the Runner.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: agent-runner
entity:
driver_type: humanitec/agent
name: agent-runner
type: agent
driver_inputs:
values:
id: my-agent
criteria:
# Change to match the name of the development type you want this to apply to
- env_type: development
class: runner
k8s-cluster-runner-config.tf
(
view on GitHub
)
:
resource "humanitec_resource_definition" "config-container-driver" {
driver_type = "humanitec/echo"
id = "config-container-driver"
name = "config-container-driver"
type = "config"
driver_inputs = {
values_string = jsonencode({
"job" = {
"image" = "ghcr.io/my-registry/container-driver-runner:1.0.1"
"command" = [
"/opt/container"
]
"shared_directory" = "/home/runneruser/workspace"
"namespace" = "humanitec-runner"
"service_account" = "humanitec-runner-job"
"pod_template" = <<END_OF_TEXT
spec:
imagePullSecrets:
- name: ghcr-private-registry
END_OF_TEXT
}
"cluster" = {
"account" = "my-org/my-aws-cloud-account"
"cluster" = {
"cluster_type" = "eks"
"loadbalancer" = "10.10.10.10"
"name" = "my-demo-cluster"
"region" = "eu-west-3"
}
}
})
secret_refs = jsonencode({
"agent_url" = {
"value" = "$${resources['agent.default#agent'].outputs.url}"
}
})
}
}
resource "humanitec_resource_definition_criteria" "config-container-driver_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.config-container-driver.id
env_type = "development"
class = "runner"
}
k8s-cluster-runner-config.yaml
(
view on GitHub
)
:
# This Resource Definition provides configuration values for the Container Driver.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: config-container-driver
entity:
name: config-container-driver
type: config
driver_type: humanitec/echo
driver_inputs:
values:
job:
# Change to match the image you built to run the IaC of your choice
image: ghcr.io/my-registry/container-driver-runner:1.0.1
# Change to match the command to run your image or remove it if you want to use the image entrypoint
command: ["/opt/container"]
# Change to match the mount point of your shared directory
shared_directory: /home/runneruser/workspace
# Change to the namespace name you created to host the Kubernetes Job created by the Driver.
namespace: humanitec-runner
# Change to the service account name with permissions to create secrets/configmaps in the Kubernetes Job namespace you created.
service_account: humanitec-runner-job
# This assumes a secret with the given name exists in the desired namespace and it contains the credentials to pull the job image from the private registry.
pod_template: |
spec:
imagePullSecrets:
- name: ghcr-private-registry
# Change to match the configuration of your target cluster
cluster:
account: my-org/my-aws-cloud-account
cluster:
cluster_type: eks
loadbalancer: 10.10.10.10
name: my-demo-cluster
region: eu-west-3
# Change to match the desired agent (if any)
secret_refs:
agent_url:
value: ${resources['agent.default#agent'].outputs.url}
criteria:
# Change to match the name of the development type you want this to apply to
- env_type: development
class: runner
s3.tf
(
view on GitHub
)
:
resource "humanitec_resource_definition" "aws-s3" {
driver_type = "humanitec/container"
id = "aws-s3"
name = "aws-s3"
type = "s3"
driver_account = "my-aws-cloud-account"
driver_inputs = {
values_string = jsonencode({
"job" = "$${resources['config.runner'].outputs.job}"
"cluster" = "$${resources['config.runner'].outputs.cluster}"
"credentials_config" = {
"environment" = {
"AWS_ACCESS_KEY_ID" = "AccessKeyId"
"AWS_SECRET_ACCESS_KEY" = "SecretAccessKey"
}
}
"source" = {
"ref" = "refs/heads/main"
"url" = "[email protected]:my-org/my-repo.git"
}
"files" = {
"terraform.tfvars.json" = "{\"REGION\": \"eu-west-3\", \"BUCKET\": \"$${context.app.id}-$${context.env.id}\"}\n"
"backend.tf" = <<END_OF_TEXT
terraform {
backend "s3" {
bucket = "my-s3-to-store-tf-state"
key = "$${context.res.guresid}/state/terraform.tfstate"
region = "eu-west-3"
}
}
END_OF_TEXT
}
})
secret_refs = jsonencode({
"cluster" = {
"agent_url" = {
"value" = "$${resources['config.runner'].outputs.agent_url}"
}
}
"source" = {
"ssh_key" = {
"store" = "my-secret-store"
"ref" = "my-path-to-git-ssh-key"
}
}
})
}
}
resource "humanitec_resource_definition_criteria" "aws-s3_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.aws-s3.id
env_type = "development"
}
s3.yaml
(
view on GitHub
)
:
# This Resource Definition specifies an `s3` Resource to be provisioned through Terraform code read from a private Git repository accessed via an SSH key.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: aws-s3
entity:
name: aws-s3
type: s3
driver_type: humanitec/container
driver_account: my-aws-cloud-account
driver_inputs:
values:
job: ${resources['config.runner'].outputs.job}
cluster: ${resources['config.runner'].outputs.cluster}
# Needed to authenticate to aws TF provider in the TF code passed via files inputs
credentials_config:
environment:
AWS_ACCESS_KEY_ID: AccessKeyId
AWS_SECRET_ACCESS_KEY: SecretAccessKey
# Change to match your repository
source:
ref: refs/heads/main
url: [email protected]:my-org/my-repo.git
# When using a GitHub personal access token, use the HTTPS URL:
# url: https://github.com/my-org/my-repo.git
files:
terraform.tfvars.json: |
{"REGION": "eu-west-3", "BUCKET": "${context.app.id}-${context.env.id}"}
# Change to match the backend of your choice.
backend.tf: |
terraform {
backend "s3" {
bucket = "my-s3-to-store-tf-state"
key = "${context.res.guresid}/state/terraform.tfstate"
region = "eu-west-3"
}
}
secret_refs:
cluster:
agent_url:
value: ${resources['config.runner'].outputs.agent_url}
# Change to match where your ssh key is stored
source:
ssh_key:
store: my-secret-store
ref: my-path-to-git-ssh-key
# Alternative to ssh_key: password or Personal Access Token
# password:
# store: my-secret-store
# ref: my-path-to-git-password-or-token
criteria:
# Change to match the name of the development type you want this to apply to
- env_type: development
Terraform
The Container Driver executes a container supplied as input as part of a Kubernetes Job execution in a target Kubernetes cluster.
The example in this section shows:
- How to use the official Hashicorp maintained Terraform image with the container driver
- How to inject the cloud account credentials into the IaC code running in the container via the credentials_config object.
The example is made up out of these files:
s3.yaml
: alongside the terraform code, a Shell script calledrun.sh
is also included. This invokes theterraform
command in the image.
This example requires:
- A managed cluster - one of AKS, EKS or GKE
- An agent configured to match res_id
runner
NOTE
Due to the
Hashicorp License change for Terraform
Humanitec is unable to provide examples of using the Container Driver with versions of the hashicorp/terraform
image higher than 1.5.7
which is the last image released under the MPL 2.0.
s3.tf
(
view on GitHub
)
:
resource "humanitec_resource_definition" "aws-s3" {
driver_type = "humanitec/container"
id = "aws-s3"
name = "aws-s3"
type = "s3"
driver_account = "aws-ref-arch"
driver_inputs = {
values_string = jsonencode({
"job" = {
"image" = "hashicorp/terraform:1.5.7"
"command" = [
"/bin/sh",
"/home/runneruser/workspace/run.sh"
]
"shared_directory" = "/home/runneruser/workspace"
"namespace" = "humanitec-runner"
"service_account" = "humanitec-container-runner"
}
"cluster" = {
"account" = "my-org/my-aws-cloud-account"
"cluster" = "$${resources[\"k8s-cluster.default#k8s-cluster\"].values}"
}
"credentials_config" = {
"environment" = {
"AWS_ACCESS_KEY_ID" = "AccessKeyId"
"AWS_SECRET_ACCESS_KEY" = "SecretAccessKey"
"AWS_SESSION_TOKEN" = "SessionToken"
}
}
"files" = {
"run.sh" = <<END_OF_TEXT
#!/bin/sh
# NOTE: This script is written to be POSIX shell compatible.
# run_cmd runs the command provided in its input args.
# If the command fails, STDERR from the command is written to ERROR_FILE and
# also to STDERR and the script exists with exit code 1.
run_cmd ()
{
if ! "$@" 2> "$\{ERROR_FILE}"
then
echo
echo "FAILED: $@"
cat "$\{ERROR_FILE}" 1>&2
exit 1
fi
}
if ! [ -d "$\{SCRIPTS_DIRECTORY}" ]
then
echo "SCRIPTS_DIRECTORY does not exist: \"$\{SCRIPTS_DIRECTORY}"\" > "$\{ERROR_FILE}"
cat "$\{ERROR_FILE}" 1>&2
exit 1
fi
run_cmd cd "$\{SCRIPTS_DIRECTORY}"
if [ "$\{ACTION}" = "create" ]
then
run_cmd terraform init -no-color
run_cmd terraform apply -auto-approve -input=false -no-color
# Terraform can export its outputs with the following schema:
# {
# "output-name": {
# "sensitive": bool: true of output is marked sensitive
# "type": string: the Terraform type of the output
# "value": any: the JSON representation of the output value
# },
# ...
# }
#
# The container driver, expects a simple map of output-name: value
#
# The hashicorp/terraform does not provide any special tooling to
# manipulate JSON. However, terraform accepts inputs and JSON so can use
# an additional run of the terraform CLI to manipulate the JSON.
# Create a new directory do convert the JSON outputs.
mkdir output_parse_container
# Generate a tfvars file in JSON format without any JSON tooling with a
# single variable of "in".
echo '{"in":' > output_parse_container/terraform.tfvars.json
run_cmd terraform output -json >> output_parse_container/terraform.tfvars.json
echo '}' >> output_parse_container/terraform.tfvars.json
# Move to a different directory and therefore a different terraform context
# This means if we run terraform apply, it will be independent from the main
# request above.
run_cmd cd output_parse_container
# Inject the Terraform code that converts the input into 2 separate maps
# depending on whether the value is marked as sensitive or not.
echo 'variable "in" { type = map }
output "values" { value = {for k, v in var.in: k => v.value if !v.sensitive} }
output "secrets" { value = {for k, v in var.in: k => v.value if v.sensitive} }' > parse.tf
echo
echo "Converting outputs using terraform apply"
# This terraform apply is just operating on the terraform.tfvars.json
# created above. It is running in a new terraform context and so will
# not influence the infrastructure deployed above
# Note: no need to run terraform init as no providers are required
run_cmd terraform apply -auto-approve -input=false -no-color > /dev/null
run_cmd terraform output -json values > "$\{OUTPUTS_FILE}"
run_cmd terraform output -json secrets > "$\{SECRET_OUTPUTS_FILE}"
echo "Done."
elif [ "$\{ACTION}" = "destroy" ]
then
run_cmd terraform init -no-color
run_cmd terraform destroy -auto-approve -input=false -no-color
else
echo "unrecognized ACTION: \"$\{ACTION}"\" > "$\{ERROR_FILE}"
cat "$\{ERROR_FILE}" 1>&2
exit 1
fi
END_OF_TEXT
"terraform.tfvars.json" = "{\"REGION\": \"eu-west-3\", \"BUCKET\": \"$${context.app.id}-$${context.env.id}\"}\n"
"backend.tf" = <<END_OF_TEXT
terraform {
backend "s3" {
bucket = "my-s3-to-store-tf-state"
key = "$${context.res.guresid}/state/terraform.tfstate"
region = "eu-central-1"
}
}
END_OF_TEXT
"providers.tf" = <<END_OF_TEXT
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.72.0"
}
}
}
END_OF_TEXT
"vars.tf" = <<END_OF_TEXT
variable "REGION" {
type = string
}
variable "BUCKET" {
type = string
}
END_OF_TEXT
"main.tf" = <<END_OF_TEXT
provider "aws" {
region = var.REGION
default_tags {
tags = {
CreatedBy = "Humanitec"
}
}
}
resource "random_string" "bucket_suffix" {
length = 5
special = false
upper = false
}
module "aws_s3" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = format("%s-%s", var.BUCKET, random_string.bucket_suffix.result)
acl = "private"
force_destroy = true
control_object_ownership = true
object_ownership = "BucketOwnerPreferred"
}
output "region" {
value = module.aws_s3.s3_bucket_region
}
output "bucket" {
value = module.aws_s3.s3_bucket_id
}
END_OF_TEXT
}
})
secret_refs = jsonencode({
"cluster" = {
"agent_url" = {
"value" = "$${resources['agent.default#runner'].outputs.url}"
}
}
})
}
}
resource "humanitec_resource_definition_criteria" "aws-s3_criteria_0" {
resource_definition_id = resource.humanitec_resource_definition.aws-s3.id
env_type = "development"
app_id = "container-test"
}
s3.yaml
(
view on GitHub
)
:
# This Resource Definition specifies an `s3` Resource to be provisioned through inline Terraform code.
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
id: aws-s3
entity:
name: aws-s3
type: s3
driver_type: humanitec/container
driver_account: aws-ref-arch
driver_inputs:
values:
job:
# Due to the Hashicorp BSL License, Humanitec cannot provide
# examples using any of the BSL covered versions of terraform
# i.e versions higher than 1.5.7
image: hashicorp/terraform:1.5.7
command: ["/bin/sh", "/home/runneruser/workspace/run.sh"]
shared_directory: /home/runneruser/workspace
# Change to the namespace name you created to host the Kubernetes Job created by the Driver.
namespace: humanitec-runner
# Change to the service account name with permissions to create secrets/configmaps in the Kubernetes Job namespace you created.
service_account: humanitec-container-runner
cluster:
# Update to your cloud account
account: my-org/my-aws-cloud-account
cluster: ${resources["k8s-cluster.default#k8s-cluster"].values}
# Needed to authenticate to aws TF provider in the TF code passed via files inputs
credentials_config:
environment:
AWS_ACCESS_KEY_ID: AccessKeyId
AWS_SECRET_ACCESS_KEY: SecretAccessKey
AWS_SESSION_TOKEN: SessionToken
files:
run.sh: |
#!/bin/sh
# NOTE: This script is written to be POSIX shell compatible.
# run_cmd runs the command provided in its input args.
# If the command fails, STDERR from the command is written to ERROR_FILE and
# also to STDERR and the script exists with exit code 1.
run_cmd ()
{
if ! "$@" 2> "$\{ERROR_FILE}"
then
echo
echo "FAILED: $@"
cat "$\{ERROR_FILE}" 1>&2
exit 1
fi
}
if ! [ -d "$\{SCRIPTS_DIRECTORY}" ]
then
echo "SCRIPTS_DIRECTORY does not exist: \"$\{SCRIPTS_DIRECTORY}"\" > "$\{ERROR_FILE}"
cat "$\{ERROR_FILE}" 1>&2
exit 1
fi
run_cmd cd "$\{SCRIPTS_DIRECTORY}"
if [ "$\{ACTION}" = "create" ]
then
run_cmd terraform init -no-color
run_cmd terraform apply -auto-approve -input=false -no-color
# Terraform can export its outputs with the following schema:
# {
# "output-name": {
# "sensitive": bool: true of output is marked sensitive
# "type": string: the Terraform type of the output
# "value": any: the JSON representation of the output value
# },
# ...
# }
#
# The container driver, expects a simple map of output-name: value
#
# The hashicorp/terraform does not provide any special tooling to
# manipulate JSON. However, terraform accepts inputs and JSON so can use
# an additional run of the terraform CLI to manipulate the JSON.
# Create a new directory do convert the JSON outputs.
mkdir output_parse_container
# Generate a tfvars file in JSON format without any JSON tooling with a
# single variable of "in".
echo '{"in":' > output_parse_container/terraform.tfvars.json
run_cmd terraform output -json >> output_parse_container/terraform.tfvars.json
echo '}' >> output_parse_container/terraform.tfvars.json
# Move to a different directory and therefore a different terraform context
# This means if we run terraform apply, it will be independent from the main
# request above.
run_cmd cd output_parse_container
# Inject the Terraform code that converts the input into 2 separate maps
# depending on whether the value is marked as sensitive or not.
echo 'variable "in" { type = map }
output "values" { value = {for k, v in var.in: k => v.value if !v.sensitive} }
output "secrets" { value = {for k, v in var.in: k => v.value if v.sensitive} }' > parse.tf
echo
echo "Converting outputs using terraform apply"
# This terraform apply is just operating on the terraform.tfvars.json
# created above. It is running in a new terraform context and so will
# not influence the infrastructure deployed above
# Note: no need to run terraform init as no providers are required
run_cmd terraform apply -auto-approve -input=false -no-color > /dev/null
run_cmd terraform output -json values > "$\{OUTPUTS_FILE}"
run_cmd terraform output -json secrets > "$\{SECRET_OUTPUTS_FILE}"
echo "Done."
elif [ "$\{ACTION}" = "destroy" ]
then
run_cmd terraform init -no-color
run_cmd terraform destroy -auto-approve -input=false -no-color
else
echo "unrecognized ACTION: \"$\{ACTION}"\" > "$\{ERROR_FILE}"
cat "$\{ERROR_FILE}" 1>&2
exit 1
fi
terraform.tfvars.json: |
{"REGION": "eu-west-3", "BUCKET": "${context.app.id}-${context.env.id}"}
# Change to match the backend of your choice.
backend.tf: |
terraform {
backend "s3" {
bucket = "my-s3-to-store-tf-state"
key = "${context.res.guresid}/state/terraform.tfstate"
region = "eu-central-1"
}
}
providers.tf: |
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.72.0"
}
}
}
vars.tf: |
variable "REGION" {
type = string
}
variable "BUCKET" {
type = string
}
main.tf: |
provider "aws" {
region = var.REGION
default_tags {
tags = {
CreatedBy = "Humanitec"
}
}
}
resource "random_string" "bucket_suffix" {
length = 5
special = false
upper = false
}
module "aws_s3" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = format("%s-%s", var.BUCKET, random_string.bucket_suffix.result)
acl = "private"
force_destroy = true
control_object_ownership = true
object_ownership = "BucketOwnerPreferred"
}
output "region" {
value = module.aws_s3.s3_bucket_region
}
output "bucket" {
value = module.aws_s3.s3_bucket_id
}
secret_refs:
cluster:
agent_url:
value: ${resources['agent.default#runner'].outputs.url}
criteria:
# Change to match the name of the development type you want this to apply to
- env_type: development
app_id: container-test