# Run the Teleport Terraform Provider on Terraform Cloud

This guide demonstrates how to use the Terraform provider for Teleport using HCP Terraform or Terraform Enterprise.

This guide does not cover running the Terraform provider locally, in other CI/CD environments, or in short-lived cloud VMs. In any of these cases, refer to a dedicated guide:

- [Run the Terraform Provider in CI or cloud VMs](https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/ci-or-cloud.md)
- [Run the Terraform Provider locally](https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/local.md)

## How it works

---

ENTERPRISE

Terraform Cloud joining with self-hosted Terraform Enterprise requires Teleport Enterprise. Terraform Cloud joining with public HCP Terraform (<https://app.terraform.io>) is supported in Teleport Community Edition.

---

When running the Teleport Terraform provider on Terraform Cloud, you can use its built-in Machine & Workload Identity support to dynamically authenticate to your Teleport cluster without any shared secrets. When run in this configuration, the Terraform provider proves its identity to the Teleport Auth Service using Terraform Cloud's [Workload Identity](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/workload-identity-tokens) tokens.

While following this guide, you'll configure your Teleport cluster to accept join requests from Terraform Cloud runs and configure the provider to authenticate using the Terraform Cloud join method.

Note that this guide applies to both the public HCP Terraform a.k.a. Terraform Cloud, as well as self-hosted Terraform Enterprise. This does **not** apply to Terraform or OpenTofu when run on other CI/CD platforms like Spacelift, so refer to our generic [CI and Cloud guide](https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/ci-or-cloud.md) to configure the provider in these environments.

## Prerequisites

- A running Teleport Enterprise cluster. If you want to get started with Teleport, [sign up](https://goteleport.com/signup) for a free trial or [set up a demo environment](https://goteleport.com/docs/get-started/deploy-community.md).

- The `tctl` and `tsh` clients.

  Installing `tctl` and `tsh` clients

  1. Determine the version of your Teleport cluster. The `tctl` and `tsh` clients must be at most one major version behind your Teleport cluster version. Send a GET request to the Proxy Service at `/v1/webapi/find` and use a JSON query tool to obtain your cluster version. Replace teleport.example.com:443 with the web address of your Teleport Proxy Service:

     ```
     $ TELEPORT_DOMAIN=teleport.example.com:443
     $ TELEPORT_VERSION="$(curl -s https://$TELEPORT_DOMAIN/v1/webapi/find | jq -r '.server_version')"
     ```

  2. Follow the instructions for your platform to install `tctl` and `tsh` clients:

     **Mac**

     Download the signed macOS .pkg installer for Teleport, which includes the `tctl` and `tsh` clients:

     ```
     $ curl -O https://cdn.teleport.dev/teleport-${TELEPORT_VERSION?}.pkg
     ```

     In Finder double-click the `pkg` file to begin installation.

     ---

     DANGER

     Using Homebrew to install Teleport is not supported. The Teleport package in Homebrew is not maintained by Teleport and we can't guarantee its reliability or security.

     ---

     **Windows - Powershell**

     ```
     $ curl.exe -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-windows-amd64-bin.zip
     Unzip the archive and move the `tctl` and `tsh` clients to your %PATH%
     NOTE: Do not place the `tctl` and `tsh` clients in the System32 directory, as this can cause issues when using WinSCP.
     Use %SystemRoot% (C:\Windows) or %USERPROFILE% (C:\Users\<username>) instead.
     ```

     **Linux**

     All of the Teleport binaries in Linux installations include the `tctl` and `tsh` clients. For more options (including RPM/DEB packages and downloads for i386/ARM/ARM64) see our [installation page](https://goteleport.com/docs/installation.md).

     ```
     $ curl -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ tar -xzf teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gz
     $ cd teleport
     $ sudo ./install
     Teleport binaries have been copied to /usr/local/bin
     ```

* To check that you can connect to your Teleport cluster, sign in with `tsh login`, then verify that you can run `tctl` commands using your current credentials.

  For example, run the following command, assigning teleport.example.com to the domain name of the Teleport Proxy Service in your cluster and email\@example.com to your Teleport username:

  ```
  $ tsh login --proxy=teleport.example.com --user=email@example.com
  $ tctl status
  Cluster  teleport.example.com
  Version  18.7.3
  CA pin   sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678
  ```

  If you can connect to the cluster and run the `tctl status` command, you can use your current credentials to run subsequent `tctl` commands from your workstation. If you host your own Teleport cluster, you can also run `tctl` commands on the computer that hosts the Teleport Auth Service for full permissions.

* [Terraform >= 1.0.0+](https://learn.hashicorp.com/tutorials/terraform/install-cli)

  ```
  $ terraform version
  Terraform v1.0.0
  ```

  To import existing Teleport resources as Terraform resources, you must have Terraform version `v1.5.0` or above.

* An account and project on either the public Terraform Cloud SaaS or a Terraform Enterprise instance

* The Teleport Terraform provider, v16.4.0 or later

## Step 1/4: Configure Terraform Cloud joining in Teleport

To start, the Teleport Auth Service needs to be configured to accept join requests from Terraform Cloud runs. We'll do this by creating a bot named `terraform` which the Teleport Terraform provider will use in a later step.

```
kind: bot
version: v1
metadata:
  name: terraform
spec:
  # The terraform-provider role is a built-in role granting access to every
  # resource supported by the terraform provider.
  roles: ["terraform-provider"]

```

Create the bot from the new YAML manifest:

```
$ tctl create -f terraform_bot.yaml
bot 'terraform' has been created
```

Next, the new bot needs to be allowed to authenticate with Terraform Cloud Workload Identity credentials. Create a file named `terraform_token.yaml` with this content, depending on whether you are using Terraform Cloud or self-hosted Terraform Enterprise:

**HCP Terraform**

```
kind: token
version: v2
metadata:
  name: terraform
spec:
  roles: [Bot]
  join_method: terraform_cloud
  bot_name: terraform
  terraform_cloud:
    allow:
      - organization_name: ExampleOrganization
        project_name: example-project
        workspace_name: example-workspace

```

**Terraform Enterprise**

---

ENTERPRISE

Teleport Enterprise is required to use Terraform Enterprise joining.

---

Self-hosted Terraform Enterprise installations will need to additionally configure the `hostname` parameter:

```
kind: token
version: v2
metadata:
  name: terraform
spec:
  roles: [Bot]
  join_method: terraform_cloud
  bot_name: terraform
  terraform_cloud:
    hostname: terraform.example.com
    allow:
      - organization_name: ExampleOrganization
        project_name: example-project
        workspace_name: example-workspace

```

This `hostname` value should match the `iss` parameter of the JWTs issued to your Terraform Enterprise runs, without the `https://` prefix. For more information, refer to the [Terraform Enterprise documentation](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/workload-identity-tokens). Note that if `hostname` is not configured, it will default to the Terraform Cloud issuer and join attempts will fail.

This token, named `terraform`, allows Terraform Cloud runs to authenticate with Teleport when all 3 of the values allowed in the token match those of the job run by Terraform Cloud.

Make sure to replace the organization, project, and workspace names to match your Terraform Cloud project or projects. If desired, the fields `organization_id`, `project_id`, and `workspace_id` can be used as well to specify exact resource IDs. The values must exactly match those shown in the Terraform Cloud dashboard.

Note that each `allow` rule must specify at least an `organization_name` or `organization_id`, and at least one other option (workspace and/or project). If desired, all workspaces under a project can be allowed by leaving `workspace_name` (or `workspace_id`) unset. You can specify as many `allow` rules as you want, and at least one must match for a run to be able to join.

Once finished, create the token:

```
$ tctl create -f terraform_token.yaml
token 'terraform' has been created
```

## Step 2/4: Configure Terraform Cloud to issue Workload Identity tokens

Terraform Cloud needs to be configured to issue JWTs during runs. This only requires that an environment variable is set in the Terraform Cloud dashboard. To do so:

1. Navigate to <https://app.terraform.io/>
2. Navigate to your desired organization, project, and workspace
3. From the workspace sidebar, select "Variables"
4. Under "Workspace Variables", click the "Add variable" button
5. Select the "Environment variable" ("env") category
6. For the key, enter: `TFC_WORKLOAD_IDENTITY_AUDIENCE_TELEPORT`
7. For the value, enter your Teleport cluster name. If using Teleport Enterprise (Cloud), this would look like `example.teleport.sh`.
8. If desired, enter a description. For example, "Workload identity token request for Teleport"

The end result should look like this:

![Terraform Cloud Dashboard Example](/docs/assets/images/terraform-cloud-variables-ff1c51a1be54cfd27c767c1f88bb8546.png)

Once this variable is set, all subsequent runs in this workspace will be issued JWTs with the audience configured in the variable value, i.e. `example.teleport.sh` as shown here.

## Step 3/4: Configure the Terraform Provider

In your `provider.tf` or similar, configure the `teleport` provider:

```
provider "teleport" {
  addr = "example.teleport.sh:443"
  join_method = "terraform_cloud"
  join_token = "terraform"
  audience_tag = "teleport"
}

```

These parameters must be set:

- `addr` should match the public hostname and port of your Teleport cluster
- `join_method` should be `terraform_cloud`. Note that Terraform Enterprise also uses the same join method, with a hostname as configured in Step 1.
- `join_token` should match the name of the `token` resource created in Step #1
- `audience_tag` should match the suffix on the key of the variable created in the Terraform Cloud dashboard. For example, given the variable key `TFC_WORKLOAD_IDENTITY_AUDIENCE_TELEPORT`, the audience tag should be `teleport`.

Be sure to remove any preexisting `identity_file_path`; it is replaced by `join_method` and `join_token`.

For a complete example, consider this minimal `provider.tf`:

```
terraform {
  cloud {
    organization = "ExampleOrganization"

    workspaces {
      name = "example-workspace"
    }
  }

  required_providers {
    teleport = {
      source  = "terraform.releases.teleport.dev/gravitational/teleport"
      version = "18.7.3"
    }
  }
}

provider "teleport" {
  addr = "example.teleport.sh:443"
  join_method = "terraform_cloud"
  join_token = "terraform"
  audience_tag = "teleport"
}

resource "teleport_role" "test" {
  version = "v7"
  metadata = {
    name        = "test"
    description = "Dummy role to validate Terraform Provider setup"
    labels = {
      test = "yes"
    }
  }
}

```

## Step 4/4: Run Terraform

You should now be able to perform a Terraform `plan` or `apply`. All types of triggers should work, including CLI, API, and Git, so long as the run is coordinated by Terraform Cloud.

Assuming your local `terraform` is authenticated to Terraform Cloud, try:

```
$ terraform plan
```

The workflow should successfully execute, depending on your Terraform configuration.

## Troubleshooting

### Extracting a JWT for debugging purposes

If you need to view a JWT sample for debugging purposes, you can create a `null_resource` that prints the JWT for the run:

```
resource "null_resource" "print_token" {
  provisioner "local-exec" {
    command = "echo TFC_WORKLOAD_IDENTITY_TOKEN_TELEPORT: $TFC_WORKLOAD_IDENTITY_TOKEN_TELEPORT"
  }
}

```

Once applied, you should see the encoded JWT printed in your Terraform log. These values can be decoded either by hand or with any number of tools, for example [`jwt-cli`](https://github.com/mike-engel/jwt-cli).

Note that these JWTs are generally valid for 2 hours and can potentially be used to authenticate to your Teleport cluster, so this value should be treated with care. The full encoded token should not be shared.

This may be useful for determining the exact issuer (`iss`) value needed to configure Terraform Enterprise join tokens, or if you need to request support.
