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

Commited8270c

Browse files
authored
feat: addorg_sync_idp_groups attribute tocoderd_organization resource (#182)
1 parent5fa9117 commited8270c

File tree

6 files changed

+207
-23
lines changed

6 files changed

+207
-23
lines changed

‎docs/resources/organization.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,35 @@ An organization on the Coder deployment.
1515
~>**Warning**
1616
This resource is only compatible with Coder version[2.16.0](https://github.com/coder/coder/releases/tag/v2.16.0) and later.
1717

18+
##Example Usage
1819

20+
```terraform
21+
resource "coderd_organization" "blueberry" {
22+
name = "blueberry"
23+
display_name = "Blueberry"
24+
description = "The organization for blueberries"
25+
icon = "/emojis/1fad0.png"
26+
27+
org_sync_idp_groups = [
28+
"wibble",
29+
"wobble",
30+
]
31+
32+
group_sync {
33+
field = "coder_groups"
34+
mapping = {
35+
toast = [coderd_group.bread.id]
36+
}
37+
}
38+
39+
role_sync {
40+
field = "coder_roles"
41+
mapping = {
42+
manager = ["organization-user-admin"]
43+
}
44+
}
45+
}
46+
```
1947

2048
<!-- schema generated by tfplugindocs-->
2149
##Schema
@@ -30,6 +58,7 @@ This resource is only compatible with Coder version [2.16.0](https://github.com/
3058
-`display_name` (String) Display name of the organization. Defaults to name.
3159
-`group_sync` (Block, Optional) Group sync settings to sync groups from an IdP. (see[below for nested schema](#nestedblock--group_sync))
3260
-`icon` (String)
61+
-`org_sync_idp_groups` (Set of String) Claims from the IdP provider that will give users access to this organization.
3362
-`role_sync` (Block, Optional) Role sync settings to sync organization roles from an IdP. (see[below for nested schema](#nestedblock--role_sync))
3463

3564
###Read-Only
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
resource"coderd_organization""blueberry" {
2+
name="blueberry"
3+
display_name="Blueberry"
4+
description="The organization for blueberries"
5+
icon="/emojis/1fad0.png"
6+
7+
org_sync_idp_groups=[
8+
"wibble",
9+
"wobble",
10+
]
11+
12+
group_sync {
13+
field="coder_groups"
14+
mapping={
15+
toast= [coderd_group.bread.id]
16+
}
17+
}
18+
19+
role_sync {
20+
field="coder_roles"
21+
mapping={
22+
manager= ["organization-user-admin"]
23+
}
24+
}
25+
}

‎internal/provider/organization_resource.go

Lines changed: 112 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"regexp"
77

8+
"github.com/coder/coder/v2/coderd/util/slice"
89
"github.com/coder/coder/v2/codersdk"
910
"github.com/coder/terraform-provider-coderd/internal/codersdkvalidator"
1011
"github.com/google/uuid"
@@ -40,8 +41,9 @@ type OrganizationResourceModel struct {
4041
Description types.String`tfsdk:"description"`
4142
Icon types.String`tfsdk:"icon"`
4243

43-
GroupSync types.Object`tfsdk:"group_sync"`
44-
RoleSync types.Object`tfsdk:"role_sync"`
44+
OrgSyncIdpGroups types.Set`tfsdk:"org_sync_idp_groups"`
45+
GroupSync types.Object`tfsdk:"group_sync"`
46+
RoleSync types.Object`tfsdk:"role_sync"`
4547
}
4648

4749
typeGroupSyncModelstruct {
@@ -134,6 +136,12 @@ This resource is only compatible with Coder version [2.16.0](https://github.com/
134136
Computed:true,
135137
Default:stringdefault.StaticString(""),
136138
},
139+
140+
"org_sync_idp_groups": schema.SetAttribute{
141+
ElementType:types.StringType,
142+
Optional:true,
143+
MarkdownDescription:"Claims from the IdP provider that will give users access to this organization.",
144+
},
137145
},
138146

139147
Blocks:map[string]schema.Block{
@@ -361,21 +369,38 @@ func (r *OrganizationResource) Create(ctx context.Context, req resource.CreateRe
361369
// default it.
362370
data.DisplayName=types.StringValue(org.DisplayName)
363371

364-
// Now apply group and role sync settings, if specified
365372
orgID:=data.ID.ValueUUID()
366-
tflog.Trace(ctx,"updating group sync",map[string]any{
367-
"orgID":orgID,
368-
})
373+
374+
// Apply org sync patches, if specified
375+
if!data.OrgSyncIdpGroups.IsNull() {
376+
tflog.Trace(ctx,"updating org sync",map[string]any{
377+
"orgID":orgID,
378+
})
379+
380+
varclaims []string
381+
resp.Diagnostics.Append(data.OrgSyncIdpGroups.ElementsAs(ctx,&claims,false)...)
382+
ifresp.Diagnostics.HasError() {
383+
return
384+
}
385+
386+
resp.Diagnostics.Append(r.patchOrgSyncMapping(ctx,orgID, []string{},claims)...)
387+
}
388+
389+
// Apply group and role sync settings, if specified
369390
if!data.GroupSync.IsNull() {
391+
tflog.Trace(ctx,"updating group sync",map[string]any{
392+
"orgID":orgID,
393+
})
394+
370395
resp.Diagnostics.Append(r.patchGroupSync(ctx,orgID,data.GroupSync)...)
371396
ifresp.Diagnostics.HasError() {
372397
return
373398
}
374399
}
375-
tflog.Trace(ctx,"updating role sync",map[string]any{
376-
"orgID":orgID,
377-
})
378400
if!data.RoleSync.IsNull() {
401+
tflog.Trace(ctx,"updating role sync",map[string]any{
402+
"orgID":orgID,
403+
})
379404
resp.Diagnostics.Append(r.patchRoleSync(ctx,orgID,data.RoleSync)...)
380405
ifresp.Diagnostics.HasError() {
381406
return
@@ -423,19 +448,42 @@ func (r *OrganizationResource) Update(ctx context.Context, req resource.UpdateRe
423448
"icon":org.Icon,
424449
})
425450

426-
tflog.Trace(ctx,"updating group sync",map[string]any{
427-
"orgID":orgID,
428-
})
451+
// Apply org sync patches, if specified
452+
if!data.OrgSyncIdpGroups.IsNull() {
453+
tflog.Trace(ctx,"updating org sync mappings",map[string]any{
454+
"orgID":orgID,
455+
})
456+
457+
varstateOrganizationResourceModel
458+
resp.Diagnostics.Append(req.State.Get(ctx,&state)...)
459+
varcurrentClaims []string
460+
resp.Diagnostics.Append(state.OrgSyncIdpGroups.ElementsAs(ctx,&currentClaims,false)...)
461+
462+
varplannedClaims []string
463+
resp.Diagnostics.Append(data.OrgSyncIdpGroups.ElementsAs(ctx,&plannedClaims,false)...)
464+
ifresp.Diagnostics.HasError() {
465+
return
466+
}
467+
468+
resp.Diagnostics.Append(r.patchOrgSyncMapping(ctx,orgID,currentClaims,plannedClaims)...)
469+
ifresp.Diagnostics.HasError() {
470+
return
471+
}
472+
}
473+
429474
if!data.GroupSync.IsNull() {
475+
tflog.Trace(ctx,"updating group sync",map[string]any{
476+
"orgID":orgID,
477+
})
430478
resp.Diagnostics.Append(r.patchGroupSync(ctx,orgID,data.GroupSync)...)
431479
ifresp.Diagnostics.HasError() {
432480
return
433481
}
434482
}
435-
tflog.Trace(ctx,"updating role sync",map[string]any{
436-
"orgID":orgID,
437-
})
438483
if!data.RoleSync.IsNull() {
484+
tflog.Trace(ctx,"updating role sync",map[string]any{
485+
"orgID":orgID,
486+
})
439487
resp.Diagnostics.Append(r.patchRoleSync(ctx,orgID,data.RoleSync)...)
440488
ifresp.Diagnostics.HasError() {
441489
return
@@ -456,6 +504,21 @@ func (r *OrganizationResource) Delete(ctx context.Context, req resource.DeleteRe
456504

457505
orgID:=data.ID.ValueUUID()
458506

507+
// Remove org sync mappings, if we were managing them
508+
if!data.OrgSyncIdpGroups.IsNull() {
509+
tflog.Trace(ctx,"deleting org sync mappings",map[string]any{
510+
"orgID":orgID,
511+
})
512+
513+
varclaims []string
514+
resp.Diagnostics.Append(data.OrgSyncIdpGroups.ElementsAs(ctx,&claims,false)...)
515+
ifresp.Diagnostics.HasError() {
516+
return
517+
}
518+
519+
resp.Diagnostics.Append(r.patchOrgSyncMapping(ctx,orgID,claims, []string{})...)
520+
}
521+
459522
tflog.Trace(ctx,"deleting organization",map[string]any{
460523
"id":orgID,
461524
"name":data.Name.ValueString(),
@@ -554,3 +617,37 @@ func (r *OrganizationResource) patchRoleSync(
554617

555618
returndiags
556619
}
620+
621+
func (r*OrganizationResource)patchOrgSyncMapping(
622+
ctx context.Context,
623+
orgID uuid.UUID,
624+
currentClaims,plannedClaims []string,
625+
) diag.Diagnostics {
626+
vardiags diag.Diagnostics
627+
628+
add,remove:=slice.SymmetricDifference(currentClaims,plannedClaims)
629+
varaddMappings []codersdk.IDPSyncMapping[uuid.UUID]
630+
for_,claim:=rangeadd {
631+
addMappings=append(addMappings, codersdk.IDPSyncMapping[uuid.UUID]{
632+
Given:claim,
633+
Gets:orgID,
634+
})
635+
}
636+
varremoveMappings []codersdk.IDPSyncMapping[uuid.UUID]
637+
for_,claim:=rangeremove {
638+
removeMappings=append(removeMappings, codersdk.IDPSyncMapping[uuid.UUID]{
639+
Given:claim,
640+
Gets:orgID,
641+
})
642+
}
643+
644+
_,err:=r.Client.PatchOrganizationIDPSyncMapping(ctx, codersdk.PatchOrganizationIDPSyncMappingRequest{
645+
Add:addMappings,
646+
Remove:removeMappings,
647+
})
648+
iferr!=nil {
649+
diags.AddError("Org Sync Update error",err.Error())
650+
}
651+
652+
returndiags
653+
}

‎internal/provider/organization_resource_test.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,19 @@ func TestAccOrganizationResource(t *testing.T) {
4242
cfg2.DisplayName=ptr.Ref("Example Organization New")
4343

4444
cfg3:=cfg2
45-
cfg3.GroupSync=ptr.Ref(codersdk.GroupSyncSettings{
45+
cfg3.OrgSyncIdpGroups= []string{"wibble","wobble"}
46+
47+
cfg4:=cfg3
48+
cfg4.OrgSyncIdpGroups= []string{"wibbley","wobbley"}
49+
50+
cfg5:=cfg4
51+
cfg5.GroupSync=ptr.Ref(codersdk.GroupSyncSettings{
4652
Field:"wibble",
4753
Mapping:map[string][]uuid.UUID{
4854
"wibble": {uuid.MustParse("6e57187f-6543-46ab-a62c-a10065dd4314")},
4955
},
5056
})
51-
cfg3.RoleSync=ptr.Ref(codersdk.RoleSyncSettings{
57+
cfg5.RoleSync=ptr.Ref(codersdk.RoleSyncSettings{
5258
Field:"wobble",
5359
Mapping:map[string][]string{
5460
"wobble": {"wobbly"},
@@ -86,9 +92,25 @@ func TestAccOrganizationResource(t *testing.T) {
8692
statecheck.ExpectKnownValue("coderd_organization.test",tfjsonpath.New("display_name"),knownvalue.StringExact("Example Organization New")),
8793
},
8894
},
89-
// Addgroup and role sync
95+
// Addorg sync
9096
{
9197
Config:cfg3.String(t),
98+
ConfigStateChecks: []statecheck.StateCheck{
99+
statecheck.ExpectKnownValue("coderd_organization.test",tfjsonpath.New("org_sync_idp_groups").AtSliceIndex(0),knownvalue.StringExact("wibble")),
100+
statecheck.ExpectKnownValue("coderd_organization.test",tfjsonpath.New("org_sync_idp_groups").AtSliceIndex(1),knownvalue.StringExact("wobble")),
101+
},
102+
},
103+
// Patch org sync
104+
{
105+
Config:cfg4.String(t),
106+
ConfigStateChecks: []statecheck.StateCheck{
107+
statecheck.ExpectKnownValue("coderd_organization.test",tfjsonpath.New("org_sync_idp_groups").AtSliceIndex(0),knownvalue.StringExact("wibbley")),
108+
statecheck.ExpectKnownValue("coderd_organization.test",tfjsonpath.New("org_sync_idp_groups").AtSliceIndex(1),knownvalue.StringExact("wobbley")),
109+
},
110+
},
111+
// Add group and role sync
112+
{
113+
Config:cfg5.String(t),
92114
ConfigStateChecks: []statecheck.StateCheck{
93115
statecheck.ExpectKnownValue("coderd_organization.test",tfjsonpath.New("group_sync").AtMapKey("field"),knownvalue.StringExact("wibble")),
94116
statecheck.ExpectKnownValue("coderd_organization.test",tfjsonpath.New("group_sync").AtMapKey("mapping").AtMapKey("wibble").AtSliceIndex(0),knownvalue.StringExact("6e57187f-6543-46ab-a62c-a10065dd4314")),
@@ -110,8 +132,9 @@ type testAccOrganizationResourceConfig struct {
110132
Description*string
111133
Icon*string
112134

113-
GroupSync*codersdk.GroupSyncSettings
114-
RoleSync*codersdk.RoleSyncSettings
135+
OrgSyncIdpGroups []string
136+
GroupSync*codersdk.GroupSyncSettings
137+
RoleSync*codersdk.RoleSyncSettings
115138
}
116139

117140
func (ctestAccOrganizationResourceConfig)String(t*testing.T)string {
@@ -128,6 +151,14 @@ resource "coderd_organization" "test" {
128151
description = {{orNull .Description}}
129152
icon = {{orNull .Icon}}
130153
154+
{{- if .OrgSyncIdpGroups}}
155+
org_sync_idp_groups = [
156+
{{- range $name := .OrgSyncIdpGroups }}
157+
"{{$name}}",
158+
{{- end}}
159+
]
160+
{{- end}}
161+
131162
{{- if .GroupSync}}
132163
group_sync {
133164
field = "{{.GroupSync.Field}}"

‎internal/provider/organization_sync_settings_resource.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func (r *OrganizationSyncSettingsResource) Delete(ctx context.Context, req resou
244244
tflog.Trace(ctx,"deleting organization sync",map[string]any{})
245245
_,err:=r.Client.PatchOrganizationIDPSyncConfig(ctx, codersdk.PatchOrganizationIDPSyncConfigRequest{
246246
// This disables organization sync without causing state conflicts for
247-
// organization resources that might still specify `sync_mapping`.
247+
// organization resources that might still specify `org_sync_idp_groups`.
248248
Field:"",
249249
})
250250
iferr!=nil {

‎internal/provider/util.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ func computeDirectoryHash(directory string) (string, error) {
8383
returnhex.EncodeToString(hash.Sum(nil)),nil
8484
}
8585

86-
// memberDiff returns the members to add and remove from the group, given the current members and the planned members.
87-
// plannedMembers is deliberately our custom type, as Terraform cannot automatically produce `[]uuid.UUID` from a set.
86+
// memberDiff returns the members to add and remove from the group, given the
87+
// current members and the planned members. plannedMembers is deliberately our
88+
// custom type, as Terraform cannot automatically produce `[]uuid.UUID` from a
89+
// set.
8890
funcmemberDiff(currentMembers []uuid.UUID,plannedMembers []UUID) (add,remove []string) {
8991
curSet:=make(map[uuid.UUID]struct{},len(currentMembers))
9092
planSet:=make(map[uuid.UUID]struct{},len(plannedMembers))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp