The gap
The Coder API and CLI support creating service accounts, but the coderd Terraform provider does not. coderd_user exposes login_type (none | password | github | oidc) but has no way to set the account as a service account, so it can't reach a capability the API already offers:
-
API: CreateUserRequestWithOrgs.ServiceAccount (json:"service_account")
-
CLI: coder users create --service-account
-
Server: the users.is_service_account column, the auto-assigned organization-service-account role, the service_account:true user filter, and the is_service_account field on user responses
The closest the provider can do today is login_type = "none", which produces a login-disabled regular user — not a service account. The two are not equivalent:
-
A login_type = "none" user (is_service_account = false) counts toward the licensed user limit, because active-user counting excludes service accounts but not login-disabled regular users. A real service account does not consume a seat.
-
It receives the standard organization-member role instead of organization-service-account.
-
It is not discoverable via the service_account:true filter or the is_service_account response field.
-
It requires a non-empty email, whereas service accounts are the only users permitted an empty email.
As a result, there is no way to manage service accounts as code — they must be created out-of-band via the CLI/UI, which is the exact operational drift this provider is meant to close.
Proposed change
-
Add an is_service_account (bool, optional, defaults false) attribute to the coderd_user resource. The attribute name matches the API's is_service_account field (json:"is_service_account"), consistent with how is_default is named on the coderd_organization data source. On create it maps to the service_account field of CreateUserRequestWithOrgs (the create request uses the unprefixed name); this attribute-name-to-request-field mapping is the same approach already used for suspended.
-
Mark it ForceNew: the service-account status is immutable server-side (set at creation and enforced by DB constraints), so changing it must recreate the user.
-
Validate at plan time to mirror the server's rules: when is_service_account = true, default/require login_type = none and reject a non-empty email or password.
-
Expose the same is_service_account attribute (read-only) on the coderd_user data source, populated from the response's is_service_account field — using one attribute name across both resource and data source, as the existing bool attributes do.
-
Add an acceptance test and regenerate docs.
This closes the parity gap for service accounts without changing existing coderd_user behavior (the attribute is optional and defaults off).
We're happy to open the PR for this.
The gap
The Coder API and CLI support creating service accounts, but the
coderdTerraform provider does not.coderd_userexposeslogin_type(none | password | github | oidc) but has no way to set the account as a service account, so it can't reach a capability the API already offers:API:
CreateUserRequestWithOrgs.ServiceAccount(json:"service_account")CLI:
coder users create --service-accountServer: the
users.is_service_accountcolumn, the auto-assignedorganization-service-accountrole, theservice_account:trueuser filter, and theis_service_accountfield on user responsesThe closest the provider can do today is
login_type = "none", which produces a login-disabled regular user — not a service account. The two are not equivalent:A
login_type = "none"user (is_service_account = false) counts toward the licensed user limit, because active-user counting excludes service accounts but not login-disabled regular users. A real service account does not consume a seat.It receives the standard
organization-memberrole instead oforganization-service-account.It is not discoverable via the
service_account:truefilter or theis_service_accountresponse field.It requires a non-empty email, whereas service accounts are the only users permitted an empty email.
As a result, there is no way to manage service accounts as code — they must be created out-of-band via the CLI/UI, which is the exact operational drift this provider is meant to close.
Proposed change
Add an
is_service_account(bool, optional, defaultsfalse) attribute to thecoderd_userresource. The attribute name matches the API'sis_service_accountfield (json:"is_service_account"), consistent with howis_defaultis named on thecoderd_organizationdata source. On create it maps to theservice_accountfield ofCreateUserRequestWithOrgs(the create request uses the unprefixed name); this attribute-name-to-request-field mapping is the same approach already used forsuspended.Mark it
ForceNew: the service-account status is immutable server-side (set at creation and enforced by DB constraints), so changing it must recreate the user.Validate at plan time to mirror the server's rules: when
is_service_account = true, default/requirelogin_type = noneand reject a non-emptyemailorpassword.Expose the same
is_service_accountattribute (read-only) on thecoderd_userdata source, populated from the response'sis_service_accountfield — using one attribute name across both resource and data source, as the existing bool attributes do.Add an acceptance test and regenerate docs.
This closes the parity gap for service accounts without changing existing
coderd_userbehavior (the attribute is optional and defaults off).We're happy to open the PR for this.