@@ -56,7 +56,6 @@ func (r *UserResource) Metadata(ctx context.Context, req resource.MetadataReques
5656func (r * UserResource )Schema (ctx context.Context ,req resource.SchemaRequest ,resp * resource.SchemaResponse ) {
5757resp .Schema = schema.Schema {
5858MarkdownDescription :"A user on the Coder deployment." ,
59-
6059Attributes :map [string ]schema.Attribute {
6160"id" : schema.StringAttribute {
6261CustomType :UUIDType ,
@@ -66,27 +65,23 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
6665stringplanmodifier .UseStateForUnknown (),
6766},
6867},
69-
7068"username" : schema.StringAttribute {
7169MarkdownDescription :"Username of the user." ,
7270Required :true ,
7371},
7472"name" : schema.StringAttribute {
75- Computed :true ,
7673MarkdownDescription :"Display name of the user. Defaults to username." ,
77- Required :false ,
74+ Computed :true ,
7875Optional :true ,
79- // Defaulted in Create
8076},
8177"email" : schema.StringAttribute {
8278MarkdownDescription :"Email address of the user." ,
8379Required :true ,
8480},
8581"roles" : schema.SetAttribute {
8682MarkdownDescription :"Roles assigned to the user. Valid roles are 'owner', 'template-admin', 'user-admin', and 'auditor'." ,
87- Required :false ,
88- Optional :true ,
8983Computed :true ,
84+ Optional :true ,
9085ElementType :types .StringType ,
9186Validators : []validator.Set {
9287setvalidator .ValueStringsAre (
@@ -97,24 +92,24 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
9792},
9893"login_type" : schema.StringAttribute {
9994MarkdownDescription :"Type of login for the user. Valid types are 'none', 'password', 'github', and 'oidc'." ,
100- Required :false ,
101- Optional :true ,
10295Computed :true ,
96+ Optional :true ,
10397Validators : []validator.String {
10498stringvalidator .OneOf ("none" ,"password" ,"github" ,"oidc" ),
10599},
106100Default :stringdefault .StaticString ("none" ),
101+ PlanModifiers : []planmodifier.String {
102+ stringplanmodifier .RequiresReplaceIfConfigured (),
103+ },
107104},
108105"password" : schema.StringAttribute {
109106MarkdownDescription :"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 ,
111107Optional :true ,
112108Sensitive :true ,
113109},
114110"suspended" : schema.BoolAttribute {
115- Computed :true ,
116111MarkdownDescription :"Whether the user is suspended." ,
117- Required :false ,
112+ Computed :true ,
118113Optional :true ,
119114Default :booldefault .StaticBool (false ),
120115},
@@ -164,14 +159,15 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
164159}
165160
166161tflog .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 ()== "" {
162+ loginType := codersdk .LoginType (data .LoginType .ValueString ())
163+ if loginType == codersdk .LoginTypePassword && data .Password .IsNull () {
172164resp .Diagnostics .AddError ("Data Error" ,"Password is required when login_type is 'password'" )
173165return
174166}
167+ if loginType != codersdk .LoginTypePassword && ! data .Password .IsNull () {
168+ resp .Diagnostics .AddError ("Data Error" ,"Password is only allowed when login_type is 'password'" )
169+ return
170+ }
175171user ,err := client .CreateUser (ctx , codersdk.CreateUserRequest {
176172Email :data .Email .ValueString (),
177173Username :data .Username .ValueString (),
@@ -189,13 +185,13 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
189185data .ID = UUIDValue (user .ID )
190186
191187tflog .Trace (ctx ,"updating user profile" )
192- name := data .Username . ValueString ()
188+ name := data .Username
193189if data .Name .ValueString ()!= "" {
194- name = data .Name . ValueString ()
190+ name = data .Name
195191}
196192user ,err = client .UpdateUserProfile (ctx ,user .ID .String (), codersdk.UpdateUserProfileRequest {
197193Username :data .Username .ValueString (),
198- Name :name ,
194+ Name :name . ValueString () ,
199195})
200196if err != nil {
201197resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update newly created user profile, got error: %s" ,err ))
@@ -290,18 +286,23 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r
290286return
291287}
292288
289+ name := data .Username
290+ if data .Name .ValueString ()!= "" {
291+ name = data .Name
292+ }
293293tflog .Trace (ctx ,"updating user" ,map [string ]any {
294294"new_username" :data .Username .ValueString (),
295- "new_name" :data . Name .ValueString (),
295+ "new_name" :name .ValueString (),
296296})
297297_ ,err = client .UpdateUserProfile (ctx ,user .ID .String (), codersdk.UpdateUserProfileRequest {
298298Username :data .Username .ValueString (),
299- Name :data . Name .ValueString (),
299+ Name :name .ValueString (),
300300})
301301if err != nil {
302302resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update user profile, got error: %s" ,err ))
303303return
304304}
305+ data .Name = name
305306tflog .Trace (ctx ,"successfully updated user profile" )
306307
307308var roles []string
@@ -320,15 +321,17 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r
320321}
321322tflog .Trace (ctx ,"successfully updated user roles" )
322323
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
324+ if data .LoginType .ValueString ()== string (codersdk .LoginTypePassword )&& ! data .Password .IsNull () {
325+ tflog .Trace (ctx ,"updating password" )
326+ err = client .UpdateUserPassword (ctx ,user .ID .String (), codersdk.UpdateUserPasswordRequest {
327+ Password :data .Password .ValueString (),
328+ })
329+ if err != nil && ! strings .Contains (err .Error (),"New password cannot match old password." ) {
330+ resp .Diagnostics .AddError ("Client Error" ,fmt .Sprintf ("Unable to update password, got error: %s" ,err ))
331+ return
332+ }
333+ tflog .Trace (ctx ,"successfully updated password" )
330334}
331- tflog .Trace (ctx ,"successfully updated password" )
332335
333336var statusErr error
334337if data .Suspended .ValueBool () {