Providers

Providers are the reusable direct equivalent of Terraform/OpenTofu providers that may be injected into the Terraform/OpenTofu code referenced in modules.

Most modules you define in the Orchestrator will need to use external resources and data sources from Terraform or OpenTofu providers. Examples of commonly used providers are “hashicorp/kubernetes”, “hashicorp/aws”, “cloudflare/cloudflare”, etc. You must preconfigure the provider in the Orchestrator so that the correct version and configuration can be used.

Once registered, you can hook up one or more providers to a module using the provider_mapping field. Multiple different configurations of the same provider can be used within the same resource graph during a deployment.

You can create and manage providers using the hctl CLI, Terraform provider , OpenTofu provider , or API.

Basic Example

A common example of a provider that you may use frequently is one to deploy Kubernetes manifests into a cluster using the hashicorp/kubernetes  Terraform provider. In the example below, we’re using a specific version of the provider and authenticating to a target Kubernetes cluster using a kubeconfig file mounted into the runner where the provider is being used.

hctl create provider kubernetes dev-k8s-cluster --set-yaml=- <<"EOF"
description: Deploy Kubernetes resources to the common dev cluster
source: hashicorp/kubernetes
version_constraint: ~> 2.38.0
configuration:
    config_path: /mnt/.kube/config
    context: dev-cluster
EOF

The way you mount the kubeconfig file differs depending on the runner type. For a Kubernetes based runner, you can use the pod_template attributes to add a mounted volume.

pod_template:
  spec:
    containers:
    - name: main
      volumeMounts:
      - name: kubeconfig
        mountPath: /mnt/.kube
        readOnly: true
    volumes:
    - name: kubeconfig
      secret:
        secretName: example-runner-kubeconfig

You can find more examples of this in the runner documentation.

Configuration

A provider consists of these elements:

# User-supplied provider type and unique id. The provider_type can be anything
# but it is recommended that you use the provider name
provider_type: kubernetes
id: dev-k8s-cluster

# Optional provider description
description: My provider description

# Provider source as an OpenTofu registry coordinate
source: hashicorp/kubernetes

# The version constraint to pull for the provider
version_constraint: '~> 2.0'

# The configuration of the provider. Available properties depend on the provider type
configuration: {}

Refer to the individual sections below for more details.

Provider source

The source of the provider is interpreted the same as the source address in the required_providers mapping used by Terraform  / OpenTofu .

Source addresses consist of two or three parts: [HOSTNAME/]NAMESPACE/TYPE.

  • The optional hostname of the registry that distributes the provider. If omitted, this defaults to registry.opentofu.org but you can set this to registry.terraform.io if you’re using a provider only available in the Terraform registry

  • The required namespace part is the organizational namespace within the specified registry

  • The required type is the short name of the provider. The type is usually the providers preferred local name and may also be used as the provider_type

Version constraint

Most providers have multiple available versions and will increment as resources are added over time or bugs are fixed. You must specify a version constraint when you register a provider

  • ~> allows only the rightmost version component to increase. For example, ~> 1.2.3 allows for versions greater or equal to 1.2.3 and less than 1.3

  • = allows for only the exact version number

  • != excludes an exact version number, often used when a known version has a bug you want to avoid

  • >, >=, <, <= enforce additional constraints

You can specify multiple expressions separated by commas: >= 1.2.0, < 2.0.0

Provider configuration

You can find the documentation for each provider on its page in the registry. Configuration properties are set in the provider block and are frequently used to specify both authentication and contextual properties of the provider. These properties are set in the configuration map of the Orchestrator provider object. For example, to achieve this provider configuration:

provider "aws" {
  region = "us-east-1"
}

provide this configuration for the Orchestrator provider:

...
configuration:
  region: us-east-1

Multiple providers in the graph

At deploy time, the Orchestrator will combine the module sources, i.e. the Terraform/OpenTofu code of all modules involved, into a single code structure to apply. The same type of provider may therefore appear more than once in that code in different modules. This is a supported and common setup.

Many providers can be configured through environment variables present in the runner. This can be beneficial when you are configuring the provider depending on the runner being used to execute it.

However, these environment variables may also cause unexpected effects on providers in the combined code that share the same variables. or when multiple different configurations of the provider are used in the same graph.

Instead of using the standard environment variables supported by the provider, we therefore recommend that you rely on values coded into the provider definition, mounted file paths, or placeholder expressions as mentioned below.

Setting provider blocks

By default, configuration is set as HCL / OpenTofu language attributes (A = B). To set provider configuration properties that are blocks, you may use a [N] suffix on the property key. For example, you could configure the aws provider with two assume role blocks to achieve chained role assumption:

source: hashicorp/aws
configuration:
  region: us-west-2
  assume_role[0]:
    role_arn: arn:aws:iam::123456789012:role/SomeRole
  assume_role[1]:
    role_arn: arn:aws:iam::123456789012:role/AnotherRole

And this will result in the following HCL at execution time with two separate assume_role blocks:

provider "aws" {
    region = "us-west-2"
    assume_role {
        role_arn = "arn:aws:iam::123456789012:role/SomeRole"
    }
    assume_role {
        role_arn = "arn:aws:iam::123456789012:role/AnotherRole"
    }
}

Placeholders in provider configuration

You can use placeholders in provider configuration to set dynamic values. Providers support context, var, and resource placeholders.

Context placeholders can be effective to specify global but environment dependent behavior in a provider. For example, the hashicorp/aws provider supports a default_tags property which tags all resources provisioned by the provider. Using context placeholders here allows you to reference the environmental source of the provisioned infrastructure.

configuration:
  default_tags[0]:
    tags:
      HumanitecOrgId: ${context.org_id}
      HumanitecProjectId: ${context.project_id}
      HumanitecEnvId: ${context.env_id}

Var placeholders can be used to securely reference environment variables as input to the provider. Here, you would set the TF_VAR_DEFAULT_AWS_ACCESS_KEY environment variable within the runner.

configuration:
  access_key: ${var.DEFAULT_AWS_ACCESS_KEY}

And resource placeholders can be used if the module that uses the provider defines a dependency with the correct key. For example, the cyrilgdn/postgresql provider, is configured to the specific instance where database queries will be executed. Here a resource placeholder can be used if the module using the provider has an instance dependency.

source: "cyrilgdn/postgresql"
version_constraint: "~> 1.0"
configuration:
    host: ${resources.instance.outputs.hostname}
    port: ${resources.instance.outputs.port}
    username: ${resources.instance.outputs.username}
    password: ${resources.instance.outputs.password}

Lifecycle

When you create or update a module, all providers used in the provider_mapping must already exist. For security reasons, you may delete a provider even if specified in a module, however, that module will fail until a provider with the same provider_type and id is created.

Modules that use a provider in the provider_mapping field will not capture a copy of the provider in the version history and will always use the current configuration. For this reason, it’s best to create new copies of the provider if you need existing usage to continue without impact.

Executable dependencies

Some providers or provider configurations may require executables within the runner container. Some examples of these are:

  • A Kubernetes provider using the exec block may expect to use the aws CLI to generate a suitable session token

  • A postgresql provider for connecting to databases may require the psql CLI

  • A provider that uses ansible to interact with remote systems may need ansible and ssh

These executables are not present by default in the runner container image used by the Platform Orchestrator. For security reasons, the default image is minimal and does not contain any executables other than the runner and OpenTofu.

If you need additional executables, it is best to publish and use a customized image. Alternatively, you may install them during execution by running as a root user and using a local exec block to install required packages, although this is not recommended.

Top