@@ -41,13 +41,14 @@ type UserResource struct {
41
41
type UserResourceModel struct {
42
42
ID types.String `tfsdk:"id"`
43
43
44
- Username types.String `tfsdk:"username"`
45
- Name types.String `tfsdk:"name"`
46
- Email types.String `tfsdk:"email"`
47
- Roles types.Set `tfsdk:"roles"` // owner, template-admin, user-admin, auditor (member is implicit)
48
- LoginType types.String `tfsdk:"login_type"` // none, password, github, oidc
49
- Password types.String `tfsdk:"password"` // only when login_type is password
50
- Suspended types.Bool `tfsdk:"suspended"`
44
+ Username types.String `tfsdk:"username"`
45
+ Name types.String `tfsdk:"name"`
46
+ Email types.String `tfsdk:"email"`
47
+ Roles types.Set `tfsdk:"roles"` // owner, template-admin, user-admin, auditor (member is implicit)
48
+ LoginType types.String `tfsdk:"login_type"` // none, password, github, oidc
49
+ Password types.String `tfsdk:"password"` // only when login_type is password
50
+ Suspended types.Bool `tfsdk:"suspended"`
51
+ CascadeDelete types.Bool `tfsdk:"cascade_delete"`
51
52
}
52
53
53
54
func (r * UserResource )Metadata (ctx context.Context ,req resource.MetadataRequest ,resp * resource.MetadataResponse ) {
@@ -79,7 +80,7 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
79
80
// Defaulted in Create
80
81
},
81
82
"email" : schema.StringAttribute {
82
- MarkdownDescription :"Email address of the user." ,
83
+ MarkdownDescription :"Email address of the user. Modifying this field will trigger a resource replacement. " ,
83
84
Required :true ,
84
85
PlanModifiers : []planmodifier.String {
85
86
stringplanmodifier .RequiresReplace (),
@@ -121,6 +122,13 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
121
122
Optional :true ,
122
123
Default :booldefault .StaticBool (false ),
123
124
},
125
+ "cascade_delete" : schema.BoolAttribute {
126
+ Computed :true ,
127
+ MarkdownDescription :"Whether to delete owned workspaces when this resource is deleted or replaced." ,
128
+ Required :false ,
129
+ Optional :true ,
130
+ Default :booldefault .StaticBool (false ),
131
+ },
124
132
},
125
133
}
126
134
}
@@ -366,6 +374,29 @@ func (r *UserResource) Delete(ctx context.Context, req resource.DeleteRequest, r
366
374
resp .Diagnostics .AddError ("Data Error" ,fmt .Sprintf ("Unable to parse user ID, got error: %s" ,err ))
367
375
return
368
376
}
377
+
378
+ if data .CascadeDelete .ValueBool () {
379
+ tflog .Trace (ctx ,"deleting user workspaces" )
380
+ workspaces ,err := client .Workspaces (ctx , codersdk.WorkspaceFilter {
381
+ Owner :data .Username .ValueString (),
382
+ })
383
+ if err != nil {
384
+ resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to get user workspaces, got error: %s" ,err ))
385
+ return
386
+ }
387
+ for _ ,workspace := range workspaces .Workspaces {
388
+ _ ,err := client .CreateWorkspaceBuild (ctx ,workspace .ID , codersdk.CreateWorkspaceBuildRequest {
389
+ Transition :codersdk .WorkspaceTransitionDelete ,
390
+ })
391
+ if err != nil {
392
+ resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to delete user workspace, got error: %s" ,err ))
393
+ return
394
+ }
395
+ }
396
+ //TODO: Wait for builds to finish
397
+ tflog .Trace (ctx ,"successfully deleted user workspaces" )
398
+ }
399
+
369
400
tflog .Trace (ctx ,"deleting user" )
370
401
err = client .DeleteUser (ctx ,id )
371
402
if err != nil {