{"token_count": 3236}

# Run the Teleport Terraform Provider on Spacelift

You can use Spacelift with the Teleport Terraform provider to manage dynamic configuration resources via GitOps and infrastructure as code. This gives you an audit trail of changes to your Teleport configuration and a single source of truth for operators to examine.

This guide shows you how to configure the Teleport Terraform Provider to authenticate to a Teleport cluster using Machine & Workload Identity when running on Spacelift.

## How it works

In this setup, the Teleport Terraform Provider proves its identity to the Teleport Auth Service by presenting an ID token signed by Spacelift. This allows it to authenticate with the Teleport cluster without the need for a long-lived shared secret.

While following this guide, you will create a Teleport user and role with no privileges in order to show you how to use Spacelift to create dynamic resources.

## Prerequisites

- A running Teleport Enterprise cluster accessible at a hostname with a valid TLS certificate. 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:

     **Mac/Linux**

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

     **Windows - Powershell**

     ```
     $ $TELEPORT_DOMAIN = "teleport.example.com:443"
     $ $TELEPORT_VERSION = (Invoke-RestMethod -Uri "https://${TELEPORT_DOMAIN}/v1/webapi/find").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/single-machine.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
     ```

  Connecting with TLS routing disabled

  This guide's commands assume your Teleport cluster uses TLS routing (`proxy_listener_mode: multiplex`), where the `tctl` and `tsh` clients reach every Teleport service through the Proxy Service's web address on port `443`. If you're not sure whether this applies to your cluster, check with whoever manages it.

  If your cluster uses separate listener ports instead, adjust ports as follows:

  - **`tsh` commands** (e.g., `tsh login --proxy=...`): continue using the Proxy Service web address on port `3080` (or `443` if behind a load balancer). Do not change these to port `3025`.

  - **Direct `tctl` or Auth Service API commands**: use port `3025` for the Auth Service gRPC listener:

    ```
    $ tctl status --auth-server=teleport.example.com:3025
    ```

* Check that you can connect to your Teleport cluster and verify that you can run `tctl` and `tsh` commands using your current credentials.

  1. Assign teleport.example.com to the domain name of the Teleport Proxy Service in your cluster and email\@example.com to your Teleport username.

  2. Authenticate to your Teleport cluster. This depends on whether your shell is interactive or not.

     **In an interactive shell:** Run the following command. By default, this triggers a multi-factor authentication prompt:

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

     **On non-interactive environments:** If you are running `tsh` and `tctl` as an AI agent, in a CI/CD environment, or similar, make sure the `TELEPORT_IDENTITY_FILE` environment variable is assigned to a valid file path with credentials for your cluster. `tsh` and `tctl` read the file path from the environment variable and do not require a separate authentication step. If there is no identity file available, we recommend that you [set up Machine ID](https://goteleport.com/docs/machine-workload-identity/getting-started.md) to provision one automatically.

     When executing `tctl` commands with an identity file, you must pass the `--auth-server` flag to provide the Teleport Auth Service address, which is not included in the identity file. If you provide the Proxy Service address, `tctl` connects to the Proxy Service, which forwards traffic to and from the Teleport Auth Service. Update 443 to `3025` if you are contacting the Auth Service directly with `tctl`:

     ```
     $ tctl status --auth-server=teleport.example.com:443
     ```

     For `tsh` commands that read an identity file, you must pass the `--proxy` flag, which points `tsh` to the address of the Teleport Proxy Service:

     ```
     $ tsh status --proxy=teleport.example.com
     ```

     Ensure client commands can access your identity file. Replace path/to/identity/file with the path to your identity file:

     ```
     $ export TELEPORT_IDENTITY_FILE="${TELEPORT_IDENTITY_FILE:-path/to/identity/file}"
     ```

     Add the `--auth-server` or `--proxy` flags to all subsequent `tctl` and `tsh` commands.

  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.

* A GitHub repository where you will store your Terraform configuration and a Spacelift stack linked to this repository.

* A paid Spacelift account. This is required to use the `spacelift` join method.

* Your Teleport user should have the privileges to create token resources.

## Step 1/3. Create a role and Machine & Workload Identity bot

First, we'll create a Machine & Workload Identity Bot for our Spacelift job to act as. We'll grant it the `terraform-provider` role, which automatically grants access to every resource supported by the Teleport terraform provider.

Create `bot.yaml`:

```
kind: bot
version: v1
metadata:
  # name is a unique identifier for the Bot in the cluster.
  name: example
spec:
  # The terraform-provider is a default role shipped in Teleport granting access
  # to every resource supported by the terraform provider.
  roles:
    - terraform-provider

```

Make sure you replace `example` with a unique, descriptive, name for your Bot.

Use `tctl` to apply this file:

```
$ tctl create bot.yaml
```

## Step 2/3. Create a join token for Spacelift

In order to allow your Spacelift stack to authenticate with your Teleport cluster, you'll first need to create a join token. A join token sets out criteria by which the Teleport Auth Service decides whether to allow a bot or node to join a cluster.

In this example, you will create a join token that grants access to any execution within a specific Spacelift stack.

Create a file named `bot-token.yaml`:

```
kind: token
version: v2
metadata:
  name: example-bot
spec:
  # The Bot role indicates that this token grants access to a bot user, rather
  # than allowing a node to join. This role is built in to Teleport.
  roles: [Bot]
  join_method: spacelift
  # The bot_name indicates which bot user this token grants access to. This
  # should match the name of the bot that you created in the previous step.
  bot_name: example
  spacelift:
    # hostname should be the hostname of your Spacelift tenant.
    hostname: example.app.spacelift.io
    # allow specifies rules that control which Spacelift executions will be
    # granted access. Those not matching any allow rule will be denied.
    allow:
    # space_id identifies the space that the module or stack resides within.
    - space_id: root
      # caller_type is the type of caller_id. This must be `stack` or `module`.
      caller_type: stack
      # caller_id is the id of the caller. e.g. the name of the stack or module.
      caller_id: my-stack

```

Replace:

- `example.app.spacelift.io` with the hostname of your Spacelift tenant.
- `my-stack` with the name of the Spacelift stack.
- `root` with the ID of the space that the stack resides within. The "space details" panel on the "Spaces" page of the Spacelift UI shows the ID.

Once the resource file has been written, create the token with `tctl`:

```
$ tctl create -f bot-token.yaml
```

Check that token `example-bot` has been created with the following command:

```
$ tctl tokens ls
Token       Type Labels Expiry Time (UTC)
----------- ---- ------ ----------------------------------------------
example-bot Bot
```

## Step 3/3. Configure your Spacelift stack

### Configure the Terraform Provider

Add the following to a file called `main.tf` to configure the Teleport Terraform provider and declare two dynamic resources, a user and role:

```
terraform {
  required_providers {
    teleport = {
      source  = "terraform.releases.teleport.dev/gravitational/teleport"
      version = ">= 18.10.0"
    }
  }
}

provider "teleport" {
  addr        = "teleport.example.com:443"
  join_method = "spacelift"
  join_token  = "example-bot"
}

resource "teleport_role" "terraform_test" {
  version = "v7"
  metadata = {
    name        = "terraform-test"
    description = "Terraform test role"
    labels = {
      test = "true"
    }
  }
}

resource "teleport_user" "terraform-test" {
  metadata = {
    name        = "terraform-test"
    description = "Terraform test user"

    labels = {
      test = "true"
    }
  }

  spec = {
    roles = [teleport_role.terraform_test.id]
  }
}

```

In the `provider` block, change:

- `teleport.example.com:443` to the host and HTTPS port of your Teleport Proxy Service.
- `example-bot` to the name of the join token you created earlier.

Commit your changes and push the branch to GitHub, then open a pull request against the `main` branch. (Do not merge it just yet.)

### Verify that the setup is working

In the Spacelift UI, navigate to your stack, then to **PRs**. Click the name of the PR you opened.

You should see a Terraform plan that includes the user and role you defined earlier:

![Terraform plan](/docs/assets/images/pr-run-e23f972b28bd17cba2741e81e02b5724.png)

When running `terraform plan`, the Teleport Terraform Provider uses Machine & Workload Identity to generate the short-lived credentials necessary to authenticate to the Teleport cluster.

Merge the PR, then navigate to your stack and click **Runs**. Click the status of the first run, which corresponds to merging your PR, to visit the page for the run. Click **Confirm** to begin applying your Terraform plan.

You should see output indicating success:

![Successful apply](/docs/assets/images/apply-success-2924aa86c80688c8e141acb4502a0702.png)

Verify that Spacelift has created the new user and role by running the following commands, which should return YAML data for each resource:

```
$ tctl get roles/terraform-test
$ tctl get users/terraform-test
```

## Next steps

- Now that you know how to manage Teleport configuration resources with Terraform and Spacelift, read the [Terraform resource reference](https://goteleport.com/docs/reference/infrastructure-as-code/terraform-provider.md) so you can flesh out your configuration.
- To find out more about Spacelift's OIDC implementation, which Machine & Workload Identity uses to authenticate to your Teleport cluster, read [the Spacelift documentation](https://docs.spacelift.io/integrations/cloud-providers/oidc/).
