Skip to content

Commit 70e3df9

Browse files
Add grafana_service_account_permission_item resource
Part of #1000 Just like #1465
1 parent e58c3df commit 70e3df9

File tree

8 files changed

+466
-0
lines changed

8 files changed

+466
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "grafana_service_account_permission_item Resource - terraform-provider-grafana"
4+
subcategory: "Grafana OSS"
5+
description: |-
6+
Manages a single permission item for a service account. Conflicts with the "grafanaserviceaccount_permission" resource which manages the entire set of permissions for a service account.
7+
* Official documentation https://grafana.com/docs/grafana/latest/administration/service-accounts/#manage-users-and-teams-permissions-for-a-service-account-in-grafana
8+
---
9+
10+
# grafana_service_account_permission_item (Resource)
11+
12+
Manages a single permission item for a service account. Conflicts with the "grafana_service_account_permission" resource which manages the entire set of permissions for a service account.
13+
* [Official documentation](https://grafana.com/docs/grafana/latest/administration/service-accounts/#manage-users-and-teams-permissions-for-a-service-account-in-grafana)
14+
15+
## Example Usage
16+
17+
```terraform
18+
resource "grafana_service_account" "test" {
19+
name = "terraform-sa"
20+
role = "Editor"
21+
is_disabled = false
22+
}
23+
24+
resource "grafana_team" "team" {
25+
name = "Team Name"
26+
}
27+
28+
resource "grafana_user" "user" {
29+
30+
login = "user.name"
31+
password = "my-password"
32+
}
33+
34+
resource "grafana_folder" "collection" {
35+
title = "Folder Title"
36+
}
37+
38+
resource "grafana_service_account_permission_item" "on_team" {
39+
service_account_id = grafana_service_account.test.id
40+
team = grafana_team.team.id
41+
permission = "View"
42+
}
43+
44+
resource "grafana_service_account_permission_item" "on_user" {
45+
service_account_id = grafana_service_account.test.id
46+
user = grafana_user.user.id
47+
permission = "Admin"
48+
}
49+
```
50+
51+
<!-- schema generated by tfplugindocs -->
52+
## Schema
53+
54+
### Required
55+
56+
- `permission` (String) the permission to be assigned
57+
- `service_account_id` (String) The ID of the service account.
58+
59+
### Optional
60+
61+
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
62+
- `team` (String) the team onto which the permission is to be assigned
63+
- `user` (String) the user or service account onto which the permission is to be assigned
64+
65+
### Read-Only
66+
67+
- `id` (String) The ID of this resource.
68+
69+
## Import
70+
71+
Import is supported using the following syntax:
72+
73+
```shell
74+
terraform import grafana_service_account_permission_item.name "{{ serviceAccountID }}:{{ type (role, team, or user) }}:{{ identifier }}"
75+
terraform import grafana_service_account_permission_item.name "{{ orgID }}:{{ serviceAccountID }}:{{ type (role, team, or user) }}:{{ identifier }}"
76+
```
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
terraform import grafana_service_account_permission_item.name "{{ serviceAccountID }}:{{ type (role, team, or user) }}:{{ identifier }}"
2+
terraform import grafana_service_account_permission_item.name "{{ orgID }}:{{ serviceAccountID }}:{{ type (role, team, or user) }}:{{ identifier }}"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
resource "grafana_service_account" "test" {
2+
name = "terraform-sa"
3+
role = "Editor"
4+
is_disabled = false
5+
}
6+
7+
resource "grafana_team" "team" {
8+
name = "Team Name"
9+
}
10+
11+
resource "grafana_user" "user" {
12+
13+
login = "user.name"
14+
password = "my-password"
15+
}
16+
17+
resource "grafana_folder" "collection" {
18+
title = "Folder Title"
19+
}
20+
21+
resource "grafana_service_account_permission_item" "on_team" {
22+
service_account_id = grafana_service_account.test.id
23+
team = grafana_team.team.id
24+
permission = "View"
25+
}
26+
27+
resource "grafana_service_account_permission_item" "on_user" {
28+
service_account_id = grafana_service_account.test.id
29+
user = grafana_user.user.id
30+
permission = "Admin"
31+
}
32+

internal/resources/grafana/common_resource_permission.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ func (r *resourcePermissionBase) readItem(id string, checkExistsFunc func(client
161161
func (r *resourcePermissionBase) writeItem(itemID string, data *resourcePermissionItemBaseModel) diag.Diagnostics {
162162
client, orgID := r.clientFromNewOrgResource(data.OrgID.ValueString())
163163
data.OrgID = types.StringValue(strconv.FormatInt(orgID, 10))
164+
_, itemID = SplitOrgResourceID(itemID)
164165
data.ResourceID = types.StringValue(itemID)
165166

166167
var err error
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package grafana
2+
3+
import (
4+
"context"
5+
"strconv"
6+
7+
"github.com/grafana/grafana-openapi-client-go/client"
8+
"github.com/grafana/terraform-provider-grafana/v2/internal/common"
9+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
10+
"github.com/hashicorp/terraform-plugin-framework/path"
11+
"github.com/hashicorp/terraform-plugin-framework/resource"
12+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
13+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
14+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
15+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
16+
"github.com/hashicorp/terraform-plugin-framework/types"
17+
)
18+
19+
var (
20+
resourceServiceAccountPermissionItemName = "grafana_service_account_permission_item"
21+
resourceServiceAccountPermissionItemID = common.NewResourceID(common.OptionalIntIDField("orgID"), common.StringIDField("serviceAccountID"), common.StringIDField("type (role, team, or user)"), common.StringIDField("identifier"))
22+
23+
// Check interface
24+
_ resource.ResourceWithImportState = (*resourceServiceAccountPermissionItem)(nil)
25+
)
26+
27+
func makeResourceServiceAccountPermissionItem() *common.Resource {
28+
resourceStruct := &resourceServiceAccountPermissionItem{
29+
resourcePermissionBase: resourcePermissionBase{
30+
resourceType: serviceAccountsPermissionsType,
31+
},
32+
}
33+
return common.NewResource(resourceServiceAccountPermissionItemName, resourceServiceAccountPermissionItemID, resourceStruct)
34+
}
35+
36+
type resourceServiceAccountPermissionItemModel struct {
37+
ID types.String `tfsdk:"id"`
38+
OrgID types.String `tfsdk:"org_id"`
39+
Team types.String `tfsdk:"team"`
40+
User types.String `tfsdk:"user"`
41+
Permission types.String `tfsdk:"permission"`
42+
ServiceAccountID types.String `tfsdk:"service_account_id"`
43+
}
44+
45+
// Framework doesn't support embedding a base struct: https://github.com/hashicorp/terraform-plugin-framework/issues/242
46+
func (m *resourceServiceAccountPermissionItemModel) ToBase() *resourcePermissionItemBaseModel {
47+
return &resourcePermissionItemBaseModel{
48+
ID: m.ID,
49+
OrgID: m.OrgID,
50+
Team: m.Team,
51+
User: m.User,
52+
Permission: m.Permission,
53+
}
54+
}
55+
56+
func (m *resourceServiceAccountPermissionItemModel) SetFromBase(base *resourcePermissionItemBaseModel) {
57+
m.ServiceAccountID = base.ResourceID
58+
m.ID = base.ID
59+
m.OrgID = base.OrgID
60+
m.Team = base.Team
61+
m.User = base.User
62+
m.Permission = base.Permission
63+
}
64+
65+
type resourceServiceAccountPermissionItem struct{ resourcePermissionBase }
66+
67+
func (r *resourceServiceAccountPermissionItem) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
68+
resp.TypeName = resourceServiceAccountPermissionItemName
69+
}
70+
71+
func (r *resourceServiceAccountPermissionItem) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
72+
resp.Schema = schema.Schema{
73+
MarkdownDescription: `Manages a single permission item for a service account. Conflicts with the "grafana_service_account_permission" resource which manages the entire set of permissions for a service account.
74+
* [Official documentation](https://grafana.com/docs/grafana/latest/administration/service-accounts/#manage-users-and-teams-permissions-for-a-service-account-in-grafana)`,
75+
Attributes: r.addInSchemaAttributes(map[string]schema.Attribute{
76+
"service_account_id": schema.StringAttribute{
77+
Required: true,
78+
Description: "The ID of the service account.",
79+
PlanModifiers: []planmodifier.String{
80+
stringplanmodifier.RequiresReplace(),
81+
&orgScopedAttributePlanModifier{},
82+
},
83+
},
84+
}),
85+
}
86+
87+
// Role is not supported for service account permissions
88+
delete(resp.Schema.Attributes, permissionTargetRole)
89+
for key, attr := range resp.Schema.Attributes {
90+
if key != permissionTargetTeam && key != permissionTargetUser {
91+
continue
92+
}
93+
attrCast := attr.(schema.StringAttribute)
94+
attrCast.Validators = []validator.String{stringvalidator.ExactlyOneOf(
95+
path.MatchRoot(permissionTargetTeam),
96+
path.MatchRoot(permissionTargetUser),
97+
)}
98+
resp.Schema.Attributes[key] = attrCast
99+
}
100+
}
101+
102+
func (r *resourceServiceAccountPermissionItem) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
103+
readData, diags := r.readItem(req.ID, r.serviceAccountQuery)
104+
if diags != nil {
105+
resp.Diagnostics = diags
106+
return
107+
}
108+
if readData == nil {
109+
resp.Diagnostics.AddError("Resource not found", "Resource not found")
110+
return
111+
}
112+
var data resourceServiceAccountPermissionItemModel
113+
data.SetFromBase(readData)
114+
115+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
116+
}
117+
118+
func (r *resourceServiceAccountPermissionItem) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
119+
// Read Terraform plan data into the model
120+
var data resourceServiceAccountPermissionItemModel
121+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
122+
if resp.Diagnostics.HasError() {
123+
return
124+
}
125+
base := data.ToBase()
126+
if diags := r.writeItem(data.ServiceAccountID.ValueString(), base); diags != nil {
127+
resp.Diagnostics = diags
128+
return
129+
}
130+
base.ResourceID = data.ServiceAccountID
131+
data.SetFromBase(base)
132+
133+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
134+
}
135+
136+
func (r *resourceServiceAccountPermissionItem) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
137+
// Read Terraform state data into the model
138+
var data resourceServiceAccountPermissionItemModel
139+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
140+
141+
// Read from API
142+
readData, diags := r.readItem(data.ID.ValueString(), r.serviceAccountQuery)
143+
if diags != nil {
144+
resp.Diagnostics = diags
145+
return
146+
}
147+
if readData == nil {
148+
resp.State.RemoveResource(ctx)
149+
return
150+
}
151+
readData.ResourceID = data.ServiceAccountID
152+
data.SetFromBase(readData)
153+
154+
// Save data into Terraform state
155+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
156+
}
157+
158+
func (r *resourceServiceAccountPermissionItem) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
159+
// Read Terraform plan data into the model
160+
var data resourceServiceAccountPermissionItemModel
161+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
162+
if resp.Diagnostics.HasError() {
163+
return
164+
}
165+
base := data.ToBase()
166+
if diags := r.writeItem(data.ServiceAccountID.ValueString(), base); diags != nil {
167+
resp.Diagnostics = diags
168+
return
169+
}
170+
base.ResourceID = data.ServiceAccountID
171+
data.SetFromBase(base)
172+
173+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
174+
}
175+
176+
func (r *resourceServiceAccountPermissionItem) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
177+
// Read Terraform prior state data into the model
178+
var data resourceServiceAccountPermissionItemModel
179+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
180+
data.Permission = types.StringValue("")
181+
182+
if diags := r.writeItem(data.ServiceAccountID.ValueString(), data.ToBase()); diags != nil {
183+
resp.Diagnostics = diags
184+
}
185+
}
186+
187+
func (r *resourceServiceAccountPermissionItem) serviceAccountQuery(client *client.GrafanaHTTPAPI, serviceAccountID string) error {
188+
idNumerical, err := strconv.ParseInt(serviceAccountID, 10, 64)
189+
if err != nil {
190+
return err
191+
}
192+
_, err = client.ServiceAccounts.RetrieveServiceAccount(idNumerical)
193+
return err
194+
}

0 commit comments

Comments
 (0)