@@ -66,27 +66,23 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
66
66
stringplanmodifier .UseStateForUnknown (),
67
67
},
68
68
},
69
-
70
69
"username" : schema.StringAttribute {
71
70
MarkdownDescription :"Username of the user." ,
72
71
Required :true ,
73
72
},
74
73
"name" : schema.StringAttribute {
75
- Computed :true ,
76
74
MarkdownDescription :"Display name of the user. Defaults to username." ,
77
- Required :false ,
75
+ Computed :true ,
78
76
Optional :true ,
79
- // Defaulted in Create
80
77
},
81
78
"email" : schema.StringAttribute {
82
79
MarkdownDescription :"Email address of the user." ,
83
80
Required :true ,
84
81
},
85
82
"roles" : schema.SetAttribute {
86
83
MarkdownDescription :"Roles assigned to the user. Valid roles are 'owner', 'template-admin', 'user-admin', and 'auditor'." ,
87
- Required :false ,
88
- Optional :true ,
89
84
Computed :true ,
85
+ Optional :true ,
90
86
ElementType :types .StringType ,
91
87
Validators : []validator.Set {
92
88
setvalidator .ValueStringsAre (
@@ -97,24 +93,24 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
97
93
},
98
94
"login_type" : schema.StringAttribute {
99
95
MarkdownDescription :"Type of login for the user. Valid types are 'none', 'password', 'github', and 'oidc'." ,
100
- Required :false ,
101
- Optional :true ,
102
96
Computed :true ,
97
+ Optional :true ,
103
98
Validators : []validator.String {
104
99
stringvalidator .OneOf ("none" ,"password" ,"github" ,"oidc" ),
105
100
},
106
101
Default :stringdefault .StaticString ("none" ),
102
+ PlanModifiers : []planmodifier.String {
103
+ stringplanmodifier .RequiresReplaceIfConfigured (),
104
+ },
107
105
},
108
106
"password" : schema.StringAttribute {
109
107
MarkdownDescription :"Password for the user. Required when login_type is 'password'. Passwords are saved into the state as plain text and should only be used for testing purposes." ,
110
- Required :false ,
111
108
Optional :true ,
112
109
Sensitive :true ,
113
110
},
114
111
"suspended" : schema.BoolAttribute {
115
- Computed :true ,
116
112
MarkdownDescription :"Whether the user is suspended." ,
117
- Required :false ,
113
+ Computed :true ,
118
114
Optional :true ,
119
115
Default :booldefault .StaticBool (false ),
120
116
},
@@ -164,14 +160,15 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
164
160
}
165
161
166
162
tflog .Trace (ctx ,"creating user" )
167
- loginType := codersdk .LoginTypeNone
168
- if data .LoginType .ValueString ()!= "" {
169
- loginType = codersdk .LoginType (data .LoginType .ValueString ())
170
- }
171
- if loginType == codersdk .LoginTypePassword && data .Password .ValueString ()== "" {
163
+ loginType := codersdk .LoginType (data .LoginType .ValueString ())
164
+ if loginType == codersdk .LoginTypePassword && data .Password .IsNull () {
172
165
resp .Diagnostics .AddError ("Data Error" ,"Password is required when login_type is 'password'" )
173
166
return
174
167
}
168
+ if loginType != codersdk .LoginTypePassword && ! data .Password .IsNull () {
169
+ resp .Diagnostics .AddError ("Data Error" ,"Password is only allowed when login_type is 'password'" )
170
+ return
171
+ }
175
172
user ,err := client .CreateUser (ctx , codersdk.CreateUserRequest {
176
173
Email :data .Email .ValueString (),
177
174
Username :data .Username .ValueString (),
@@ -189,13 +186,13 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
189
186
data .ID = UUIDValue (user .ID )
190
187
191
188
tflog .Trace (ctx ,"updating user profile" )
192
- name := data .Username . ValueString ()
189
+ name := data .Username
193
190
if data .Name .ValueString ()!= "" {
194
- name = data .Name . ValueString ()
191
+ name = data .Name
195
192
}
196
193
user ,err = client .UpdateUserProfile (ctx ,user .ID .String (), codersdk.UpdateUserProfileRequest {
197
194
Username :data .Username .ValueString (),
198
- Name :name ,
195
+ Name :name . ValueString () ,
199
196
})
200
197
if err != nil {
201
198
resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update newly created user profile, got error: %s" ,err ))
@@ -290,18 +287,23 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r
290
287
return
291
288
}
292
289
290
+ name := data .Username
291
+ if data .Name .ValueString ()!= "" {
292
+ name = data .Name
293
+ }
293
294
tflog .Trace (ctx ,"updating user" ,map [string ]any {
294
295
"new_username" :data .Username .ValueString (),
295
- "new_name" :data . Name .ValueString (),
296
+ "new_name" :name .ValueString (),
296
297
})
297
298
_ ,err = client .UpdateUserProfile (ctx ,user .ID .String (), codersdk.UpdateUserProfileRequest {
298
299
Username :data .Username .ValueString (),
299
- Name :data . Name .ValueString (),
300
+ Name :name .ValueString (),
300
301
})
301
302
if err != nil {
302
303
resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update user profile, got error: %s" ,err ))
303
304
return
304
305
}
306
+ data .Name = name
305
307
tflog .Trace (ctx ,"successfully updated user profile" )
306
308
307
309
var roles []string
@@ -320,15 +322,17 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r
320
322
}
321
323
tflog .Trace (ctx ,"successfully updated user roles" )
322
324
323
- tflog .Trace (ctx ,"updating password" )
324
- err = client .UpdateUserPassword (ctx ,user .ID .String (), codersdk.UpdateUserPasswordRequest {
325
- Password :data .Password .ValueString (),
326
- })
327
- if err != nil && ! strings .Contains (err .Error (),"New password cannot match old password." ) {
328
- resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update password, got error: %s" ,err ))
329
- return
325
+ if data .LoginType .ValueString ()== string (codersdk .LoginTypePassword )&& ! data .Password .IsNull () {
326
+ tflog .Trace (ctx ,"updating password" )
327
+ err = client .UpdateUserPassword (ctx ,user .ID .String (), codersdk.UpdateUserPasswordRequest {
328
+ Password :data .Password .ValueString (),
329
+ })
330
+ if err != nil && ! strings .Contains (err .Error (),"New password cannot match old password." ) {
331
+ resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update password, got error: %s" ,err ))
332
+ return
333
+ }
334
+ tflog .Trace (ctx ,"successfully updated password" )
330
335
}
331
- tflog .Trace (ctx ,"successfully updated password" )
332
336
333
337
var statusErr error
334
338
if data .Suspended .ValueBool () {