rfd/0218-access-list-members-iac.md
Ability to manage Access List members with Terraform. This is enterprise-only feature.
Currently the Access List membership model requires periodic membership reviews. Because of that, the IaC approach to Access List membership was not provided so far and we don't have a good way to introduce it for the Access Lists in their current form.
Manual management of Access List membership doesn't always scale. There are ways of proper structuring teams as Access Lists and then using the nested Access List concept to assign teams to resources, but that doesn't work when users are managed externally.
The concept of dynamically assigning users to Access Lists (something like membership criteria) also won't scale when users are managed externally in large organizations. For example Microsoft Entra ID won't display any groups in SAML assertion if the user is assigned to more than 150 groups.
A new concept, a static Access Lists, is introduced to overcome the outlined limitations. The idea is to have Access List with a spec.type set to "static". Creating the Access List with the new static type will disable reviews and therefore make it possible to manage such Access Lists using IaC tools.
There will be a new Terraform resource named teleport_access_list_member.
resource "teleport_access_list" "characters" {
header = {
version = "v1"
metadata = {
name = "characters"
}
}
spec = {
type = "static" # type must be set to "static" to manage members with Terraform
audit = null # audit can be skipped and it's ignored if specified
title = "Characters"
description = "The list of game characters."
owners = [
{ name = "dungeon_master" },
]
grants = {
roles = ["dungeon_access"]
}
}
}
resource "teleport_access_list_member" "fighter" {
header = {
version = "v1"
metadata = {
name = "fighter" # Teleport user name
}
}
spec = {
access_list = teleport_access_list.characters.id
membership_kind = 1 # 1 for "MEMBERSHIP_KIND_USER", 2 for "MEMBERSHIP_KIND_LIST"
# expires is optional. The member will stay in the list after it expires but will lose the
# grants. expires can be updated.
expires = "2025-07-28T22:00:00Z"
}
}
It is also possible to add a nested Access List member. The Access Lists members can be created manually or with an integration (i.e. it also works for Access Lists created with Okta integration and Microsoft Entra ID integration).
For example to add "npcs" Access List member to the characters Access List defined above:
resource "teleport_access_list" "npcs" {
header = {
version = "v1"
metadata = {
name = "npcs"
}
}
spec = {
title = "NPCs"
description = "Non-player characters."
owners = [
{ name = "dungeon_master" }
]
grants = {
roles = ["dungeon_access"]
}
audit = {
recurrence = {
frequency = 3
day_of_month = 15
}
}
}
}
resource "teleport_access_list_member" "npcs" {
header = {
version = "v1"
metadata = {
name = teleport_access_list.npcs.id
}
}
spec = {
access_list = teleport_access_list.characters.id
membership_kind = 2 # 1 for "MEMBERSHIP_KIND_USER", 2 for "MEMBERSHIP_KIND_LIST"
}
}
To add a member which is an Access List created for an Okta group:
resource "teleport_access_list_member" "characters_from_okta" {
header = {
version = "v1"
metadata = {
name = "00gt3c8z9ukePm5uF697" # taken from Access List URL: https://my-company.teleport.sh/web/accesslists/00gt3c8z9ukePm5uF697
}
}
spec = {
access_list = teleport_access_list.characters.id # defined in the example above
membership_kind = 2 # 1 for "MEMBERSHIP_KIND_USER", 2 for "MEMBERSHIP_KIND_LIST"
}
}
There are a few things to note here:
protoc generates
schema code from proto filesThe new resource only allows managing members for the access_list with .spec.type set to "static". If the type is not set to "static":
teleport_access_list_member.fighter: Creating...
╷
│ Error: Error reading Member
│
│ with teleport_access_list_member.fighter,
│ on main.tf line 43, in resource "teleport_access_list_member" "fighter":
│ 43: resource "teleport_access_list_member" "fighter" {
│
│ Access list member's ("fighter") access list ("characters") is not static (i.e., access_list with spec.type set to "static"). Access list "characters" type is "" (default). Teleport IaC
│ tools support adding members only to access lists of type "static".
╵
The access_list type cannot be modified once it's created:
teleport_access_list.characters: Modifying... [id=characters]
╷
│ Error: Error updating AccessList
│
│ with teleport_access_list.characters,
│ on main.tf line 14, in resource "teleport_access_list" "characters":
│ 14: resource "teleport_access_list" "characters" {
│
│ access_list "characters" type "static" cannot be changed to ""
╵
teleport_access_list.characters: Modifying... [id=characters]
╷
│ Error: Error updating AccessList
│
│ with teleport_access_list.characters,
│ on main.tf line 14, in resource "teleport_access_list" "characters":
│ 14: resource "teleport_access_list" "characters" {
│
│ access_list "characters" type "" cannot be changed to "static"
╵
Trying to import non-static access_list member:
teleport_access_list_member.fighter: Importing from ID "characters/fighter"...
╷
│ Error: Error reading Member
│
│ Access list member's ("fighter") access list ("characters") is not static (i.e., access_list with spec.type set to "static"). Access list "characters" type is "" (default). Teleport IaC
│ tools support adding members only to access lists of type "static".
╵
Member's spec.name is not empty and not equal to metadata.name:
teleport_access_list_member.fighter: Creating...
╷
│ Error: Error creating Member
│
│ with teleport_access_list_member.fighter,
│ on main.tf line 43, in resource "teleport_access_list_member" "fighter":
│ 43: resource "teleport_access_list_member" "fighter" {
│
│ The values of member.header.metadata.name ("fighter") and member.spec.name ("wizard") must match, unless member.spec.name is left empty. Tip: You can have multiple members with the same
│ metadata.name as long as each of them has a different spec.access_list (i.e., they belong to different access lists
╵
tctl - can modify members of the static access_list resources with the existing acl users
commands and create -f command.teleport-operator - won't have support to reduce the scope but the possibility is open too.We want Terraform teleport_access_list_member resources to be created only for the static Access Lists. To achieve that on the server side, a new set of gRPCs for static members management will be exposed.
All the new gRPCs have Static in the name and will only allow member management for the access_list resources of "static" type.
The API should be similar to the existing non-Static endpoints. E.g. for
UpsertStaticAccessListMember:
service AccessListService {
...
// UpsertStaticAccessListMember creates or updates an access_list_member resource. It returns
// error and does nothing if the target access_list is not of type static. This API is there for
// the IaC tools to prevent them from making changes to members of dynamic access lists.
rpc UpsertStaticAccessListMember(UpsertStaticAccessListMemberRequest) returns (UpsertStaticAccessListMemberResponse);
...
}
// UpsertStaticAccessListMemberRequest is the request for upserting an access_list_member. It fails
// if the access_list is not static type.
message UpsertStaticAccessListMemberRequest {
// member is the access_list_member to upsert.
Member member = 1;
}
message UpsertStaticAccessListMemberResponse {
// member is the upserted access_list_member.
Member member = 1;
}
There is no restriction on how static Access Lists members can modified on the RBAC level. All the "obstacles" to modify the static Access Lists and their members are the UI tweaks only, and their purpose is the user's guidance on how to properly utilize static Access Lists.
In other words the existing gRPCs (e.g. UpsertAccessListWithMembers) can be still used to modify
static Access Lists and their members but:
*Static* gRPCs)tctl can modify any Access List (with create -f and edit access_list/<name) and its members
(with acl users add/rm) freelyIf a cluster with Access Lists of the new "static" type is created and the downgraded to a version not supporting the new type:
To better illustrate this, let's consider this scenario:
When Terraform is being run again, there will be errors like this:
Plan: 0 to add, 1 to change, 0 to destroy.
╷
│ Error: Error reading Member
│
│ with teleport_access_list_member.fighter,
│ on main.tf line 43, in resource "teleport_access_list_member" "fighter":
│ 43: resource "teleport_access_list_member" "fighter" {
│
│ Access list member's ("fighter") access list ("characters") is not static (i.e., access_list with spec.type set to "static"). Access list "characters" type is "" (default). Teleport IaC
│ tools support adding members only to access lists of type "static".
╵
NOTE: This can be very confusing if the HA cluster runs two versions (one with the static Access *Lists support, and one without) and the feature is being used.
It can mean, that in case of downgrade and upgrade, everything has to be potentially removed (including the resources in the Terraform state) and started over again.
Audit events will be exactly the same as the current Access List membership related events.
Access Lists section of the test plan should be extended with points verifying that: