Skip to content

Commit e9d4b45

Browse files
grafana_cloud_stack_service_account: Compatibility with roles/permissions (#1546)
Currently, service accounts created on Grafana Cloud stacks cannot be assigned permissions and roles (See the test in `internal/resources/cloud/resource_cloud_stack_service_account_test.go` for an example
1 parent fd7d124 commit e9d4b45

File tree

5 files changed

+116
-6
lines changed

5 files changed

+116
-6
lines changed

internal/resources/cloud/resource_cloud_stack_service_account_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,97 @@ func TestAccGrafanaServiceAccountFromCloud(t *testing.T) {
6161
})
6262
}
6363

64+
func TestAccGrafanaServiceAccountFromCloud_AssignRoleOrPermissions(t *testing.T) {
65+
testutils.CheckCloudAPITestsEnabled(t)
66+
67+
var stack gcom.FormattedApiInstance
68+
prefix := "tfsatest"
69+
slug := GetRandomStackName(prefix)
70+
71+
resource.ParallelTest(t, resource.TestCase{
72+
PreCheck: func() {
73+
testAccDeleteExistingStacks(t, prefix)
74+
},
75+
ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
76+
CheckDestroy: testAccStackCheckDestroy(&stack),
77+
Steps: []resource.TestStep{
78+
// SA permission item
79+
{
80+
Config: testAccGrafanaServiceAccountFromCloud(slug, slug, false, "Admin") + `
81+
provider "grafana" {
82+
alias = "stack"
83+
auth = grafana_cloud_stack_service_account_token.management_token.key
84+
url = grafana_cloud_stack.test.url
85+
}
86+
87+
resource "grafana_team" "test" {
88+
provider = grafana.stack
89+
name = "test"
90+
}
91+
92+
resource "grafana_service_account_permission_item" "test" {
93+
provider = grafana.stack
94+
service_account_id = grafana_cloud_stack_service_account.management.id
95+
permission = "Admin"
96+
team = grafana_team.test.id
97+
}
98+
`,
99+
},
100+
// Role assignment
101+
{
102+
Config: testAccGrafanaServiceAccountFromCloud(slug, slug, false, "Admin") + `
103+
provider "grafana" {
104+
alias = "stack"
105+
auth = grafana_cloud_stack_service_account_token.management_token.key
106+
url = grafana_cloud_stack.test.url
107+
}
108+
109+
resource "grafana_role" "test" {
110+
provider = grafana.stack
111+
name = "test"
112+
description = "test desc"
113+
version = 1
114+
uid = "test"
115+
group = "testgroup"
116+
display_name = "testdisplay"
117+
}
118+
119+
resource "grafana_role_assignment" "test" {
120+
provider = grafana.stack
121+
role_uid = grafana_role.test.uid
122+
service_accounts = [grafana_cloud_stack_service_account.management.id]
123+
}
124+
`,
125+
},
126+
// SA permission
127+
{
128+
Config: testAccGrafanaServiceAccountFromCloud(slug, slug, false, "Admin") + `
129+
provider "grafana" {
130+
alias = "stack"
131+
auth = grafana_cloud_stack_service_account_token.management_token.key
132+
url = grafana_cloud_stack.test.url
133+
}
134+
135+
resource "grafana_team" "test" {
136+
provider = grafana.stack
137+
name = "test"
138+
}
139+
140+
resource "grafana_service_account_permission" "test" {
141+
provider = grafana.stack
142+
service_account_id = grafana_cloud_stack_service_account.management.id
143+
144+
permissions {
145+
team_id = grafana_team.test.id
146+
permission = "Admin"
147+
}
148+
}
149+
`,
150+
},
151+
},
152+
})
153+
}
154+
64155
func TestAccGrafanaServiceAccountFromCloudNoneRole(t *testing.T) {
65156
testutils.CheckCloudAPITestsEnabled(t)
66157

internal/resources/grafana/common_resource_permission.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,11 @@ func (r *resourcePermissionBase) writeItem(itemID string, data *resourcePermissi
170170
}
171171

172172
data.OrgID = types.StringValue(strconv.FormatInt(orgID, 10))
173-
_, itemID = SplitOrgResourceID(itemID)
173+
if r.resourceType == serviceAccountsPermissionsType {
174+
_, itemID = SplitServiceAccountID(itemID)
175+
} else {
176+
_, itemID = SplitOrgResourceID(itemID)
177+
}
174178
data.ResourceID = types.StringValue(itemID)
175179

176180
switch {

internal/resources/grafana/oss_org_id.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ func SplitOrgResourceID(id string) (int64, string) {
5151
return 0, id
5252
}
5353

54+
// SplitServiceAccountID is like SplitOrgResourceID but for service accounts
55+
// Service accounts can also come from Grafana Cloud where the format is <stackSlug>:<serviceAccountID>
56+
func SplitServiceAccountID(id string) (int64, string) {
57+
if strings.ContainsRune(id, ':') {
58+
parts := strings.SplitN(id, ":", 2)
59+
orgID, err := strconv.ParseInt(parts[0], 10, 64)
60+
if err != nil {
61+
return 1, parts[1]
62+
}
63+
return orgID, parts[1]
64+
}
65+
66+
return 0, id
67+
}
68+
5469
// OAPIClientFromExistingOrgResource creates a client from the ID of an org-scoped resource
5570
// Those IDs are in the <orgID>:<resourceID> format
5671
func OAPIClientFromExistingOrgResource(meta interface{}, id string) (*goapi.GrafanaHTTPAPI, int64, string) {

internal/resources/grafana/resource_role_assignment.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Manages the entire set of assignments for a role. Assignments that aren't specif
6363
Description: "IDs of service accounts that the role should be assigned to.",
6464
// Ignore the org ID of the team when hashing. It works with or without it.
6565
Set: func(i interface{}) int {
66-
_, saID := SplitOrgResourceID(i.(string))
66+
_, saID := SplitServiceAccountID(i.(string))
6767
return schema.HashString(saID)
6868
},
6969
Elem: &schema.Schema{
@@ -152,7 +152,7 @@ func collectRoleAssignents(r interface{}, orgScoped bool) []int64 {
152152
for _, rID := range r.(*schema.Set).List() {
153153
var id int64
154154
if orgScoped {
155-
_, idStr := SplitOrgResourceID(rID.(string))
155+
_, idStr := SplitServiceAccountID(rID.(string))
156156
id, _ = strconv.ParseInt(idStr, 10, 64)
157157
} else {
158158
if idInt, ok := rID.(int); ok {

internal/resources/grafana/resource_service_account_permission.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ Manages the entire set of permissions for a service account. Permissions that ar
3636
ForceNew: true,
3737
Description: "The id of the service account.",
3838
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
39-
_, old = SplitOrgResourceID(old)
40-
_, new = SplitOrgResourceID(new)
39+
_, old = SplitServiceAccountID(old)
40+
_, new = SplitServiceAccountID(new)
4141
return old == new
4242
},
4343
},
@@ -54,7 +54,7 @@ Manages the entire set of permissions for a service account. Permissions that ar
5454

5555
func resourceServiceAccountPermissionGet(d *schema.ResourceData, meta interface{}) (string, error) {
5656
client, _ := OAPIClientFromNewOrgResource(meta, d)
57-
_, id := SplitOrgResourceID(d.Get("service_account_id").(string))
57+
_, id := SplitServiceAccountID(d.Get("service_account_id").(string))
5858
if d.Id() != "" {
5959
client, _, id = OAPIClientFromExistingOrgResource(meta, d.Id())
6060
}

0 commit comments

Comments
 (0)