# 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. 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.
* 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.7.3"
    }
  }
}

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/).
