Skip to content

Commit 9799739

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

File tree

8 files changed

+458
-0
lines changed

8 files changed

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

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: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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/roles-and-permissions/access-control/)
75+
* [HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/service_account_permissions/)`,
76+
Attributes: r.addInSchemaAttributes(map[string]schema.Attribute{
77+
"service_account_id": schema.StringAttribute{
78+
Required: true,
79+
Description: "The ID of the service account.",
80+
PlanModifiers: []planmodifier.String{
81+
stringplanmodifier.RequiresReplace(),
82+
&orgScopedAttributePlanModifier{},
83+
},
84+
},
85+
}),
86+
}
87+
88+
// Role is not supported for service account permissions
89+
delete(resp.Schema.Attributes, permissionTargetRole)
90+
for key, attr := range resp.Schema.Attributes {
91+
if key != permissionTargetTeam && key != permissionTargetUser {
92+
continue
93+
}
94+
attrCast := attr.(schema.StringAttribute)
95+
attrCast.Validators = []validator.String{stringvalidator.ExactlyOneOf(
96+
path.MatchRoot(permissionTargetTeam),
97+
path.MatchRoot(permissionTargetUser),
98+
)}
99+
resp.Schema.Attributes[key] = attrCast
100+
}
101+
102+
}
103+
104+
func (r *resourceServiceAccountPermissionItem) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
105+
readData, diags := r.readItem(req.ID, r.serviceAccountQuery)
106+
if diags != nil {
107+
resp.Diagnostics = diags
108+
return
109+
}
110+
if readData == nil {
111+
resp.Diagnostics.AddError("Resource not found", "Resource not found")
112+
return
113+
}
114+
var data resourceServiceAccountPermissionItemModel
115+
data.SetFromBase(readData)
116+
117+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
118+
}
119+
120+
func (r *resourceServiceAccountPermissionItem) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
121+
// Read Terraform plan data into the model
122+
var data resourceServiceAccountPermissionItemModel
123+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
124+
if resp.Diagnostics.HasError() {
125+
return
126+
}
127+
base := data.ToBase()
128+
if diags := r.writeItem(data.ServiceAccountID.ValueString(), base); diags != nil {
129+
resp.Diagnostics = diags
130+
return
131+
}
132+
base.ResourceID = data.ServiceAccountID
133+
data.SetFromBase(base)
134+
135+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
136+
}
137+
138+
func (r *resourceServiceAccountPermissionItem) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
139+
// Read Terraform state data into the model
140+
var data resourceServiceAccountPermissionItemModel
141+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
142+
143+
// Read from API
144+
readData, diags := r.readItem(data.ID.ValueString(), r.serviceAccountQuery)
145+
if diags != nil {
146+
resp.Diagnostics = diags
147+
return
148+
}
149+
if readData == nil {
150+
resp.State.RemoveResource(ctx)
151+
return
152+
}
153+
readData.ResourceID = data.ServiceAccountID
154+
data.SetFromBase(readData)
155+
156+
// Save data into Terraform state
157+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
158+
}
159+
160+
func (r *resourceServiceAccountPermissionItem) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
161+
// Read Terraform plan data into the model
162+
var data resourceServiceAccountPermissionItemModel
163+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
164+
if resp.Diagnostics.HasError() {
165+
return
166+
}
167+
base := data.ToBase()
168+
if diags := r.writeItem(data.ServiceAccountID.ValueString(), base); diags != nil {
169+
resp.Diagnostics = diags
170+
return
171+
}
172+
base.ResourceID = data.ServiceAccountID
173+
data.SetFromBase(base)
174+
175+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
176+
}
177+
178+
func (r *resourceServiceAccountPermissionItem) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
179+
// Read Terraform prior state data into the model
180+
var data resourceServiceAccountPermissionItemModel
181+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
182+
data.Permission = types.StringValue("")
183+
184+
if diags := r.writeItem(data.ServiceAccountID.ValueString(), data.ToBase()); diags != nil {
185+
resp.Diagnostics = diags
186+
}
187+
}
188+
189+
func (r *resourceServiceAccountPermissionItem) serviceAccountQuery(client *client.GrafanaHTTPAPI, serviceAccountID string) error {
190+
idNumerical, err := strconv.ParseInt(serviceAccountID, 10, 64)
191+
if err != nil {
192+
return err
193+
}
194+
_, err = client.ServiceAccounts.RetrieveServiceAccount(idNumerical)
195+
return err
196+
}

0 commit comments

Comments
 (0)