Terraform module which creates a Temporal Cloud user group, together with its account and namespace permissions and its membership.
This is the module that grants access. Whatever else your account uses, something has to say which namespaces a group can reach and at what level, and that is what lives here.
If your account has SAML and SCIM, this is the module you want — not
user. Those two
features cover authentication and provisioning, and stop short of permissions:
| Concern | Owned by |
|---|---|
| Authentication — signing in | SAML SSO |
| Which people exist, and their group membership | SCIM, from your identity provider |
| What a group is allowed to do | this module |
| Machine access for workers and CI | service-account — never a group |
Temporal Cloud's SCIM documentation is explicit that roles are assigned to a group after it syncs.
So SCIM delivers a group with the right people in it and stops; account_access and
namespace_accesses remain yours to set, and granting them to groups rather than to individuals is
what makes a synced user's access follow from their membership.
Two ways to use it, depending on where the group comes from:
- The group is provisioned by SCIM, or otherwise already exists. Set
create_group = falseand passgroup_idplus an emptyname, enablecreate_group_access, and leavecreate_group_membersoff so the identity provider stays the only writer of membership. See Groups provisioned by SCIM. - You own the group outright. Let the module create it and manage membership here too. Appropriate when there is no SCIM integration — both SAML and SCIM are paid features — or for a group that has no equivalent in the directory.
Because temporalcloud_group_access owns a group's entire access map rather than individual
entries, exactly one configuration may manage a given group's access. Two module calls pointing at the
same group_id will overwrite each other on every apply.
The temporalcloud provider authenticates with an API key, read from the TEMPORAL_CLOUD_API_KEY
environment variable:
export TEMPORAL_CLOUD_API_KEY="<your-api-key>"The provider authenticates when it initialises, so a key is needed even for a terraform plan that
would create nothing. Keep the key out of version control — an untracked .env file rather than a
committed .tfvars.
The group's role on the account is the coarsest control: it applies everywhere and needs no namespace to point at.
module "group" {
source = "terraform-temporalcloud-modules/group/temporalcloud"
version = "~> 2.0"
name = "platform-engineering"
create_group_access = true
account_access = "developer"
}namespace_accesses grants per-namespace permissions. It is a set, and it replaces the group's whole
namespace access map on every apply, so it has to list every namespace the group should reach.
module "group" {
source = "terraform-temporalcloud-modules/group/temporalcloud"
version = "~> 2.0"
name = "support"
create_group_access = true
# `none` gives no account-wide role, so the group reaches exactly the two
# namespaces below and nothing else.
account_access = "none"
namespace_accesses = [
{
namespace_id = module.orders.namespace_id
permission = "read"
},
{
namespace_id = module.payments.namespace_id
permission = "write"
},
]
}Membership is set by user ID, not email address. The temporalcloud_users data source lists the
account's existing users; temporalcloud_user invites new ones and belongs to a different module in
this family.
data "temporalcloud_users" "all" {}
module "group" {
source = "terraform-temporalcloud-modules/group/temporalcloud"
version = "~> 2.0"
name = "platform-engineering"
create_group_members = true
users = [
for u in data.temporalcloud_users.all.users : u.id
if endswith(u.email, "@example.com")
]
}If your account uses SCIM, the identity provider — Entra ID, Okta and so on — creates groups in Temporal Cloud and keeps their membership in sync. Temporal Cloud assigns both to the integration: a SCIM group cannot be created or deleted except through it, and its membership is managed through it. Terraform must not own either.
temporalcloud_groupalways creates a new group; it cannot adopt an existing one. Since a SCIM group cannot be created any other way, a SCIM group reaches this module throughgroup_idwithcreate_group = false.- Leave
create_group_membersoff.temporalcloud_group_membersreplaces a group's whole member list on every apply, and that list belongs to the identity provider.
Roles and namespace permissions are assigned in Temporal Cloud once a group has synced, rather than
carried across from the identity provider, so they are the part of a SCIM group Terraform should own.
Set create_group = false, resolve the group's Temporal Cloud ID from its identity provider ID with
the temporalcloud_scim_group data source, and leave create_group_members off:
data "temporalcloud_scim_group" "contractors" {
# The group's Object ID in the identity provider, not its Temporal Cloud ID.
idp_id = "00000000-0000-0000-0000-000000000000"
}
module "contractor_access" {
source = "terraform-temporalcloud-modules/group/temporalcloud"
version = "~> 2.0"
create_group = false
group_id = data.temporalcloud_scim_group.contractors.id
# Required by Terraform, and unused here — the SCIM group keeps its own name.
name = ""
create_group_access = true
account_access = "none"
namespace_accesses = [
{
namespace_id = module.sandbox.namespace_id
permission = "write"
},
]
# Owned by the identity provider.
create_group_members = false
}The same pattern adopts any group created outside Terraform, SCIM or not — supply group_id directly
when you already have the Temporal Cloud ID.
Provider and Temporal Cloud behaviours worth knowing before you plan:
account_accessandpermissionare different vocabularies.account_accessisowner,admin,developer,readornone.namespace_accesses[*].permissionisadmin,writeorread.writeis not an account role,developeris not a namespace permission, and both are matched case-insensitively.ownerandadmincannot hold explicit namespace permissions. Those roles already reach every namespace, so combining them withnamespace_accessesis rejected —namespace_accesses must be empty when account_access is admin. Pairnamespace_accesseswithdeveloper,readornone.ownercan only be adopted by import. It cannot be created, updated or deleted without Temporal support.- Both child resources replace, they do not merge.
namespace_accessesowns the group's entire access map andusersowns its entire membership, so anything granted or added outside Terraform is removed on the next apply. - Empty sets are rejected, for both
namespace_accessesandaccount_access_custom_roles— omit the variable instead.usersis the exception: an empty set creates no membership resource rather than sending one. - Only one membership resource may exist per group. Calling this module twice against the same
group_idwithcreate_group_members = truein both makes each apply overwrite the other's members.
- complete — a group with an account role, namespace permissions and membership, with the namespace it points at created alongside
- scim-managed-group — permissions for a group the identity provider owns, leaving the group and its membership to SCIM
The wrappers submodule creates many groups from one call, for use with Terragrunt or
anywhere a for_each on the module block is awkward:
module "groups" {
source = "terraform-temporalcloud-modules/group/temporalcloud//wrappers"
version = "~> 2.0"
defaults = {
create_group_access = true
account_access = "read"
}
items = {
platform = { name = "platform-engineering", account_access = "developer" }
support = { name = "support" }
}
}name is required outright — it carries no default, because temporalcloud_group requires it — and
the generated Inputs table below says so. Everything else that is required is required only
behind one of the three gates, which the table cannot express.
Where a rule is caught differs, and the tables say so:
- validate — this module's own
variablevalidations. They run underterraform validateand need no credentials. - plan — the provider's schema validators. They see a module input only once its value is known, which is at plan rather than validate. The provider authenticates when it initialises, so reaching them needs an API key.
- apply — nothing catches it beforehand.
- nothing — no resource and no error. Two inputs behave this way when omitted; they are the ones worth reading twice.
Terraform demands a value for name whether or not a group is created, but it is only meaningful
when one is. Which of the two matters follows create_group:
create_group |
name |
group_id |
|---|---|---|
true (the default) |
The name of the new group. An empty string is sent as an empty name and nothing before apply objects. | Ignored — access and membership always attach to the group the module created. |
false |
Unused: the adopted group keeps its own name. Pass "". |
Required. Leave it empty and nothing happens — with no group to attach to, create_group_access and create_group_members both produce no resource, no error, and an empty group_id output. |
| Input | Omitting it |
|---|---|
account_access |
Fails at plan: Attribute account_access value must be one of: ["owner" "admin" "developer" "read" "none"], got: "". The provider requires an account role and infers none. Use none for a group whose permissions come entirely from namespace_accesses. |
| Input | Omitting it |
|---|---|
users |
Nothing happens. The provider requires the attribute and rejects an empty set, so rather than send one the module creates no membership resource — silently. A group whose membership never appears is usually this. |
| Combination | Caught at | Error |
|---|---|---|
account_access of owner or admin with namespace_accesses |
plan | namespace_accesses must be empty when account_access is <role> |
[] for namespace_accesses or account_access_custom_roles |
validate | Empty … sets are not accepted by the provider. Omit the variable instead. |
A role outside owner, admin, developer, read, none, or a namespace permission outside admin, write, read |
validate | Account access must be one of: … / Namespace access permission must be one of: … |
Both keys of every entry are required. namespace_id and permission are required in the provider
schema and in this module's object type, which the generated table cannot show: it lists the
variable, not the keys inside it.
| Name | Version |
|---|---|
| terraform | >= 1.5.7 |
| temporalcloud | >= 1.6.0 |
| Name | Version |
|---|---|
| temporalcloud | >= 1.6.0 |
No modules.
| Name | Type |
|---|---|
| temporalcloud_group.this | resource |
| temporalcloud_group_access.this | resource |
| temporalcloud_group_members.this | resource |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| account_access | The group's role on the account: owner, admin, developer, read or none, matched case-insensitively. Required when create_group_access is true — there is no account role the provider will infer, and the empty default is rejected at plan. Use none for a group whose permissions come entirely from namespace_accesses. owner can only be adopted by import — it cannot be created, updated or deleted without Temporal support |
string |
"" |
no |
| account_access_custom_roles | IDs of custom roles granted at account level, in addition to the built-in role in account_access. Optional; the group holds only the role in account_access when unset. Omit rather than passing an empty set |
set(string) |
null |
no |
| create_group | Controls if the group should be created. Set to false and supply group_id to manage the access and membership of a group that already exists, such as one provisioned by SCIM. name is required by Terraform either way, so pass "" for it when adopting an existing group |
bool |
true |
no |
| create_group_access | Controls if the group's access should be managed. Setting it to true makes account_access required. Requires either create_group = true or group_id set to an existing group; with neither, no access resource is created |
bool |
false |
no |
| create_group_members | Controls if the group's membership should be managed. Setting it to true makes a non-empty users required. Requires either create_group = true or group_id set to an existing group; with neither, no membership resource is created. Leave false for SCIM-provisioned groups, whose membership is owned by the identity provider |
bool |
false |
no |
| group_id | The ID of an existing group to attach access and membership to. Required when create_group is false, and ignored otherwise. Leaving it empty with create_group = false leaves nothing to attach to, so the module creates no resources at all and reports no error. For groups created outside Terraform — a SCIM-provisioned group, for example, whose ID comes from the temporalcloud_scim_group data source |
string |
"" |
no |
| members_timeouts | Create and delete timeouts for the group membership, as duration strings such as 30s or 2h45m. Optional; the provider's own defaults apply to whichever is unset |
object({ |
{} |
no |
| name | The name of the group to create. Unused when create_group is false, where the adopted group keeps the name it already has — pass "" there, since Terraform requires a value for it regardless |
string |
n/a | yes |
| namespace_accesses | Per-namespace permissions for the group, as a set of namespace_id and permission pairs. permission is admin, write or read, matched case-insensitively. Optional; without it the group reaches namespaces only through its account_access role. This replaces the group's entire namespace access map, so it must list every namespace the group can reach. Leave unset for groups whose account_access is owner or admin — those roles already reach every namespace and explicit permissions are rejected. Omit rather than passing an empty set |
set(object({ |
null |
no |
| timeouts | Create and delete timeouts for the group, as duration strings such as 30s or 2h45m. Optional; the provider's own defaults apply to whichever is unset |
object({ |
{} |
no |
| users | IDs of the users that make up the group, as returned by the temporalcloud_users data source or the id of a temporalcloud_user resource. Required, and non-empty, when create_group_members is true: the provider requires the attribute, so rather than send an empty membership the module creates no membership resource at all and reports no error. This replaces the group's entire membership, so users added outside Terraform are removed on the next apply |
set(string) |
[] |
no |
| Name | Description |
|---|---|
| group_account_access | The group's role on the account |
| group_account_access_custom_roles | IDs of the custom roles granted to the group at account level. Empty when none are granted |
| group_id | The unique identifier of the group. Echoes group_id when the module manages an existing group rather than creating one |
| group_members | IDs of the users that make up the group |
| group_name | The name of the group. Empty when the module manages a group it did not create |
| group_namespace_accesses | The group's complete namespace access map, as a set of namespace_id and permission pairs. Empty when the group has no namespace access |
| group_state | The current state of the group. Empty when the module manages a group it did not create |
See CONTRIBUTING.md for the development workflow, how the test layers are arranged, and the Temporal Cloud API behaviours the tests exist to guard against.
Apache-2.0 licensed. See LICENSE.