Skip to content

feat(coderd_user): Add service account support #356

@PushTheLimit

Description

@PushTheLimit

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions