@@ -56,7 +56,6 @@ func (r *UserResource) Metadata(ctx context.Context, req resource.MetadataReques
56
56
func (r * UserResource ) Schema (ctx context.Context , req resource.SchemaRequest , resp * resource.SchemaResponse ) {
57
57
resp .Schema = schema.Schema {
58
58
MarkdownDescription : "A user on the Coder deployment." ,
59
-
60
59
Attributes : map [string ]schema.Attribute {
61
60
"id" : schema.StringAttribute {
62
61
CustomType : UUIDType ,
@@ -66,27 +65,23 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
66
65
stringplanmodifier .UseStateForUnknown (),
67
66
},
68
67
},
69
-
70
68
"username" : schema.StringAttribute {
71
69
MarkdownDescription : "Username of the user." ,
72
70
Required : true ,
73
71
},
74
72
"name" : schema.StringAttribute {
75
- Computed : true ,
76
73
MarkdownDescription : "Display name of the user. Defaults to username." ,
77
- Required : false ,
74
+ Computed : true ,
78
75
Optional : true ,
79
- // Defaulted in Create
80
76
},
81
77
"email" : schema.StringAttribute {
82
78
MarkdownDescription : "Email address of the user." ,
83
79
Required : true ,
84
80
},
85
81
"roles" : schema.SetAttribute {
86
82
MarkdownDescription : "Roles assigned to the user. Valid roles are 'owner', 'template-admin', 'user-admin', and 'auditor'." ,
87
- Required : false ,
88
- Optional : true ,
89
83
Computed : true ,
84
+ Optional : true ,
90
85
ElementType : types .StringType ,
91
86
Validators : []validator.Set {
92
87
setvalidator .ValueStringsAre (
@@ -97,24 +92,24 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r
97
92
},
98
93
"login_type" : schema.StringAttribute {
99
94
MarkdownDescription : "Type of login for the user. Valid types are 'none', 'password', 'github', and 'oidc'." ,
100
- Required : false ,
101
- Optional : true ,
102
95
Computed : true ,
96
+ Optional : true ,
103
97
Validators : []validator.String {
104
98
stringvalidator .OneOf ("none" , "password" , "github" , "oidc" ),
105
99
},
106
100
Default : stringdefault .StaticString ("none" ),
101
+ PlanModifiers : []planmodifier.String {
102
+ stringplanmodifier .RequiresReplaceIfConfigured (),
103
+ },
107
104
},
108
105
"password" : schema.StringAttribute {
109
106
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
107
Optional : true ,
112
108
Sensitive : true ,
113
109
},
114
110
"suspended" : schema.BoolAttribute {
115
- Computed : true ,
116
111
MarkdownDescription : "Whether the user is suspended." ,
117
- Required : false ,
112
+ Computed : true ,
118
113
Optional : true ,
119
114
Default : booldefault .StaticBool (false ),
120
115
},
@@ -164,14 +159,15 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
164
159
}
165
160
166
161
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 () == "" {
162
+ loginType := codersdk .LoginType (data .LoginType .ValueString ())
163
+ if loginType == codersdk .LoginTypePassword && data .Password .IsNull () {
172
164
resp .Diagnostics .AddError ("Data Error" , "Password is required when login_type is 'password'" )
173
165
return
174
166
}
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
+ }
175
171
user , err := client .CreateUser (ctx , codersdk.CreateUserRequest {
176
172
Email : data .Email .ValueString (),
177
173
Username : data .Username .ValueString (),
@@ -189,13 +185,13 @@ func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, r
189
185
data .ID = UUIDValue (user .ID )
190
186
191
187
tflog .Trace (ctx , "updating user profile" )
192
- name := data .Username . ValueString ()
188
+ name := data .Username
193
189
if data .Name .ValueString () != "" {
194
- name = data .Name . ValueString ()
190
+ name = data .Name
195
191
}
196
192
user , err = client .UpdateUserProfile (ctx , user .ID .String (), codersdk.UpdateUserProfileRequest {
197
193
Username : data .Username .ValueString (),
198
- Name : name ,
194
+ Name : name . ValueString () ,
199
195
})
200
196
if err != nil {
201
197
resp .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
290
286
return
291
287
}
292
288
289
+ name := data .Username
290
+ if data .Name .ValueString () != "" {
291
+ name = data .Name
292
+ }
293
293
tflog .Trace (ctx , "updating user" , map [string ]any {
294
294
"new_username" : data .Username .ValueString (),
295
- "new_name" : data . Name .ValueString (),
295
+ "new_name" : name .ValueString (),
296
296
})
297
297
_ , err = client .UpdateUserProfile (ctx , user .ID .String (), codersdk.UpdateUserProfileRequest {
298
298
Username : data .Username .ValueString (),
299
- Name : data . Name .ValueString (),
299
+ Name : name .ValueString (),
300
300
})
301
301
if err != nil {
302
302
resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Unable to update user profile, got error: %s" , err ))
303
303
return
304
304
}
305
+ data .Name = name
305
306
tflog .Trace (ctx , "successfully updated user profile" )
306
307
307
308
var roles []string
@@ -320,15 +321,17 @@ func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, r
320
321
}
321
322
tflog .Trace (ctx , "successfully updated user roles" )
322
323
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" )
330
334
}
331
- tflog .Trace (ctx , "successfully updated password" )
332
335
333
336
var statusErr error
334
337
if data .Suspended .ValueBool () {
0 commit comments