Skip to content

Commit 76c2e8f

Browse files
Permissions: Support service account in user_id field (#1051)
* Permissions: Support service account in `user_id` field Closes #994 This seems like an undocumented feature that's known to users. You can set a service account ID in the `user_id` field of permissions resources In this PR: - Make this behavior work with the new ID format (`<org>:<id>`) - Document that the field supports an SA - Add tests to make sure we don't regress it again * Do not test Grafana 8
1 parent 1ce86ab commit 76c2e8f

12 files changed

+90
-41
lines changed

docs/resources/dashboard_permission.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Optional:
7171

7272
- `role` (String) Manage permissions for `Viewer` or `Editor` roles.
7373
- `team_id` (String) ID of the team to manage permissions for. Defaults to `0`.
74-
- `user_id` (Number) ID of the user to manage permissions for. Defaults to `0`.
74+
- `user_id` (String) ID of the user or service account to manage permissions for. Defaults to `0`.
7575

7676
## Import
7777

docs/resources/data_source_permission.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ resource "grafana_user" "user" {
3939
password = "hunter2"
4040
}
4141
42+
resource "grafana_service_account" "sa" {
43+
name = "test-ds-permissions"
44+
role = "Viewer"
45+
}
46+
4247
resource "grafana_data_source_permission" "fooPermissions" {
4348
datasource_id = grafana_data_source.foo.id
4449
permissions {
@@ -53,6 +58,10 @@ resource "grafana_data_source_permission" "fooPermissions" {
5358
built_in_role = "Viewer"
5459
permission = "Query"
5560
}
61+
permissions {
62+
user_id = grafana_service_account.sa.id
63+
permission = "Query"
64+
}
5665
}
5766
```
5867

@@ -83,4 +92,4 @@ Optional:
8392

8493
- `built_in_role` (String) Name of the basic role to manage permissions for. Options: `Viewer`, `Editor` or `Admin`. Can only be set from Grafana v9.2.3+. Defaults to ``.
8594
- `team_id` (String) ID of the team to manage permissions for. Defaults to `0`.
86-
- `user_id` (Number) ID of the user to manage permissions for. Defaults to `0`.
95+
- `user_id` (String) ID of the user or service account to manage permissions for. Defaults to `0`.

docs/resources/folder_permission.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ Optional:
7070

7171
- `role` (String) Manage permissions for `Viewer` or `Editor` roles.
7272
- `team_id` (String) ID of the team to manage permissions for. Defaults to `0`.
73-
- `user_id` (Number) ID of the user to manage permissions for. Defaults to `0`.
73+
- `user_id` (String) ID of the user or service account to manage permissions for. Defaults to `0`.

docs/resources/service_account_permission.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,4 @@ Required:
7272
Optional:
7373

7474
- `team_id` (String) ID of the team to manage permissions for. Specify either this or `user_id`. Defaults to `0`.
75-
- `user_id` (Number) ID of the user to manage permissions for. Specify either this or `team_id`. Defaults to `0`.
75+
- `user_id` (String) ID of the user or service account to manage permissions for. Specify either this or `team_id`. Defaults to `0`.

examples/resources/grafana_data_source_permission/resource.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ resource "grafana_user" "user" {
2424
password = "hunter2"
2525
}
2626

27+
resource "grafana_service_account" "sa" {
28+
name = "test-ds-permissions"
29+
role = "Viewer"
30+
}
31+
2732
resource "grafana_data_source_permission" "fooPermissions" {
2833
datasource_id = grafana_data_source.foo.id
2934
permissions {
@@ -38,4 +43,8 @@ resource "grafana_data_source_permission" "fooPermissions" {
3843
built_in_role = "Viewer"
3944
permission = "Query"
4045
}
46+
permissions {
47+
user_id = grafana_service_account.sa.id
48+
permission = "Query"
49+
}
4150
}

internal/resources/grafana/resource_dashboard_permission.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ func ResourceDashboardPermission() *schema.Resource {
5151
Type: schema.TypeSet,
5252
Required: true,
5353
Description: "The permission items to add/update. Items that are omitted from the list will be removed.",
54-
// Ignore the org ID of the team when hashing. It works with or without it.
54+
// Ignore the org ID of the team/SA when hashing. It works with or without it.
5555
Set: func(i interface{}) int {
5656
m := i.(map[string]interface{})
5757
_, teamID := SplitOrgResourceID(m["team_id"].(string))
58-
return schema.HashString(m["role"].(string) + teamID + strconv.Itoa(m["user_id"].(int)) + m["permission"].(string))
58+
_, userID := SplitOrgResourceID((m["user_id"].(string)))
59+
return schema.HashString(m["role"].(string) + teamID + userID + m["permission"].(string))
5960
},
6061
Elem: &schema.Resource{
6162
Schema: map[string]*schema.Schema{
@@ -72,10 +73,10 @@ func ResourceDashboardPermission() *schema.Resource {
7273
Description: "ID of the team to manage permissions for.",
7374
},
7475
"user_id": {
75-
Type: schema.TypeInt,
76+
Type: schema.TypeString,
7677
Optional: true,
77-
Default: 0,
78-
Description: "ID of the user to manage permissions for.",
78+
Default: "0",
79+
Description: "ID of the user or service account to manage permissions for.",
7980
},
8081
"permission": {
8182
Type: schema.TypeString,
@@ -109,8 +110,10 @@ func UpdateDashboardPermissions(ctx context.Context, d *schema.ResourceData, met
109110
if teamID > 0 {
110111
permissionItem.TeamID = teamID
111112
}
112-
if permission["user_id"].(int) != -1 {
113-
permissionItem.UserID = int64(permission["user_id"].(int))
113+
_, userIDStr := SplitOrgResourceID(permission["user_id"].(string))
114+
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
115+
if userID > 0 {
116+
permissionItem.UserID = userID
114117
}
115118
permissionItem.Permission = mapPermissionStringToInt64(permission["permission"].(string))
116119
permissionList.Items = append(permissionList.Items, &permissionItem)
@@ -161,7 +164,7 @@ func ReadDashboardPermissions(ctx context.Context, d *schema.ResourceData, meta
161164
permissionItem := make(map[string]interface{})
162165
permissionItem["role"] = permission.Role
163166
permissionItem["team_id"] = strconv.FormatInt(permission.TeamID, 10)
164-
permissionItem["user_id"] = permission.UserID
167+
permissionItem["user_id"] = strconv.FormatInt(permission.UserID, 10)
165168
permissionItem["permission"] = mapPermissionInt64ToString(permission.Permission)
166169

167170
permissionItems[count] = permissionItem

internal/resources/grafana/resource_dashboard_permission_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestAccDashboardPermission_basic(t *testing.T) {
2626
Config: testAccDashboardPermissionConfig_Basic,
2727
Check: resource.ComposeAggregateTestCheckFunc(
2828
testAccDashboardPermissionsCheckExistsUID("grafana_dashboard_permission.testPermission", &dashboardUID),
29-
resource.TestCheckResourceAttr("grafana_dashboard_permission.testPermission", "permissions.#", "4"),
29+
resource.TestCheckResourceAttr("grafana_dashboard_permission.testPermission", "permissions.#", "5"),
3030
),
3131
},
3232
{
@@ -159,6 +159,11 @@ resource "grafana_user" "testAdminUser" {
159159
password = "zyx987"
160160
}
161161
162+
resource "grafana_service_account" "test" {
163+
name = "terraform-test-service-account-dashboard-perms"
164+
role = "Editor"
165+
}
166+
162167
resource "grafana_dashboard_permission" "testPermission" {
163168
dashboard_uid = grafana_dashboard.testDashboard.uid
164169
permissions {
@@ -177,6 +182,10 @@ resource "grafana_dashboard_permission" "testPermission" {
177182
user_id = grafana_user.testAdminUser.id
178183
permission = "Admin"
179184
}
185+
permissions {
186+
user_id = grafana_service_account.test.id
187+
permission = "Admin"
188+
}
180189
}
181190
`
182191
const testAccDashboardPermissionConfig_Remove = `

internal/resources/grafana/resource_data_source_permission.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ func ResourceDatasourcePermission() *schema.Resource {
3737
Type: schema.TypeSet,
3838
Required: true,
3939
Description: "The permission items to add/update. Items that are omitted from the list will be removed.",
40-
// Ignore the org ID of the team when hashing. It works with or without it.
40+
// Ignore the org ID of the team/SA when hashing. It works with or without it.
4141
Set: func(i interface{}) int {
4242
m := i.(map[string]interface{})
4343
_, teamID := SplitOrgResourceID(m["team_id"].(string))
44-
return schema.HashString(m["built_in_role"].(string) + teamID + strconv.Itoa(m["user_id"].(int)) + m["permission"].(string))
44+
_, userID := SplitOrgResourceID((m["user_id"].(string)))
45+
return schema.HashString(m["built_in_role"].(string) + teamID + userID + m["permission"].(string))
4546
},
4647
Elem: &schema.Resource{
4748
Schema: map[string]*schema.Schema{
@@ -52,10 +53,10 @@ func ResourceDatasourcePermission() *schema.Resource {
5253
Description: "ID of the team to manage permissions for.",
5354
},
5455
"user_id": {
55-
Type: schema.TypeInt,
56+
Type: schema.TypeString,
5657
Optional: true,
57-
Default: 0,
58-
Description: "ID of the user to manage permissions for.",
58+
Default: "0",
59+
Description: "ID of the user or service account to manage permissions for.",
5960
},
6061
"built_in_role": {
6162
Type: schema.TypeString,
@@ -97,8 +98,10 @@ func UpdateDatasourcePermissions(ctx context.Context, d *schema.ResourceData, me
9798
if teamID > 0 {
9899
permissionItem.TeamID = teamID
99100
}
100-
if permission["user_id"].(int) != -1 {
101-
permissionItem.UserID = int64(permission["user_id"].(int))
101+
_, userIDStr := SplitOrgResourceID(permission["user_id"].(string))
102+
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
103+
if userID > 0 {
104+
permissionItem.UserID = userID
102105
}
103106
if permission["built_in_role"].(string) != "" {
104107
permissionItem.BuiltInRole = permission["built_in_role"].(string)
@@ -137,7 +140,7 @@ func ReadDatasourcePermissions(ctx context.Context, d *schema.ResourceData, meta
137140
permissionItem := make(map[string]interface{})
138141
permissionItem["built_in_role"] = permission.BuiltInRole
139142
permissionItem["team_id"] = strconv.FormatInt(permission.TeamID, 10)
140-
permissionItem["user_id"] = permission.UserID
143+
permissionItem["user_id"] = strconv.FormatInt(permission.UserID, 10)
141144

142145
if permissionItem["permission"], err = mapDatasourcePermissionTypeToString(permission.Permission); err != nil {
143146
return diag.FromErr(err)

internal/resources/grafana/resource_data_source_permission_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestAccDatasourcePermission_basic(t *testing.T) {
2424
Config: testutils.TestAccExample(t, "resources/grafana_data_source_permission/resource.tf"),
2525
Check: resource.ComposeAggregateTestCheckFunc(
2626
testAccDatasourcePermissionsCheckExists("grafana_data_source_permission.fooPermissions", &datasourceID),
27-
resource.TestCheckResourceAttr("grafana_data_source_permission.fooPermissions", "permissions.#", "3"),
27+
resource.TestCheckResourceAttr("grafana_data_source_permission.fooPermissions", "permissions.#", "4"),
2828
),
2929
},
3030
{
@@ -35,7 +35,6 @@ func TestAccDatasourcePermission_basic(t *testing.T) {
3535
})
3636
}
3737

38-
//nolint:unused
3938
func testAccDatasourcePermissionsCheckExists(rn string, datasourceID *int64) resource.TestCheckFunc {
4039
return func(s *terraform.State) error {
4140
rs, ok := s.RootModule().Resources[rn]
@@ -66,7 +65,6 @@ func testAccDatasourcePermissionsCheckExists(rn string, datasourceID *int64) res
6665
}
6766
}
6867

69-
//nolint:unused
7068
func testAccDatasourcePermissionCheckDestroy(datasourceID *int64) resource.TestCheckFunc {
7169
return func(s *terraform.State) error {
7270
client := testutils.Provider.Meta().(*common.Client).GrafanaAPI

internal/resources/grafana/resource_folder_permission.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ func ResourceFolderPermission() *schema.Resource {
3737
Type: schema.TypeSet,
3838
Required: true,
3939
Description: "The permission items to add/update. Items that are omitted from the list will be removed.",
40-
// Ignore the org ID of the team when hashing. It works with or without it.
40+
// Ignore the org ID of the team/SA when hashing. It works with or without it.
4141
Set: func(i interface{}) int {
4242
m := i.(map[string]interface{})
4343
_, teamID := SplitOrgResourceID(m["team_id"].(string))
44-
return schema.HashString(m["role"].(string) + teamID + strconv.Itoa(m["user_id"].(int)) + m["permission"].(string))
44+
_, userID := SplitOrgResourceID(m["user_id"].(string))
45+
return schema.HashString(m["role"].(string) + teamID + userID + m["permission"].(string))
4546
},
4647
Elem: &schema.Resource{
4748
Schema: map[string]*schema.Schema{
@@ -58,10 +59,10 @@ func ResourceFolderPermission() *schema.Resource {
5859
Description: "ID of the team to manage permissions for.",
5960
},
6061
"user_id": {
61-
Type: schema.TypeInt,
62+
Type: schema.TypeString,
6263
Optional: true,
63-
Default: 0,
64-
Description: "ID of the user to manage permissions for.",
64+
Default: "0",
65+
Description: "ID of the user or service account to manage permissions for.",
6566
},
6667
"permission": {
6768
Type: schema.TypeString,
@@ -95,8 +96,10 @@ func UpdateFolderPermissions(ctx context.Context, d *schema.ResourceData, meta i
9596
if teamID > 0 {
9697
permissionItem.TeamID = teamID
9798
}
98-
if permission["user_id"].(int) != -1 {
99-
permissionItem.UserID = int64(permission["user_id"].(int))
99+
_, userIDStr := SplitOrgResourceID(permission["user_id"].(string))
100+
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
101+
if userID > 0 {
102+
permissionItem.UserID = userID
100103
}
101104
permissionItem.Permission = mapPermissionStringToInt64(permission["permission"].(string))
102105
permissionList.Items = append(permissionList.Items, &permissionItem)
@@ -129,7 +132,7 @@ func ReadFolderPermissions(ctx context.Context, d *schema.ResourceData, meta int
129132
permissionItem := make(map[string]interface{})
130133
permissionItem["role"] = permission.Role
131134
permissionItem["team_id"] = strconv.FormatInt(permission.TeamID, 10)
132-
permissionItem["user_id"] = permission.UserID
135+
permissionItem["user_id"] = strconv.FormatInt(permission.UserID, 10)
133136
permissionItem["permission"] = mapPermissionInt64ToString(permission.Permission)
134137

135138
permissionItems[count] = permissionItem

0 commit comments

Comments
 (0)