Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions internal/resources/grafana/resource_role_assignment_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,17 @@ func (r *resourceRoleAssignmentItem) Create(ctx context.Context, req resource.Cr
resp.Diagnostics.AddError("Failed to parse team ID", err.Error())
return
}
roleAssignments.Teams = append(roleAssignments.Teams, teamID)
// Check if team is already assigned to avoid duplicates
teamExists := false
for _, existingTeamID := range roleAssignments.Teams {
if existingTeamID == teamID {
teamExists = true
break
}
}
if !teamExists {
roleAssignments.Teams = append(roleAssignments.Teams, teamID)
}
assignmentType = "team"
resourceID = teamIDStr
case !data.UserID.IsNull():
Expand All @@ -172,7 +182,17 @@ func (r *resourceRoleAssignmentItem) Create(ctx context.Context, req resource.Cr
resp.Diagnostics.AddError("Failed to parse user ID", err.Error())
return
}
roleAssignments.Users = append(roleAssignments.Users, userID)
// Check if user is already assigned to avoid duplicates
userExists := false
for _, existingUserID := range roleAssignments.Users {
if existingUserID == userID {
userExists = true
break
}
}
if !userExists {
roleAssignments.Users = append(roleAssignments.Users, userID)
}
assignmentType = "user"
resourceID = data.UserID.ValueString()
case !data.ServiceAccountID.IsNull():
Expand All @@ -182,7 +202,17 @@ func (r *resourceRoleAssignmentItem) Create(ctx context.Context, req resource.Cr
resp.Diagnostics.AddError("Failed to parse service account ID", err.Error())
return
}
roleAssignments.ServiceAccounts = append(roleAssignments.ServiceAccounts, serviceAccountID)
// Check if service account is already assigned to avoid duplicates
saExists := false
for _, existingSAID := range roleAssignments.ServiceAccounts {
if existingSAID == serviceAccountID {
saExists = true
break
}
}
if !saExists {
roleAssignments.ServiceAccounts = append(roleAssignments.ServiceAccounts, serviceAccountID)
}
assignmentType = "service_account"
resourceID = serviceAccountIDStr
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,29 @@ func TestAccRoleAssignmentItem_withCloudServiceAccount(t *testing.T) {
})
}

func TestAccRoleAssignmentItem_NoDuplicates(t *testing.T) {
testutils.CheckEnterpriseTestsEnabled(t, ">=9.0.0")

testName := acctest.RandString(10)
var role models.RoleDTO

resource.ParallelTest(t, resource.TestCase{
ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
CheckDestroy: roleAssignmentCheckExists.destroyed(&role, nil),
Steps: []resource.TestStep{
{
Config: roleAssignmentItemNoDuplicatesConfig(testName),
Check: resource.ComposeTestCheckFunc(
roleAssignmentCheckExists.exists("grafana_role.test", &role),
// Verify the role assignment doesn't contain duplicates
resource.TestCheckResourceAttr("grafana_role_assignment_item.user1", "role_uid", testName),
resource.TestCheckResourceAttr("grafana_role_assignment_item.team", "role_uid", testName),
),
},
},
})
}

func roleAssignmentItemConfig(name string) string {
return fmt.Sprintf(`
resource "grafana_role" "test" {
Expand Down Expand Up @@ -289,3 +312,46 @@ resource "grafana_role_assignment_item" "service_account" {
}
`, name)
}

func roleAssignmentItemNoDuplicatesConfig(name string) string {
return fmt.Sprintf(`
// Create a test role that will have multiple assignments
// This role will be the target for both user and team assignments
resource "grafana_role" "test" {
name = "%[1]s"
description = "test desc"
version = 1
uid = "%[1]s"
global = true
group = "testgroup"
display_name = "testdisplay"
hidden = true
}

// Create a test team that will be assigned to the role
resource "grafana_team" "test_team" {
name = "%[1]s"
}

// Create a test user that will be assigned to the same role
resource "grafana_user" "test_user" {
email = "%[1][email protected]"
login = "%[1][email protected]"
password = "12345"
}

// Multiple grafana_role_assignment_item resources targeting the SAME role.
// This setup reproduces the bug where duplicate assignments could be created
// when multiple assignment items reference the same role UID.

resource grafana_role_assignment_item "user1" {
role_uid = grafana_role.test.uid // Same role as team assignment
user_id = grafana_user.test_user.id
}

resource grafana_role_assignment_item "team" {
role_uid = grafana_role.test.uid // Same role as user1 assignment
team_id = grafana_team.test_team.id
}
`, name)
}
3 changes: 3 additions & 0 deletions pkg/generate/postprocessing/replace_references.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ var knownReferences = []string{
"grafana_team_external_group.team_id=grafana_team.id",
"grafana_team_preferences.home_dashboard_uid=grafana_dashboard.uid",
"grafana_team_preferences.team_id=grafana_team.id",
"grafana_user.role_uid=grafana_role.uid",
"grafana_user.team_id=grafana_team.id",
"grafana_user.user_id=grafana_user.id",
}

func ReplaceReferences(fpath string, plannedState *tfjson.Plan, extraKnownReferences []string) error {
Expand Down
2 changes: 1 addition & 1 deletion provider_schema.json

Large diffs are not rendered by default.

Loading