@@ -8,7 +8,10 @@ import (
88 "github.com/coder/coder/v2/codersdk"
99 "github.com/coder/terraform-provider-coderd/internal/codersdkvalidator"
1010 "github.com/google/uuid"
11+ "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
12+ "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
1113 "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
14+ "github.com/hashicorp/terraform-plugin-framework/diag"
1215 "github.com/hashicorp/terraform-plugin-framework/path"
1316 "github.com/hashicorp/terraform-plugin-framework/resource"
1417 "github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -17,6 +20,7 @@ import (
1720 "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
1821 "github.com/hashicorp/terraform-plugin-framework/schema/validator"
1922 "github.com/hashicorp/terraform-plugin-framework/types"
23+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
2024 "github.com/hashicorp/terraform-plugin-log/tflog"
2125)
2226
@@ -41,6 +45,18 @@ type OrganizationResourceModel struct {
4145 RoleSync types.Object `tfsdk:"role_sync"`
4246}
4347
48+ type GroupSyncModel struct {
49+ Field types.String `tfsdk:"field"`
50+ RegexFilter types.String `tfsdk:"regex_filter"`
51+ AutoCreateMissing types.Bool `tfsdk:"auto_create_missing"`
52+ Mapping types.Map `tfsdk:"mapping"`
53+ }
54+
55+ type RoleSyncModel struct {
56+ Field types.String `tfsdk:"field"`
57+ Mapping types.Map `tfsdk:"mapping"`
58+ }
59+
4460func NewOrganizationResource () resource.Resource {
4561 return & OrganizationResource {}
4662}
@@ -101,7 +117,7 @@ func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRe
101117 stringvalidator .LengthAtLeast (1 ),
102118 },
103119 },
104- "regex " : schema.StringAttribute {
120+ "regex_filter " : schema.StringAttribute {
105121 Optional : true ,
106122 MarkdownDescription : "A regular expression that will be used to " +
107123 "filter the groups returned by the OIDC provider. Any group " +
@@ -116,9 +132,12 @@ func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRe
116132 "they are missing." ,
117133 },
118134 "mapping" : schema.MapAttribute {
119- ElementType : UUIDType ,
135+ ElementType : types. ListType { ElemType : UUIDType } ,
120136 Optional : true ,
121137 MarkdownDescription : "A map from OIDC group name to Coder group ID." ,
138+ Validators : []validator.Map {
139+ mapvalidator .ValueListsAre (listvalidator .ValueStringsAre (stringvalidator .Any ())),
140+ },
122141 },
123142 },
124143 },
@@ -133,10 +152,13 @@ func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRe
133152 },
134153 },
135154 "mapping" : schema.MapAttribute {
136- ElementType : UUIDType ,
155+ ElementType : types. ListType { ElemType : UUIDType } ,
137156 Optional : true ,
138157 MarkdownDescription : "A map from OIDC group name to Coder " +
139158 "organization role." ,
159+ Validators : []validator.Map {
160+ mapvalidator .ValueListsAre (listvalidator .ValueStringsAre (stringvalidator .Any ())),
161+ },
140162 },
141163 },
142164 },
@@ -191,6 +213,26 @@ func (r *OrganizationResource) Read(ctx context.Context, req resource.ReadReques
191213 }
192214 }
193215
216+ if ! data .GroupSync .IsNull () {
217+ _ , err := r .Client .GroupIDPSyncSettings (ctx , data .ID .ValueUUID ().String ())
218+ if err != nil {
219+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("unable to get organization group sync settings, got error: %s" , err ))
220+ return
221+ }
222+
223+ // data.GroupSync = ???
224+ }
225+
226+ if ! data .RoleSync .IsNull () {
227+ _ , err := r .Client .RoleIDPSyncSettings (ctx , data .ID .ValueUUID ().String ())
228+ if err != nil {
229+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("unable to get organization role sync settings, got error: %s" , err ))
230+ return
231+ }
232+
233+ // data.RoleSync = ???
234+ }
235+
194236 // We've fetched the organization ID from state, and the latest values for
195237 // everything else from the backend. Ensure that any mutable data is synced
196238 // with the backend.
@@ -241,6 +283,21 @@ func (r *OrganizationResource) Create(ctx context.Context, req resource.CreateRe
241283 // default it.
242284 data .DisplayName = types .StringValue (org .DisplayName )
243285
286+ // Now apply group and role sync settings, if specified
287+ orgID := data .ID .ValueUUID ()
288+ tflog .Trace (ctx , "updating group sync" , map [string ]any {
289+ "orgID" : orgID ,
290+ })
291+ if ! data .GroupSync .IsNull () {
292+ r .patchGroupSync (ctx , orgID , data .GroupSync )
293+ }
294+ tflog .Trace (ctx , "updating role sync" , map [string ]any {
295+ "orgID" : orgID ,
296+ })
297+ if ! data .RoleSync .IsNull () {
298+ r .patchRoleSync (ctx , orgID , data .RoleSync )
299+ }
300+
244301 // Save data into Terraform state
245302 resp .Diagnostics .Append (resp .State .Set (ctx , & data )... )
246303}
@@ -282,12 +339,17 @@ func (r *OrganizationResource) Update(ctx context.Context, req resource.UpdateRe
282339 "icon" : org .Icon ,
283340 })
284341
285- if data .GroupSync .IsNull () {
286- err = r .patchGroupSync (ctx , orgID , data .GroupSync )
287- if err != nil {
288- resp .Diagnostics .AddError ("Group Sync Update error" , err .Error ())
289- return
290- }
342+ tflog .Trace (ctx , "updating group sync" , map [string ]any {
343+ "orgID" : orgID ,
344+ })
345+ if ! data .GroupSync .IsNull () {
346+ r .patchGroupSync (ctx , orgID , data .GroupSync )
347+ }
348+ tflog .Trace (ctx , "updating role sync" , map [string ]any {
349+ "orgID" : orgID ,
350+ })
351+ if ! data .RoleSync .IsNull () {
352+ r .patchRoleSync (ctx , orgID , data .RoleSync )
291353 }
292354
293355 // Save updated data into Terraform state
@@ -331,48 +393,65 @@ func (r *OrganizationResource) ImportState(ctx context.Context, req resource.Imp
331393func (r * OrganizationResource ) patchGroupSync (
332394 ctx context.Context ,
333395 orgID uuid.UUID ,
334- groupSyncAttr types.Object ,
335- ) error {
336- var settings codersdk. GroupSyncSettings
396+ groupSyncObject types.Object ,
397+ ) diag. Diagnostics {
398+ var diags diag. Diagnostics
337399
338- field , ok := groupSyncAttr .Attributes ()["field" ].(types.String )
339- if ! ok {
340- return fmt .Errorf ("oh jeez" )
400+ // Read values from Terraform
401+ var groupSyncData GroupSyncModel
402+ diags .Append (groupSyncObject .As (ctx , & groupSyncData , basetypes.ObjectAsOptions {})... )
403+ if diags .HasError () {
404+ return diags
341405 }
342- settings .Field = field .ValueString ()
343406
344- mappingMap , ok := groupSyncAttr . Attributes ()[ "mapping" ].(types. Map )
345- if ! ok {
346- return fmt . Errorf ( "oh jeez" )
347- }
348- var mapping map [ string ][]uuid. UUID
349- diags := mappingMap . ElementsAs (ctx , mapping , false )
407+ // Convert that into the type used to send the PATCH to the backend
408+ var groupSync codersdk. GroupSyncSettings
409+ groupSync . Field = groupSyncData . Field . ValueString ( )
410+ groupSync . RegexFilter = regexp . MustCompile ( groupSyncData . RegexFilter . ValueString ())
411+ groupSync . AutoCreateMissing = groupSyncData . AutoCreateMissing . ValueBool ()
412+ diags . Append ( groupSyncData . Mapping . ElementsAs (ctx , & groupSync . Mapping , false ) ... )
350413 if diags .HasError () {
351- return fmt . Errorf ( "oh jeez" )
414+ return diags
352415 }
353- settings .Mapping = mapping
354416
355- regexFilterStr , ok := groupSyncAttr .Attributes ()["regex_filter" ].(types.String )
356- if ! ok {
357- return fmt .Errorf ("oh jeez" )
358- }
359- regexFilter , err := regexp .Compile (regexFilterStr .ValueString ())
417+ // Perform the PATCH
418+ _ , err := r .Client .PatchGroupIDPSyncSettings (ctx , orgID .String (), groupSync )
360419 if err != nil {
361- return err
420+ diags .AddError ("Group Sync Update error" , err .Error ())
421+ return diags
362422 }
363- settings .RegexFilter = regexFilter
364423
365- legacyMappingMap , ok := groupSyncAttr .Attributes ()["legacy_group_name_mapping" ].(types.Map )
366- if ! ok {
367- return fmt .Errorf ("oh jeez" )
424+ return diags
425+ }
426+
427+ func (r * OrganizationResource ) patchRoleSync (
428+ ctx context.Context ,
429+ orgID uuid.UUID ,
430+ roleSyncObject types.Object ,
431+ ) diag.Diagnostics {
432+ var diags diag.Diagnostics
433+
434+ // Read values from Terraform
435+ var roleSyncData RoleSyncModel
436+ diags .Append (roleSyncObject .As (ctx , & roleSyncData , basetypes.ObjectAsOptions {})... )
437+ if diags .HasError () {
438+ return diags
368439 }
369- var legacyMapping map [string ]string
370- diags = legacyMappingMap .ElementsAs (ctx , legacyMapping , false )
440+
441+ // Convert that into the type used to send the PATCH to the backend
442+ var roleSync codersdk.RoleSyncSettings
443+ roleSync .Field = roleSyncData .Field .ValueString ()
444+ diags .Append (roleSyncData .Mapping .ElementsAs (ctx , & roleSync .Mapping , false )... )
371445 if diags .HasError () {
372- return fmt .Errorf ("oh jeez" )
446+ return diags
447+ }
448+
449+ // Perform the PATCH
450+ _ , err := r .Client .PatchRoleIDPSyncSettings (ctx , orgID .String (), roleSync )
451+ if err != nil {
452+ diags .AddError ("Role Sync Update error" , err .Error ())
453+ return diags
373454 }
374- settings .LegacyNameMapping = legacyMapping
375455
376- _ , err = r .Client .PatchGroupIDPSyncSettings (ctx , orgID .String (), settings )
377- return err
456+ return diags
378457}
0 commit comments