# Creating Access Lists with IaC

Access Lists allow Teleport users to be granted long-term access to resources managed within Teleport. With Access Lists, administrators can regularly audit and control membership to specific roles and traits, which then tie easily back into Teleport's existing RBAC system.

In this guide, we'll follow up on [the IaC users and roles guide](https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/managing-resources/user-and-role.md) by allowing users with the `manager` role to grant the `support-engineer` role to users meeting specific criteria.

## How it works

Access Lists are registered with the Teleport Auth Service as resources stored on the Auth Service backend. The Teleport Auth Service exposes a gRPC API that enables clients to create, delete, or modify backend resources, including Access Lists. The Teleport Kubernetes Operator and Terraform provider, along with the `tctl` command-line tool, can manage Access Lists by authenticating to the Teleport Auth Service and interacting with its gRPC API.

By default, Access Lists can be managed via IaC but Access List memberships cannot. The goal of Access Lists is to decentralize granting and reviewing access. By allowing managers to grant access within specific guidelines and automatically enforcing review, users can request common access rights without having to go through the centralized team managing the Teleport IaC. This reduces the load on the centralized IaC/security team, ensures the access reviewer is aware of the context, reduces the request resolution time, and ensures access grants are periodically reviewed.

## Prerequisites

To follow this guide, you must follow first [the basic users and roles IaC guide](https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/managing-resources/user-and-role.md). We will reuse its users and roles for our Access List.

## Step 1/3 - Write manifests

### Write the privileged role manifest

We will create a new role `support-engineer` that grants access to production servers. The `engineer` role from the previous guide was only granting access to `dev` and `staging` servers.

**tctl**

Create the following `privileged-role.yaml` file:

```
kind: role
version: v7
metadata:
  name: support-engineer
spec:
  allow:
    logins: ['root', 'ubuntu', '{{internal.logins}}']
    node_labels:
      'env': ['production']

```

**Kubernetes Operator**

Create the following `privileged-role.yaml` file:

```
apiVersion: resources.teleport.dev/v1
kind: TeleportRoleV7
metadata:
  name: support-engineer
spec:
  allow:
    logins: [ 'root', 'ubuntu', '{{internal.logins}}' ]
    node_labels:
      'env': [ 'production' ]

```

**Terraform**

Create the following `privileged-role.tf` file:

```
resource "teleport_role" "support-engineer" {
  version = "v7"
  metadata = {
    name = "support-engineer"
  }

  spec = {
    allow = {
      logins = ["root", "ubuntu", "{{internal.logins}}"]
      node_labels = {
        env = ["production"]
      }
    }
  }
}

```

### Write the Access List manifest

In this step we'll create an Access List that allows users with the `manager` role such as `alice` to grant access to production to users with the `engineer` role.

**tctl**

Create the following `accesslist.yaml` file:

```
version: v1
kind: access_list
metadata:
  name: support-engineers
spec:
  title: "Production access for support engineers"
  audit:
    recurrence:
      frequency: 6months
  description: "Use this Access List to grant access to production to your engineers enrolled in the support rotation."
  owners:
    - description: "manager of NA support team"
      name: alice
  ownership_requires:
    roles:
      - manager
  grants:
    roles:
      - support-engineer
  membership_requires:
    roles:
      - engineer

```

**Kubernetes Operator**

Create the following `accesslist.yaml` file:

```
apiVersion: resources.teleport.dev/v1
kind: TeleportAccessList
metadata:
  name: support-engineers
spec:
  title: "Production access for support engineers"
  description: "Use this Access List to grant access to production to your engineers enrolled in the support rotation."
  audit:
    recurrence:
      frequency: 6months
  owners:
    - description: "manager of NA support team"
      name: alice
  ownership_requires:
    roles:
      - manager
  grants:
    roles:
      - support-engineer
  membership_requires:
    roles:
      - engineer

```

**Terraform**

Create the following `accesslist.tf` file:

```
resource "teleport_access_list" "support-engineers" {
  header =  {
    version = "v1"
    metadata = {
      name = "support-engineers"
    }
  }

  spec = {
    title = "Production access for support engineers"
    description = "Use this Access List to grant access to production to your engineers enrolled in the support rotation."
    audit = {
      recurrence = {
        frequency = 6
      }
    }
    owners = [
      {
        description = "manager of NA support team"
        name = "alice"
      }
    ]
    ownership_requires = {
      roles = ["manager"]
    }
    grants = {
      roles = ["support-engineer"]
    }
    membership_requires = {
      roles = ["engineer"]
    }
  }
}

```

## Step 2/3 - Apply the manifests

**tctl**

```
$ tctl create -f privileged-role.yaml
role 'support-engineer' has been created

$ tctl create -f accesslist.yaml
Access list "support-engineers" has been created
```

---

NOTE

The user resource depends on roles. You must create roles before users as a user with a non-existing role is invalid and will be rejected by Teleport.

---

**Kubernetes Operator**

Create the Kubernetes CRs with the following commands:

```
$ kubectl apply -n "$OPERATOR_NAMESPACE" -f privileged-role.yaml
teleportrolev7.resources.teleport.dev/support-engineer created

$ kubectl apply -n "$OPERATOR_NAMESPACE" -f accesslist.yaml
teleportaccesslist.resources.teleport.dev/support-engineers
```

**Terraform**

```
$ terraform plan
[...]
Plan: 2 to add, 0 to change, 0 to destroy.

$ terraform apply
teleport_access_list.support-engineers: Creating...
teleport_role.support-engineer: Creating...
teleport_role.support-engineer: Creation complete after 0s [id=support-engineer]
teleport_access_list.support-engineers: Creation complete after 0s [id=support-engineers]
```

## Step 3/3 - Log in as `alice` and grant access to `bob`

Now, you created an Access List allowing `alice` to grant the `support-engineer` role to its engineers.

You can log in as alice and add `bob` to the `support-engineers` Access List.

**Web UI**

Log in as `alice` in the Web UI, Zero Trust Access, select Access Lists, and click on your Access List. Click "Add new Members or Access Lists or Access Lists" and add `bob`.

![Screenshot of the Web UI showing the Access List and the \&quot;Enroll Member\&quot; button](/docs/assets/images/access-list-web-ui-8b5d883d94a237302c46fd458b12bfb0.png)

**CLI**

Log in as `alice` with `tsh`, then add bob to the Access List:

```
log in as alice
$ tsh login --proxy <your-cluster-domain>:<port> --user alice

tctl acl users add <access-list-name> <user> [<expires>] [<reason>]
$ tctl acl users add support-engineers bob "" "Bob is now part of the on-call support rotation"
```

Finally, list the Access List members:

```
$ tctl acl users ls support-engineers
Members of support-engineers:
- bob
```

## Managing Access List members with Terraform

You can manage Access Lists with Terraform is using two Terraform resources: `teleport_access_list` and `teleport_access_list_member`. This section explains how you can adjust your Teleport Terraform configuration to manage Access List members, and the implications of managing Access List members using Terraform versus using the Web UI and `tctl`.

### Static versus default Access Lists

The **default Access List**, which you create using `tctl` or the Web UI, has the `.spec.type` field unset, or set to null or an empty string. As a consequence:

- They require auditing. If`.spec.audit` is not specified in the Terraform resource, Teleport assigns a default value. Static Access Lists must be periodically reviewed in the Web UI.
- Their members can be only managed with the Web UI and `tctl`. The source of truth for these lists is Teleport so you cannot manage their members with Terraform.

**Static Access Lists** have the `.spec.type` field set to "static". They differ from the default Access Lists in that:

- They don't support auditing. Teleport ignores the `.spec.audit` field.
- Their members can be managed by Terraform.

For static Access Lists, the source of truth for the membership is external, your Terraform configuration, so auditing is not supported. Instead, you can review their members when modifying your Terraform configuration.

### Designating a static Access List

Edit the Access List you created earlier to set the type to `"static"`, which marks it as a static Access List. While Teleport ignores the `spec.audit` field for static Access Lists, you can remove it for clarity as below:

```
  resource "teleport_access_list" "support-engineers" {
    header =  {
      version = "v1"
      metadata = {
        name = "support-engineers"
      }
    }
  
    spec = {
+     type = "static"
      title = "Production access for support engineers"
      description = "Use this Access List to grant access to production to your engineers enrolled in the support rotation."
-     audit = {
-     recurrence = {
-       frequency = 6
-     }
-   }
    }
  # remaining fields truncated
  }

```

### Assigning users to an Access List

To assign a user as a member of an Access List, declare a `teleport_access_list_member` resource and set its `spec.membership_kind` to `1`, which indicates that the member is a user:

```
resource "teleport_access_list_member" "developers_alice" {
  header = {
    version = "v1"
    metadata = {
      name = "alice" # Teleport user name
    }
  }
  spec = {
    access_list     = teleport_access_list.support-engineers.id
    membership_kind = 1 # 1 for "MEMBERSHIP_KIND_USER", 2 for "MEMBERSHIP_KIND_LIST"
  }
}

resource "teleport_access_list_member" "developers_bob" {
  header = {
    version = "v1"
    metadata = {
      name = "bob"
    }
  }
  spec = {
    access_list     = teleport_access_list.support-engineers.id
    membership_kind = 1 # 1 for "MEMBERSHIP_KIND_USER", 2 for "MEMBERSHIP_KIND_LIST"
  }
}

```

Notice that both members' `spec.access_list` refers to the ID of the `support-engineers` Access List you created earlier.

### Assigning nested Access Lists

Static Access Lists also allow you to assign nested Access Lists. For example, consider the following `teleport_access_list_member` resource:

```
resource "teleport_access_list_member" "db_access_staging_developers" {
  header = {
    version = "v1"
    metadata = {
      name = teleport_access_list.technical-account-managers.id
    }
  }
  spec = {
    access_list     = teleport_access_list.support-engineers.id
    membership_kind = 2 # 1 for "MEMBERSHIP_KIND_USER", 2 for "MEMBERSHIP_KIND_LIST"
  }
}

```

This resource has a `spec.membership_kind` of `2`, indicating that it is a nested Access List. The name of the member is the ID of another Access List (not shown in this guide) called `technical-account-managers`. This instructs the Terraform provider to assign the `technical-account-managers` Access List as a nested Access List within `support-engineers`.

### Give access to an Okta or Microsoft Entra ID group

The Okta integration allows [synchronizing Okta groups and apps](https://goteleport.com/docs/identity-governance/integrations/okta/app-and-group-sync.md) as Teleport Access Lists, while the Microsoft Entra ID integration allows [synchronizing groups](https://goteleport.com/docs/identity-governance/integrations/entra-id.md) as Teleport Access Lists.

To give permissions to an Access List based on these integrations in Terraform, navigate to the Access List in the Web UI, and from its URL (e.g. `https://example.teleport.sh/web/accesslists/00gt3c8z9ukePm5uF697`) copy the last path segment. In this case `00gt3c8z9ukePm5uF697` - this is the name of the Access List resource in Teleport.

For example, the following `teleport_access_list_member` configures an Access List managed via the Okta integration to be a child of the `support-engineers` Access List:

```
resource "teleport_access_list_member" "db_access_staging_okta_group" {
  header = {
    version = "v1"
    metadata = {
      name = "00gt3c8z9ukePm5uF697"
    }
  }
  spec = {
    access_list     = teleport_access_list.support-engineers.id
    membership_kind = 2 # 1 for "MEMBERSHIP_KIND_USER", 2 for "MEMBERSHIP_KIND_LIST"
  }
}

```

For an Access List managed via the Microsoft Entra ID integration, the name of the Access List will be similar to `b1a6a594-a4ac-51d1-a6f6-1746a413a79a`.

### Importing Access Lists created using the Web UI into Terraform

It is usually not possible to import Access Lists created using the Web UI into a Terraform module. Any Access List can be imported, but Access Lists created in the UI are not of the static type, so their members can't be managed with Terraform. The same applies to Access Lists created by an integration (e.g. [Okta](https://goteleport.com/docs/identity-governance/integrations/okta/app-and-group-sync.md) or [Microsoft Entra ID](https://goteleport.com/docs/identity-governance/integrations/entra-id.md)). An existing static list created by another Terraform setup (with a different state) could be imported, and its members can be managed by Terraform, but this situation is unusual.

The recommended solution is to create a static Access List managed with Terraform and make this list a member of an existing Access List. That way the members of the static Access List managed with Terraform will inherit the grants of the existing Access List.

## Next steps

You can see all supported Access List fields [in the Access List reference](https://goteleport.com/docs/reference/access-controls/access-lists.md).
