@@ -66,27 +66,23 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
6666stringplanmodifier .UseStateForUnknown (),
6767},
6868},
69-
7069"username" : schema.StringAttribute {
7170MarkdownDescription :"Username of the user." ,
7271Required :true ,
7372},
7473"name" : schema.StringAttribute {
75- Computed :true ,
7674MarkdownDescription :"Display name of the user. Defaults to username." ,
77- Required :false ,
75+ Computed :true ,
7876Optional :true ,
79- // Defaulted in Create
8077},
8178"email" : schema.StringAttribute {
8279MarkdownDescription :"Email address of the user." ,
8380Required :true ,
8481},
8582"roles" : schema.SetAttribute {
8683MarkdownDescription :"Roles assigned to the user. Valid roles are 'owner', 'template-admin', 'user-admin', and 'auditor'." ,
87- Required :false ,
88- Optional :true ,
8984Computed :true ,
85+ Optional :true ,
9086ElementType :types .StringType ,
9187Validators : []validator.Set {
9288setvalidator .ValueStringsAre (
@@ -97,24 +93,24 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
9793},
9894"login_type" : schema.StringAttribute {
9995MarkdownDescription :"Type of login for the user. Valid types are 'none', 'password', 'github', and 'oidc'." ,
100- Required :false ,
101- Optional :true ,
10296Computed :true ,
97+ Optional :true ,
10398Validators : []validator.String {
10499stringvalidator .OneOf ("none" ,"password" ,"github" ,"oidc" ),
105100},
106101Default :stringdefault .StaticString ("none" ),
102+ PlanModifiers : []planmodifier.String {
103+ stringplanmodifier .RequiresReplaceIfConfigured (),
104+ },
107105},
108106"password" : schema.StringAttribute {
109107MarkdownDescription :"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 ,
111108Optional :true ,
112109Sensitive :true ,
113110},
114111"suspended" : schema.BoolAttribute {
115- Computed :true ,
116112MarkdownDescription :"Whether the user is suspended." ,
117- Required :false ,
113+ Computed :true ,
118114Optional :true ,
119115Default :booldefault .StaticBool (false ),
120116},
@@ -164,14 +160,15 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
164160}
165161
166162tflog .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 () {
172165resp .Diagnostics .AddError ("Data Error" ,"Password is required when login_type is 'password'" )
173166return
174167}
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+ }
175172user ,err := client .CreateUser (ctx , codersdk.CreateUserRequest {
176173Email :data .Email .ValueString (),
177174Username :data .Username .ValueString (),
@@ -189,13 +186,13 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
189186data .ID = UUIDValue (user .ID )
190187
191188tflog .Trace (ctx ,"updating user profile" )
192- name := data .Username . ValueString ()
189+ name := data .Username
193190if data .Name .ValueString ()!= "" {
194- name = data .Name . ValueString ()
191+ name = data .Name
195192}
196193user ,err = client .UpdateUserProfile (ctx ,user .ID .String (), codersdk.UpdateUserProfileRequest {
197194Username :data .Username .ValueString (),
198- Name :name ,
195+ Name :name . ValueString () ,
199196})
200197if err != nil {
201198resp .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
290287return
291288}
292289
290+ name := data .Username
291+ if data .Name .ValueString ()!= "" {
292+ name = data .Name
293+ }
293294tflog .Trace (ctx ,"updating user" ,map [string ]any {
294295"new_username" :data .Username .ValueString (),
295- "new_name" :data . Name .ValueString (),
296+ "new_name" :name .ValueString (),
296297})
297298_ ,err = client .UpdateUserProfile (ctx ,user .ID .String (), codersdk.UpdateUserProfileRequest {
298299Username :data .Username .ValueString (),
299- Name :data . Name .ValueString (),
300+ Name :name .ValueString (),
300301})
301302if err != nil {
302303resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update user profile, got error: %s" ,err ))
303304return
304305}
306+ data .Name = name
305307tflog .Trace (ctx ,"successfully updated user profile" )
306308
307309var roles []string
@@ -320,15 +322,17 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r
320322}
321323tflog .Trace (ctx ,"successfully updated user roles" )
322324
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" )
330335}
331- tflog .Trace (ctx ,"successfully updated password" )
332336
333337var statusErr error
334338if data .Suspended .ValueBool () {