@@ -12,6 +12,7 @@ import (
12
12
"github.com/google/uuid"
13
13
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
14
14
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
15
+ "github.com/hashicorp/terraform-plugin-framework/attr"
15
16
"github.com/hashicorp/terraform-plugin-framework/path"
16
17
"github.com/hashicorp/terraform-plugin-framework/resource"
17
18
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -22,6 +23,7 @@ import (
22
23
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
23
24
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
24
25
"github.com/hashicorp/terraform-plugin-framework/types"
26
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
25
27
"github.com/hashicorp/terraform-plugin-log/tflog"
26
28
)
27
29
@@ -51,20 +53,19 @@ type TemplateResourceModel struct {
51
53
AllowUserAutoStart types.Bool `tfsdk:"allow_user_auto_start"`
52
54
AllowUserAutoStop types.Bool `tfsdk:"allow_user_auto_stop"`
53
55
54
- ACL * ACL `tfsdk:"acl"`
55
- Versions Versions `tfsdk:"versions"`
56
+ ACL types. Object `tfsdk:"acl"`
57
+ Versions Versions `tfsdk:"versions"`
56
58
}
57
59
58
- // EqualTemplateMetadata returns true if two templates have identical metadata & ACL.
60
+ // EqualTemplateMetadata returns true if two templates have identical metadata (excluding ACL)
59
61
func (m TemplateResourceModel ) EqualTemplateMetadata (other TemplateResourceModel ) bool {
60
62
return m .Name .Equal (other .Name ) &&
61
63
m .DisplayName .Equal (other .DisplayName ) &&
62
64
m .Description .Equal (other .Description ) &&
63
65
m .OrganizationID .Equal (other .OrganizationID ) &&
64
66
m .Icon .Equal (other .Icon ) &&
65
67
m .AllowUserAutoStart .Equal (other .AllowUserAutoStart ) &&
66
- m .AllowUserAutoStop .Equal (other .AllowUserAutoStop ) &&
67
- m .ACL .Equal (other .ACL )
68
+ m .AllowUserAutoStop .Equal (other .AllowUserAutoStop )
68
69
}
69
70
70
71
type TemplateVersion struct {
@@ -110,38 +111,10 @@ type ACL struct {
110
111
GroupPermissions []Permission `tfsdk:"groups"`
111
112
}
112
113
113
- func (a * ACL ) Equal (other * ACL ) bool {
114
- if len (a .UserPermissions ) != len (other .UserPermissions ) {
115
- return false
116
- }
117
- if len (a .GroupPermissions ) != len (other .GroupPermissions ) {
118
- return false
119
- }
120
- for _ , e1 := range a .UserPermissions {
121
- found := false
122
- for _ , e2 := range other .UserPermissions {
123
- if e1 .Equal (& e2 ) {
124
- found = true
125
- break
126
- }
127
- }
128
- if ! found {
129
- return false
130
- }
131
- }
132
- for _ , e1 := range a .GroupPermissions {
133
- found := false
134
- for _ , e2 := range other .GroupPermissions {
135
- if e1 .Equal (& e2 ) {
136
- found = true
137
- break
138
- }
139
- }
140
- if ! found {
141
- return false
142
- }
143
- }
144
- return true
114
+ // aclTypeAttr is the type schema for an instance of `ACL`.
115
+ var aclTypeAttr = map [string ]attr.Type {
116
+ "users" : permissionTypeAttr ,
117
+ "groups" : permissionTypeAttr ,
145
118
}
146
119
147
120
type Permission struct {
@@ -151,12 +124,8 @@ type Permission struct {
151
124
Role types.String `tfsdk:"role"`
152
125
}
153
126
154
- func (p * Permission ) Equal (other * Permission ) bool {
155
- return p .ID .Equal (other .ID ) && p .Role .Equal (other .Role )
156
- }
157
-
158
- // permissionsAttribute is the attribute schema for an instance of `[]Permission`.
159
- var permissionsAttribute = schema.SetNestedAttribute {
127
+ // permissionAttribute is the attribute schema for an instance of `[]Permission`.
128
+ var permissionAttribute = schema.SetNestedAttribute {
160
129
Required : true ,
161
130
NestedObject : schema.NestedAttributeObject {
162
131
Attributes : map [string ]schema.Attribute {
@@ -165,14 +134,19 @@ var permissionsAttribute = schema.SetNestedAttribute{
165
134
},
166
135
"role" : schema.StringAttribute {
167
136
Required : true ,
168
- Validators : []validator.String {
169
- stringvalidator .OneOf ("admin" , "use" , "" ),
170
- },
171
137
},
172
138
},
173
139
},
174
140
}
175
141
142
+ // permissionTypeAttr is the type schema for an instance of `[]Permission`.
143
+ var permissionTypeAttr = basetypes.SetType {ElemType : types.ObjectType {
144
+ AttrTypes : map [string ]attr.Type {
145
+ "id" : basetypes.StringType {},
146
+ "role" : basetypes.StringType {},
147
+ },
148
+ }}
149
+
176
150
func (r * TemplateResource ) Metadata (ctx context.Context , req resource.MetadataRequest , resp * resource.MetadataResponse ) {
177
151
resp .TypeName = req .ProviderTypeName + "_template"
178
152
}
@@ -234,11 +208,11 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
234
208
Default : booldefault .StaticBool (true ),
235
209
},
236
210
"acl" : schema.SingleNestedAttribute {
237
- MarkdownDescription : "Access control list for the template." ,
238
- Required : true ,
211
+ MarkdownDescription : "Access control list for the template. Requires an enterprise Coder deployment. If null, ACL policies will not be added or removed by Terraform. " ,
212
+ Optional : true ,
239
213
Attributes : map [string ]schema.Attribute {
240
- "users" : permissionsAttribute ,
241
- "groups" : permissionsAttribute ,
214
+ "users" : permissionAttribute ,
215
+ "groups" : permissionAttribute ,
242
216
},
243
217
},
244
218
"versions" : schema.ListNestedAttribute {
@@ -371,13 +345,22 @@ func (r *TemplateResource) Create(ctx context.Context, req resource.CreateReques
371
345
"id" : templateResp .ID ,
372
346
})
373
347
374
- tflog .Trace (ctx , "updating template ACL" )
375
- err = client .UpdateTemplateACL (ctx , templateResp .ID , convertACLToRequest (data .ACL ))
376
- if err != nil {
377
- resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to update template ACL: %s" , err ))
378
- return
348
+ if ! data .ACL .IsNull () {
349
+ tflog .Trace (ctx , "updating template ACL" )
350
+ var acl ACL
351
+ resp .Diagnostics .Append (
352
+ data .ACL .As (ctx , & acl , basetypes.ObjectAsOptions {})... ,
353
+ )
354
+ if resp .Diagnostics .HasError () {
355
+ return
356
+ }
357
+ err = client .UpdateTemplateACL (ctx , templateResp .ID , convertACLToRequest (acl ))
358
+ if err != nil {
359
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to create template ACL: %s" , err ))
360
+ return
361
+ }
362
+ tflog .Trace (ctx , "successfully updated template ACL" )
379
363
}
380
- tflog .Trace (ctx , "successfully updated template ACL" )
381
364
}
382
365
if version .Active .ValueBool () {
383
366
tflog .Trace (ctx , "marking template version as active" , map [string ]any {
@@ -430,12 +413,22 @@ func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, r
430
413
data .AllowUserAutoStart = types .BoolValue (template .AllowUserAutostart )
431
414
data .AllowUserAutoStop = types .BoolValue (template .AllowUserAutostop )
432
415
433
- acl , err := client .TemplateACL (ctx , templateID )
434
- if err != nil {
435
- resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to get template ACL: %s" , err ))
436
- return
416
+ if ! data .ACL .IsNull () {
417
+ tflog .Trace (ctx , "reading template ACL" )
418
+ acl , err := client .TemplateACL (ctx , templateID )
419
+ if err != nil {
420
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to get template ACL: %s" , err ))
421
+ return
422
+ }
423
+ TFAcl := convertResponseToACL (acl )
424
+ aclObj , diag := types .ObjectValueFrom (ctx , aclTypeAttr , TFAcl )
425
+ diag .Append (diag ... )
426
+ if diag .HasError () {
427
+ return
428
+ }
429
+ data .ACL = aclObj
430
+ tflog .Trace (ctx , "read template ACL" )
437
431
}
438
- data .ACL = convertResponseToACL (acl )
439
432
440
433
for idx , version := range data .Versions {
441
434
versionID := version .ID .ValueUUID ()
@@ -500,11 +493,20 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
500
493
DisableEveryoneGroupAccess : true ,
501
494
})
502
495
if err != nil {
503
- resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to update template: %s" , err ))
496
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to update template metadata : %s" , err ))
504
497
return
505
498
}
506
499
tflog .Trace (ctx , "successfully updated template metadata" )
507
- err = client .UpdateTemplateACL (ctx , templateID , convertACLToRequest (planState .ACL ))
500
+ }
501
+
502
+ // If there's a change, and we're still managing ACL
503
+ if ! planState .ACL .Equal (curState .ACL ) && ! planState .ACL .IsNull () {
504
+ var acl ACL
505
+ resp .Diagnostics .Append (planState .ACL .As (ctx , & acl , basetypes.ObjectAsOptions {})... )
506
+ if resp .Diagnostics .HasError () {
507
+ return
508
+ }
509
+ err := client .UpdateTemplateACL (ctx , templateID , convertACLToRequest (acl ))
508
510
if err != nil {
509
511
resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to update template ACL: %s" , err ))
510
512
return
@@ -784,10 +786,7 @@ func newVersion(ctx context.Context, client *codersdk.Client, req newVersionRequ
784
786
return & versionResp , nil
785
787
}
786
788
787
- func convertACLToRequest (permissions * ACL ) codersdk.UpdateTemplateACL {
788
- if permissions == nil {
789
- return codersdk.UpdateTemplateACL {}
790
- }
789
+ func convertACLToRequest (permissions ACL ) codersdk.UpdateTemplateACL {
791
790
var userPerms = make (map [string ]codersdk.TemplateRole )
792
791
for _ , perm := range permissions .UserPermissions {
793
792
userPerms [perm .ID .ValueString ()] = codersdk .TemplateRole (perm .Role .ValueString ())
@@ -802,7 +801,7 @@ func convertACLToRequest(permissions *ACL) codersdk.UpdateTemplateACL {
802
801
}
803
802
}
804
803
805
- func convertResponseToACL (acl codersdk.TemplateACL ) * ACL {
804
+ func convertResponseToACL (acl codersdk.TemplateACL ) ACL {
806
805
userPerms := make ([]Permission , 0 , len (acl .Users ))
807
806
for _ , user := range acl .Users {
808
807
userPerms = append (userPerms , Permission {
@@ -817,7 +816,7 @@ func convertResponseToACL(acl codersdk.TemplateACL) *ACL {
817
816
Role : types .StringValue (string (group .Role )),
818
817
})
819
818
}
820
- return & ACL {
819
+ return ACL {
821
820
UserPermissions : userPerms ,
822
821
GroupPermissions : groupPerms ,
823
822
}
0 commit comments