Repository: hashicorp/terraform-provider-vault

Issue:
[Enhancement]: Add support for multi-token terraform secret engine role
### Description

Adding support for new plugin feature: https://github.com/hashicorp/vault-plugin-secrets-terraform/pull/89

### Affected Resource(s) and/or Data Source(s)

_No response_

### Potential Terraform Configuration

```terraform

```

### References

_No response_

### Would you like to implement a fix?

None

Discussion:

Solution patch:
diff --git a/.gitignore b/.gitignore
index 16d6daa1..bee6815f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,4 @@ scratch
 
 # others
 .swp
+.vscode
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1b8db6e..15c72e4e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
 ## Unreleased
 
+* New parameters for `vault_terraform_cloud_secret_role` to support multi-team tokens, by @drewmullen ([#2498](https://github.com/hashicorp/terraform-provider-vault/pull/2498))
+
 ## 5.3.0 (Sep 4, 2025)
 
 FEATURES:
diff --git a/vault/resource_terraform_cloud_secret_role.go b/vault/resource_terraform_cloud_secret_role.go
index 2d329cb8..ed12f38e 100644
--- a/vault/resource_terraform_cloud_secret_role.go
+++ b/vault/resource_terraform_cloud_secret_role.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
 
 	"github.com/hashicorp/terraform-provider-vault/internal/provider"
 )
@@ -43,6 +44,18 @@ func terraformCloudSecretRoleResource() *schema.Resource {
 				ForceNew:    true,
 				Description: "The path of the Terraform Cloud Secret Backend the role belongs to.",
 			},
+			"credential_type": {
+				Type:         schema.TypeString,
+				Optional:     true,
+				ForceNew:     true,
+				Description:  "The type of credential to generate. Valid values are 'team', 'team_legacy', 'user', or 'organization'.",
+				ValidateFunc: validation.StringInSlice([]string{"team", "team_legacy", "user", "organization"}, false),
+			},
+			"description": {
+				Type:        schema.TypeString,
+				Optional:    true,
+				Description: "Description of the role. This is used as a prefix to help identify the token in the HCP Terraform UI. Only valid with 'team' or 'user' credential types.",
+			},
 			"organization": {
 				Type:        schema.TypeString,
 				Optional:    true,
@@ -114,6 +127,16 @@ func terraformCloudSecretRoleWrite(d *schema.ResourceData, meta interface{}) err
 	if v, ok := d.GetOkExists("user_id"); ok {
 		payload["user_id"] = v
 	}
+	if v, ok := d.GetOk("description"); ok {
+		payload["description"] = v
+	}
+
+	if provider.IsAPISupported(meta, provider.VaultVersion120) {
+		// parse credential_type field if 1.20 server or higher
+		if v, ok := d.GetOk("credential_type"); ok {
+			payload["credential_type"] = v
+		}
+	}
 
 	log.Printf("[DEBUG] Configuring Terraform Cloud secrets backend role at %q", path)
 
@@ -167,6 +190,12 @@ func terraformCloudSecretRoleRead(d *schema.ResourceData, meta interface{}) erro
 	d.Set("user_id", data["user_id"])
 	d.Set("max_ttl", data["max_ttl"])
 	d.Set("ttl", data["ttl"])
+	if data["description"] != nil {
+		d.Set("description", data["description"])
+	}
+	if data["credential_type"] != nil {
+		d.Set("credential_type", data["credential_type"])
+	}
 
 	return nil
 }
diff --git a/website/docs/r/terraform_cloud_secret_role.html.md b/website/docs/r/terraform_cloud_secret_role.html.md
index db90b58e..9af8841f 100644
--- a/website/docs/r/terraform_cloud_secret_role.html.md
+++ b/website/docs/r/terraform_cloud_secret_role.html.md
@@ -51,6 +51,10 @@ The following arguments are supported:
 
 * `ttl` - (Optional) Specifies the TTL for this role, in seconds.
 
+* `credential_type` - (Optional) The type of credential to generate. Valid values are 'team', 'team_legacy', 'user', or 'organization'. Can only create multiple-team tokens with `team`. 
+
+* `description` - (Optional) Description of the role. This is used as a prefix to help identify the token in the HCP Terraform UI. Only valid with `team` or `user` credential types.
+
 ## Attributes Reference
 
 No additional attributes are exported by this resource.



Test patch:
diff --git a/vault/resource_terraform_cloud_secret_role_test.go b/vault/resource_terraform_cloud_secret_role_test.go
index 806450a1..16b39c40 100644
--- a/vault/resource_terraform_cloud_secret_role_test.go
+++ b/vault/resource_terraform_cloud_secret_role_test.go
@@ -16,7 +16,7 @@ import (
 	"github.com/hashicorp/terraform-provider-vault/testutil"
 )
 
-func TestTerraformCloudSecretRole(t *testing.T) {
+func TestTerraformCloudSecretRole_basic(t *testing.T) {
 	backend := acctest.RandomWithPrefix("tf-test-backend")
 	name := acctest.RandomWithPrefix("tf-test-name")
 	organization := "hashicorp-vault-testing"
@@ -73,6 +73,53 @@ func TestTerraformCloudSecretRole(t *testing.T) {
 	})
 }
 
+func TestTerraformCloudSecretRole_options(t *testing.T) {
+	backend := acctest.RandomWithPrefix("tf-test-backend")
+	name := acctest.RandomWithPrefix("tf-test-name")
+	vals := testutil.SkipTestEnvUnset(t, "TEST_TF_TOKEN", "TEST_TF_TEAM_ID")
+	token, teamID := vals[0], vals[1]
+	resource.Test(t, resource.TestCase{
+		ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(context.Background(), t),
+		PreCheck: func() {
+			testutil.TestAccPreCheck(t)
+			SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion120)
+		},
+		CheckDestroy: testAccTerraformCloudSecretRoleCheckDestroy,
+		Steps: []resource.TestStep{
+			{
+				Config: testTerraformCloudSecretRole_optionsInitialConfig(backend, token, name, teamID),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "name", name+"_team_id"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "description", "team role"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "credential_type", "team"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "team_id", teamID),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "ttl", "100"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "max_ttl", "200"),
+
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team_legacy", "name", name+"_team_legacy_id"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team_legacy", "credential_type", "team_legacy"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team_legacy", "team_id", teamID),
+				),
+			},
+			{
+				Config: testTerraformCloudSecretRole_optionsUpdatedConfig(backend, token, name, teamID),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "name", name+"_team_id"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "description", "team role2"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "credential_type", "team"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "team_id", teamID),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "ttl", "200"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team", "max_ttl", "300"),
+
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team_legacy", "name", name+"_team_legacy_id"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team_legacy", "credential_type", "team_legacy"),
+					resource.TestCheckResourceAttr("vault_terraform_cloud_secret_role.test_team_legacy", "team_id", teamID),
+				),
+			},
+		},
+	})
+}
+
 func testAccTerraformCloudSecretRoleCheckDestroy(s *terraform.State) error {
 	for _, rs := range s.RootModule().Resources {
 		if rs.Type != "vault_terraform_cloud_secret_role" {
@@ -163,6 +210,60 @@ resource "vault_terraform_cloud_secret_role" "test_user" {
 `, backend, token, name, organization, teamId, userId)
 }
 
+func testTerraformCloudSecretRole_optionsInitialConfig(backend, token, name, teamId string) string {
+	return fmt.Sprintf(`
+resource "vault_terraform_cloud_secret_backend" "test" {
+  backend     = "%s"
+  description = "test description"
+  token       = "%s"
+}
+
+resource "vault_terraform_cloud_secret_role" "test_team" {
+  backend         = vault_terraform_cloud_secret_backend.test.backend
+  name            = "%[3]s_team_id"
+  team_id         = "%[4]s"
+  credential_type = "team"
+  description     = "team role"
+  ttl             = 100
+  max_ttl         = 200
+}
+
+resource "vault_terraform_cloud_secret_role" "test_team_legacy" {
+  backend         = vault_terraform_cloud_secret_backend.test.backend
+  name            = "%[3]s_team_legacy_id"
+  team_id         = "%[4]s"
+  credential_type = "team_legacy"
+}
+`, backend, token, name, teamId)
+}
+
+func testTerraformCloudSecretRole_optionsUpdatedConfig(backend, token, name, teamId string) string {
+	return fmt.Sprintf(`
+resource "vault_terraform_cloud_secret_backend" "test" {
+  backend     = "%s"
+  description = "test description"
+  token       = "%s"
+}
+
+resource "vault_terraform_cloud_secret_role" "test_team" {
+  backend         = vault_terraform_cloud_secret_backend.test.backend
+  name            = "%[3]s_team_id"
+  team_id         = "%[4]s"
+  credential_type = "team"
+  description     = "team role2"
+  ttl             = 200
+  max_ttl         = 300
+}
+
+resource "vault_terraform_cloud_secret_role" "test_team_legacy" {
+  backend         = vault_terraform_cloud_secret_backend.test.backend
+  name            = "%[3]s_team_legacy_id"
+  team_id         = "%[4]s"
+  credential_type = "team_legacy"
+}
+`, backend, token, name, teamId)
+}
+
 func TestTerraformCloudSecretBackendRoleNameFromPath(t *testing.T) {
 	{
 		name, err := terraformCloudSecretRoleNameFromPath("foo/role/bar")