Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit921f4af

Browse files
committed
feat: add coderd_organization resource
1 parentcc7335a commit921f4af

File tree

4 files changed

+517
-0
lines changed

4 files changed

+517
-0
lines changed

‎docs/resources/organization.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title:"coderd_organization Resource - coderd"
4+
subcategory:""
5+
description:|-
6+
An organization on the coder deployment.
7+
---
8+
9+
#coderd_organization (Resource)
10+
11+
An organization on the coder deployment.
12+
13+
14+
15+
<!-- schema generated by tfplugindocs-->
16+
##Schema
17+
18+
###Required
19+
20+
-`name` (String)
21+
22+
###Optional
23+
24+
-`description` (String)
25+
-`display_name` (String)
26+
-`icon` (String)
27+
-`members` (Set of String) Members of the organization, by ID. If null, members will not be added or removed by Terraform.
28+
29+
###Read-Only
30+
31+
-`id` (String) The ID of this resource.
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package provider
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"github.com/coder/coder/v2/coderd/util/slice"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/google/uuid"
13+
"github.com/hashicorp/terraform-plugin-framework/attr"
14+
"github.com/hashicorp/terraform-plugin-framework/path"
15+
"github.com/hashicorp/terraform-plugin-framework/resource"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
18+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
19+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
20+
"github.com/hashicorp/terraform-plugin-framework/types"
21+
"github.com/hashicorp/terraform-plugin-log/tflog"
22+
)
23+
24+
// Ensure provider defined types fully satisfy framework interfaces.
25+
var_ resource.Resource=&OrganizationResource{}
26+
var_ resource.ResourceWithImportState=&OrganizationResource{}
27+
28+
funcNewOrganizationResource() resource.Resource {
29+
return&OrganizationResource{}
30+
}
31+
32+
// OrganizationResource defines the resource implementation.
33+
typeOrganizationResourcestruct {
34+
data*CoderdProviderData
35+
}
36+
37+
// OrganizationResourceModel describes the resource data model.
38+
typeOrganizationResourceModelstruct {
39+
IDUUID`tfsdk:"id"`
40+
41+
Name types.String`tfsdk:"name"`
42+
DisplayName types.String`tfsdk:"display_name"`
43+
Description types.String`tfsdk:"description"`
44+
Icon types.String`tfsdk:"icon"`
45+
Members types.Set`tfsdk:"members"`
46+
}
47+
48+
func (r*OrganizationResource)Metadata(ctx context.Context,req resource.MetadataRequest,resp*resource.MetadataResponse) {
49+
resp.TypeName=req.ProviderTypeName+"_organization"
50+
}
51+
52+
func (r*OrganizationResource)Schema(ctx context.Context,req resource.SchemaRequest,resp*resource.SchemaResponse) {
53+
resp.Schema= schema.Schema{
54+
MarkdownDescription:"An organization on the coder deployment.",
55+
56+
Attributes:map[string]schema.Attribute{
57+
"id": schema.StringAttribute{
58+
CustomType:UUIDType,
59+
Computed:true,
60+
PlanModifiers: []planmodifier.String{
61+
stringplanmodifier.UseStateForUnknown(),
62+
},
63+
},
64+
"name": schema.StringAttribute{
65+
Required:true,
66+
},
67+
"display_name": schema.StringAttribute{
68+
Optional:true,
69+
Computed:true,
70+
},
71+
"description": schema.StringAttribute{
72+
Optional:true,
73+
Computed:true,
74+
Default:stringdefault.StaticString(""),
75+
},
76+
"icon": schema.StringAttribute{
77+
Optional:true,
78+
Computed:true,
79+
Default:stringdefault.StaticString(""),
80+
},
81+
"members": schema.SetAttribute{
82+
MarkdownDescription:"Members of the organization, by ID. If null, members will not be added or removed by Terraform.",
83+
ElementType:UUIDType,
84+
Optional:true,
85+
},
86+
// TODO: Custom roles, premium license gated
87+
},
88+
}
89+
}
90+
91+
func (r*OrganizationResource)Configure(ctx context.Context,req resource.ConfigureRequest,resp*resource.ConfigureResponse) {
92+
// Prevent panic if the provider has not been configured.
93+
ifreq.ProviderData==nil {
94+
return
95+
}
96+
97+
data,ok:=req.ProviderData.(*CoderdProviderData)
98+
99+
if!ok {
100+
resp.Diagnostics.AddError(
101+
"Unexpected Resource Configure Type",
102+
fmt.Sprintf("Expected *CoderdProviderData, got: %T. Please report this issue to the provider developers.",req.ProviderData),
103+
)
104+
105+
return
106+
}
107+
108+
r.data=data
109+
}
110+
111+
func (r*OrganizationResource)Create(ctx context.Context,req resource.CreateRequest,resp*resource.CreateResponse) {
112+
vardataOrganizationResourceModel
113+
114+
// Read Terraform plan data into the model
115+
resp.Diagnostics.Append(req.Plan.Get(ctx,&data)...)
116+
117+
ifresp.Diagnostics.HasError() {
118+
return
119+
}
120+
121+
client:=r.data.Client
122+
123+
displayName:=data.Name.ValueString()
124+
ifdata.DisplayName.ValueString()!="" {
125+
displayName=data.DisplayName.ValueString()
126+
}
127+
128+
tflog.Trace(ctx,"creating organization")
129+
org,err:=client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
130+
Name:data.Name.ValueString(),
131+
DisplayName:displayName,
132+
Description:data.Description.ValueString(),
133+
Icon:data.Icon.ValueString(),
134+
})
135+
iferr!=nil {
136+
resp.Diagnostics.AddError("Failed to create organization",err.Error())
137+
return
138+
}
139+
tflog.Trace(ctx,"successfully created organization",map[string]any{
140+
"id":org.ID,
141+
})
142+
data.ID=UUIDValue(org.ID)
143+
data.DisplayName=types.StringValue(org.DisplayName)
144+
145+
tflog.Trace(ctx,"setting organization members")
146+
varmembers []UUID
147+
resp.Diagnostics.Append(data.Members.ElementsAs(ctx,&members,false)...)
148+
ifresp.Diagnostics.HasError() {
149+
return
150+
}
151+
for_,memberID:=rangemembers {
152+
_,err=client.PostOrganizationMember(ctx,org.ID,memberID.ValueString())
153+
iferr!=nil {
154+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to add member %s to organization %s, got error: %s",memberID,org.ID,err))
155+
return
156+
}
157+
}
158+
159+
me,err:=client.User(ctx,codersdk.Me)
160+
iferr!=nil {
161+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to get current user, got error: %s",err))
162+
return
163+
}
164+
165+
// If the logged-in user isn't in the members list, remove them from the organization (as they were added by default)
166+
// Ideally, future Coder versions won't add the logged-in user by default.
167+
if!slice.Contains(members,UUIDValue(me.ID)) {
168+
err=client.DeleteOrganizationMember(ctx,org.ID,codersdk.Me)
169+
iferr!=nil {
170+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to delete self from new organization: %s",err))
171+
}
172+
}
173+
174+
tflog.Trace(ctx,"successfully set organization members")
175+
// Save data into Terraform state
176+
resp.Diagnostics.Append(resp.State.Set(ctx,&data)...)
177+
}
178+
179+
func (r*OrganizationResource)Read(ctx context.Context,req resource.ReadRequest,resp*resource.ReadResponse) {
180+
vardataOrganizationResourceModel
181+
182+
// Read Terraform prior state data into the model
183+
resp.Diagnostics.Append(req.State.Get(ctx,&data)...)
184+
185+
ifresp.Diagnostics.HasError() {
186+
return
187+
}
188+
189+
client:=r.data.Client
190+
191+
orgID:=data.ID.ValueUUID()
192+
org,err:=client.Organization(ctx,orgID)
193+
iferr!=nil {
194+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to get organization by ID, got error: %s",err))
195+
}
196+
197+
data.Name=types.StringValue(org.Name)
198+
data.DisplayName=types.StringValue(org.DisplayName)
199+
data.Description=types.StringValue(org.Description)
200+
data.Icon=types.StringValue(org.Icon)
201+
if!data.Members.IsNull() {
202+
members,err:=client.OrganizationMembers(ctx,orgID)
203+
iferr!=nil {
204+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to get organization members, got error: %s",err))
205+
return
206+
}
207+
memberIDs:=make([]attr.Value,0,len(members))
208+
for_,member:=rangemembers {
209+
memberIDs=append(memberIDs,UUIDValue(member.UserID))
210+
}
211+
data.Members=types.SetValueMust(UUIDType,memberIDs)
212+
}
213+
214+
// Save updated data into Terraform state
215+
resp.Diagnostics.Append(resp.State.Set(ctx,&data)...)
216+
}
217+
218+
func (r*OrganizationResource)Update(ctx context.Context,req resource.UpdateRequest,resp*resource.UpdateResponse) {
219+
vardataOrganizationResourceModel
220+
221+
// Read Terraform plan data into the model
222+
resp.Diagnostics.Append(req.Plan.Get(ctx,&data)...)
223+
224+
ifresp.Diagnostics.HasError() {
225+
return
226+
}
227+
228+
client:=r.data.Client
229+
orgID:=data.ID.ValueUUID()
230+
231+
orgMembers,err:=client.OrganizationMembers(ctx,orgID)
232+
iferr!=nil {
233+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to get organization members , got error: %s",err))
234+
return
235+
}
236+
237+
if!data.Members.IsNull() {
238+
varplannedMembers []UUID
239+
resp.Diagnostics.Append(data.Members.ElementsAs(ctx,&plannedMembers,false)...)
240+
ifresp.Diagnostics.HasError() {
241+
return
242+
}
243+
curMembers:=make([]uuid.UUID,0,len(orgMembers))
244+
for_,member:=rangeorgMembers {
245+
curMembers=append(curMembers,member.UserID)
246+
}
247+
add,remove:=memberDiff(curMembers,plannedMembers)
248+
tflog.Trace(ctx,"updating organization members",map[string]any{
249+
"new_members":add,
250+
"removed_members":remove,
251+
})
252+
for_,memberID:=rangeadd {
253+
_,err:=client.PostOrganizationMember(ctx,orgID,memberID)
254+
iferr!=nil {
255+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to add member %s to organization %s, got error: %s",memberID,orgID,err))
256+
return
257+
}
258+
}
259+
for_,memberID:=rangeremove {
260+
err:=client.DeleteOrganizationMember(ctx,orgID,memberID)
261+
iferr!=nil {
262+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to remove member %s from organization %s, got error: %s",memberID,orgID,err))
263+
return
264+
}
265+
}
266+
tflog.Trace(ctx,"successfully updated organization members")
267+
}
268+
269+
tflog.Trace(ctx,"updating organization",map[string]any{
270+
"id":orgID,
271+
"new_name":data.Name,
272+
"new_display_name":data.DisplayName,
273+
"new_description":data.Description,
274+
"new_icon":data.Icon,
275+
})
276+
_,err=client.UpdateOrganization(ctx,orgID.String(), codersdk.UpdateOrganizationRequest{
277+
Name:data.Name.ValueString(),
278+
DisplayName:data.DisplayName.ValueString(),
279+
Description:data.Description.ValueStringPointer(),
280+
Icon:data.Icon.ValueStringPointer(),
281+
})
282+
iferr!=nil {
283+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to update organization %s, got error: %s",orgID,err))
284+
return
285+
}
286+
tflog.Trace(ctx,"successfully updated organization")
287+
288+
// Save updated data into Terraform state
289+
resp.Diagnostics.Append(resp.State.Set(ctx,&data)...)
290+
}
291+
292+
func (r*OrganizationResource)Delete(ctx context.Context,req resource.DeleteRequest,resp*resource.DeleteResponse) {
293+
vardataOrganizationResourceModel
294+
295+
// Read Terraform prior state data into the model
296+
resp.Diagnostics.Append(req.State.Get(ctx,&data)...)
297+
298+
ifresp.Diagnostics.HasError() {
299+
return
300+
}
301+
302+
client:=r.data.Client
303+
orgID:=data.ID.ValueUUID()
304+
305+
tflog.Trace(ctx,"deleting organization",map[string]any{
306+
"id":orgID,
307+
})
308+
309+
err:=client.DeleteOrganization(ctx,orgID.String())
310+
iferr!=nil {
311+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Unable to delete organization %s, got error: %s",orgID,err))
312+
return
313+
}
314+
tflog.Trace(ctx,"successfully deleted organization")
315+
316+
// Read Terraform prior state data into the model
317+
resp.Diagnostics.Append(req.State.Get(ctx,&data)...)
318+
}
319+
320+
func (r*OrganizationResource)ImportState(ctx context.Context,req resource.ImportStateRequest,resp*resource.ImportStateResponse) {
321+
resource.ImportStatePassthroughID(ctx,path.Root("id"),req,resp)
322+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp