@@ -69,6 +69,7 @@ type TemplateResourceModel struct {
69
69
TimeTilDormantAutoDeleteMillis types.Int64 `tfsdk:"time_til_dormant_autodelete_ms"`
70
70
RequireActiveVersion types.Bool `tfsdk:"require_active_version"`
71
71
DeprecationMessage types.String `tfsdk:"deprecation_message"`
72
+ MaxPortShareLevel types.String `tfsdk:"max_port_share_level"`
72
73
73
74
// If null, we are not managing ACL via Terraform (such as for AGPL).
74
75
ACL types.Object `tfsdk:"acl"`
@@ -92,7 +93,9 @@ func (m *TemplateResourceModel) EqualTemplateMetadata(other *TemplateResourceMod
92
93
m .FailureTTLMillis .Equal (other .FailureTTLMillis ) &&
93
94
m .TimeTilDormantMillis .Equal (other .TimeTilDormantMillis ) &&
94
95
m .TimeTilDormantAutoDeleteMillis .Equal (other .TimeTilDormantAutoDeleteMillis ) &&
95
- m .RequireActiveVersion .Equal (other .RequireActiveVersion )
96
+ m .RequireActiveVersion .Equal (other .RequireActiveVersion ) &&
97
+ m .DeprecationMessage .Equal (other .DeprecationMessage ) &&
98
+ m .MaxPortShareLevel .Equal (other .MaxPortShareLevel )
96
99
}
97
100
98
101
func (m * TemplateResourceModel ) CheckEntitlements (ctx context.Context , features map [codersdk.FeatureName ]codersdk.Feature ) (diags diag.Diagnostics ) {
@@ -110,7 +113,8 @@ func (m *TemplateResourceModel) CheckEntitlements(ctx context.Context, features
110
113
len (m .AutostartPermittedDaysOfWeek .Elements ()) != 7
111
114
requiresActiveVersion := m .RequireActiveVersion .ValueBool ()
112
115
requiresACL := ! m .ACL .IsNull ()
113
- if requiresScheduling || requiresActiveVersion || requiresACL {
116
+ requiresSharedPortsControl := m .MaxPortShareLevel .ValueString () != "" && m .MaxPortShareLevel .ValueString () != string (codersdk .WorkspaceAgentPortShareLevelPublic )
117
+ if requiresScheduling || requiresActiveVersion || requiresACL || requiresSharedPortsControl {
114
118
if requiresScheduling && ! features [codersdk .FeatureAdvancedTemplateScheduling ].Enabled {
115
119
diags .AddError (
116
120
"Feature not enabled" ,
@@ -132,6 +136,13 @@ func (m *TemplateResourceModel) CheckEntitlements(ctx context.Context, features
132
136
)
133
137
return
134
138
}
139
+ if requiresSharedPortsControl && ! features [codersdk .FeatureControlSharedPorts ].Enabled {
140
+ diags .AddError (
141
+ "Feature not enabled" ,
142
+ "Your license is not entitled to use port sharing control, so you cannot set max_port_share_level." ,
143
+ )
144
+ return
145
+ }
135
146
}
136
147
return
137
148
}
@@ -369,6 +380,14 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
369
380
Computed : true ,
370
381
Default : booldefault .StaticBool (false ),
371
382
},
383
+ "max_port_share_level" : schema.StringAttribute {
384
+ MarkdownDescription : "(Enterprise) The maximum port share level for workspaces created from this template. Defaults to `owner` on an Enterprise deployment, or `public` otherwise." ,
385
+ Optional : true ,
386
+ Computed : true ,
387
+ Validators : []validator.String {
388
+ stringvalidator .OneOfCaseInsensitive (string (codersdk .WorkspaceAgentPortShareLevelAuthenticated ), string (codersdk .WorkspaceAgentPortShareLevelOwner ), string (codersdk .WorkspaceAgentPortShareLevelPublic )),
389
+ },
390
+ },
372
391
"deprecation_message" : schema.StringAttribute {
373
392
MarkdownDescription : "If set, the template will be marked as deprecated with the provided message and users will be blocked from creating new workspaces from it. Does nothing if set when the resource is created." ,
374
393
Optional : true ,
@@ -553,6 +572,24 @@ func (r *TemplateResource) Create(ctx context.Context, req resource.CreateReques
553
572
data .ID = UUIDValue (templateResp .ID )
554
573
data .DisplayName = types .StringValue (templateResp .DisplayName )
555
574
575
+ // TODO: Remove this update call once this provider requires a Coder
576
+ // deployment running `v2.15.0` or later.
577
+ if data .MaxPortShareLevel .IsUnknown () {
578
+ data .MaxPortShareLevel = types .StringValue (string (templateResp .MaxPortShareLevel ))
579
+ } else {
580
+
581
+ mpslReq := data .toUpdateRequest (ctx , & resp .Diagnostics )
582
+ if resp .Diagnostics .HasError () {
583
+ return
584
+ }
585
+ mpslResp , err := client .UpdateTemplateMeta (ctx , data .ID .ValueUUID (), * mpslReq )
586
+ if err != nil {
587
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to set max port share level via update: %s" , err ))
588
+ return
589
+ }
590
+ data .MaxPortShareLevel = types .StringValue (string (mpslResp .MaxPortShareLevel ))
591
+ }
592
+
556
593
resp .Diagnostics .Append (data .Versions .setPrivateState (ctx , resp .Private )... )
557
594
if resp .Diagnostics .HasError () {
558
595
return
@@ -591,6 +628,7 @@ func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, r
591
628
resp .Diagnostics .Append (diag ... )
592
629
return
593
630
}
631
+ data .MaxPortShareLevel = types .StringValue (string (template .MaxPortShareLevel ))
594
632
595
633
if ! data .ACL .IsNull () {
596
634
tflog .Info (ctx , "reading template ACL" )
@@ -665,11 +703,16 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
665
703
666
704
client := r .data .Client
667
705
706
+ // TODO(ethanndickson): Remove this once the provider requires a Coder
707
+ // deployment running `v2.15.0` or later.
708
+ if newState .MaxPortShareLevel .IsUnknown () {
709
+ newState .MaxPortShareLevel = curState .MaxPortShareLevel
710
+ }
668
711
templateMetadataChanged := ! newState .EqualTemplateMetadata (& curState )
669
712
// This is required, as the API will reject no-diff updates.
670
713
if templateMetadataChanged {
671
714
tflog .Info (ctx , "change in template metadata detected, updating." )
672
- updateReq := newState .toUpdateRequest (ctx , resp )
715
+ updateReq := newState .toUpdateRequest (ctx , & resp . Diagnostics )
673
716
if resp .Diagnostics .HasError () {
674
717
return
675
718
}
@@ -758,6 +801,14 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
758
801
}
759
802
}
760
803
}
804
+ // TODO(ethanndickson): Remove this once the provider requires a Coder
805
+ // deployment running `v2.15.0` or later.
806
+ templateResp , err := client .Template (ctx , templateID )
807
+ if err != nil {
808
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to get template: %s" , err ))
809
+ return
810
+ }
811
+ newState .MaxPortShareLevel = types .StringValue (string (templateResp .MaxPortShareLevel ))
761
812
762
813
resp .Diagnostics .Append (newState .Versions .setPrivateState (ctx , resp .Private )... )
763
814
if resp .Diagnostics .HasError () {
@@ -1147,25 +1198,27 @@ func (r *TemplateResourceModel) readResponse(ctx context.Context, template *code
1147
1198
r .TimeTilDormantAutoDeleteMillis = types .Int64Value (template .TimeTilDormantAutoDeleteMillis )
1148
1199
r .RequireActiveVersion = types .BoolValue (template .RequireActiveVersion )
1149
1200
r .DeprecationMessage = types .StringValue (template .DeprecationMessage )
1201
+ // TODO(ethanndickson): MaxPortShareLevel deliberately omitted, as it can't
1202
+ // be set during a create request, and we call this during `Create`.
1150
1203
return nil
1151
1204
}
1152
1205
1153
- func (r * TemplateResourceModel ) toUpdateRequest (ctx context.Context , resp * resource. UpdateResponse ) * codersdk.UpdateTemplateMeta {
1206
+ func (r * TemplateResourceModel ) toUpdateRequest (ctx context.Context , diag * diag. Diagnostics ) * codersdk.UpdateTemplateMeta {
1154
1207
var days []string
1155
- resp . Diagnostics .Append (
1208
+ diag .Append (
1156
1209
r .AutostartPermittedDaysOfWeek .ElementsAs (ctx , & days , false )... ,
1157
1210
)
1158
- if resp . Diagnostics .HasError () {
1211
+ if diag .HasError () {
1159
1212
return nil
1160
1213
}
1161
1214
autoStart := & codersdk.TemplateAutostartRequirement {
1162
1215
DaysOfWeek : days ,
1163
1216
}
1164
1217
var reqs AutostopRequirement
1165
- resp . Diagnostics .Append (
1218
+ diag .Append (
1166
1219
r .AutostopRequirement .As (ctx , & reqs , basetypes.ObjectAsOptions {})... ,
1167
1220
)
1168
- if resp . Diagnostics .HasError () {
1221
+ if diag .HasError () {
1169
1222
return nil
1170
1223
}
1171
1224
autoStop := & codersdk.TemplateAutostopRequirement {
@@ -1189,6 +1242,7 @@ func (r *TemplateResourceModel) toUpdateRequest(ctx context.Context, resp *resou
1189
1242
TimeTilDormantAutoDeleteMillis : r .TimeTilDormantAutoDeleteMillis .ValueInt64 (),
1190
1243
RequireActiveVersion : r .RequireActiveVersion .ValueBool (),
1191
1244
DeprecationMessage : r .DeprecationMessage .ValueStringPointer (),
1245
+ MaxPortShareLevel : PtrTo (codersdk .WorkspaceAgentPortShareLevel (r .MaxPortShareLevel .ValueString ())),
1192
1246
// If we're managing ACL, we want to delete the everyone group
1193
1247
DisableEveryoneGroupAccess : ! r .ACL .IsNull (),
1194
1248
}
0 commit comments