gitlab_group_service_account_access_token cannot be imported

Bug Report

When importing a gitlab_group_service_account_access_token manually created, the terraform configuration wants to delete/re-create it.

Relevant Terraform Configuration

resource "gitlab_group" "mygroup" {
  name                              = "mygroup"
  path                              = "mygroup"
  visibility_level                  = "private"
}

resource "gitlab_group_service_account" "renovate_bot" {
  group    = gitlab_group.mygroup.id
  name     = "renovate-bot-001"
  username = "renovate-bot-001"
}

resource "gitlab_group_service_account_access_token" "renovate_bot" {
   expires_at = "2026-11-12"
   group      = gitlab_group.mygroup.id
   name       = "renovate-bot-001"
   user_id    = gitlab_group_service_account.renovate_bot.service_account_id

   scopes = ["api", "write_repository"]
}

Relevant Terraform Command

terraform import gitlab_group_service_account_access_token.renovate_bot xxx:yyy:zzz
terraform plan

  # gitlab_group_service_account_access_token.renovate_bot must be replaced
-/+ resource "gitlab_group_service_account_access_token" "renovate_bot" {
      + active     = (known after apply)
      + created_at = (known after apply)
      + expires_at = "2026-11-12" # forces replacement
      + group      = "xxx" # forces replacement
      ~ id         = "xxx:yyy:zzz" -> (known after apply)
      + name       = "renovate-bot-001" # forces replacement
      + revoked    = (known after apply)
      + scopes     = [ # forces replacement
          + "api",
          + "write_repository",
        ]
      + token      = (sensitive value)
      + user_id    = yyy # forces replacement
    }

Plan: 1 to add, 0 to change, 1 to destroy.

Relevant Log Output

{"@caller":"github.com/hashicorp/terraform-plugin-sdk/v2@v2.37.0/helper/logging/logging_http_transport.go:160","@level":"debug","@message":"Sending HTTP Request","@module":"gitlab.GitLab","@timestamp":"2025-11-13T09:10:38.128304+01:00","Accept":"application/json","Accept-Encoding":"gzip","Authorization":"Bearer ...","Host":"gitlab.com","User-Agent":"Terraform/1.13.5 (+https://www.terraform.io) Terraform-Plugin-Framework terraform-provider-gitlab/18.4.1","new_logger_warning":"This log was generated by a subsystem logger that wasn't created before being used. Use tflog.NewSubsystem to create this logger before it is used.","tf_http_op_type":"request","tf_http_req_body":"","tf_http_req_method":"GET","tf_http_req_uri":"/api/v4/personal_access_tokens/zzz","tf_http_req_version":"HTTP/1.1","tf_http_trans_id":"817a146d-6ef7-754c-9aa1-86dfafab5450","timestamp":"2025-11-13T09:10:38.128+0100"}
{"@caller":"github.com/hashicorp/terraform-plugin-sdk/v2@v2.37.0/helper/logging/logging_http_transport.go:160","@level":"debug","@message":"Received HTTP Response","@module":"gitlab.GitLab","@timestamp":"2025-11-13T09:10:38.743299+01:00","Cache-Control":"no-cache","Cf-Cache-Status":"MISS","Cf-Ray":"99dccb32380ce1ac-MRS","Content-Length":"30","Content-Security-Policy":"default-src 'none'","Content-Type":"application/json","Date":"Thu, 13 Nov 2025 08:10:38 GMT","Gitlab-Lb":"haproxy-main-16-lb-gprd","Gitlab-Sv":"api-gke-us-east1-b","Nel":"{\"max_age\": 0}","Ratelimit-Limit":"8000","Ratelimit-Name":"throttle_authenticated_api","Ratelimit-Observed":"19","Ratelimit-Remaining":"7981","Ratelimit-Reset":"1763021460","Referrer-Policy":"strict-origin-when-cross-origin","Server":"cloudflare","Set-Cookie":["..."],"Strict-Transport-Security":"max-age=31536000","Vary":"Origin, Accept-Encoding","X-Content-Type-Options":"nosniff","X-Frame-Options":"SAMEORIGIN","X-Gitlab-Meta":"{\"correlation_id\":\"99dccb32518ce1ac-ATL\",\"version\":\"1\"}","X-Request-Id":"99dccb32518ce1ac-ATL","X-Runtime":"0.416037","new_logger_warning":"This log was generated by a subsystem logger that wasn't created before being used. Use tflog.NewSubsystem to create this logger before it is used.","tf_http_op_type":"response","tf_http_res_body":"{\"message\":\"401 Unauthorized\"}","tf_http_res_status_code":401,"tf_http_res_status_reason":"401 Unauthorized","tf_http_res_version":"HTTP/2.0","tf_http_trans_id":"817a146d-6ef7-754c-9aa1-86dfafab5450","timestamp":"2025-11-13T09:10:38.742+0100"}
{"@caller":"gitlab.com/gitlab-org/terraform-provider-gitlab/internal/provider/resource_gitlab_group_service_account_access_token.go:537","@level":"warn","@message":"AccessToken read returned a 401, ignoring because service account access tokens can't be read without an admin (top-level group owner on gitlab.com) token currently. This will make the tfplan rely on state data instead of the current API values.","@module":"gitlab","@timestamp":"2025-11-13T09:10:38.743529+01:00","tf_mux_provider":"*proto6server.Server","tf_provider_addr":"registry.terraform.io/gitlabhq/gitlab","tf_req_id":"1c6b0487-f65d-3d14-afc4-abf79757df33","tf_resource_type":"gitlab_group_service_account_access_token","tf_rpc":"ReadResource","timestamp":"2025-11-13T09:10:38.743+0100","token_id":"zzz","user_id":"yyy"}

Additional Details

It seems like the terraform is using the wrong API. It looks like it uses /api/v4/personal_access_tokens/:user_id, while it should use /groups/:id/service_accounts/:user_id/personal_access_tokens. The used API will always return 401 when not used with the service account in question.

The relevant APIs are implemented under ListServiceAccountPersonalAccessTokensOptions, CreateServiceAccountPersonalAccessTokenOptions and RevokeServiceAccountPersonalAccessToken

One API may be missing to implement this properly: GET a single token by ID.

Implementation Guide

  • Read the CONTRIBUTING.md guide to set up your local development environment and clone the community fork of this project.
  • Amend the Read function in resource_gitlab_group_service_account_access_token.go to use r.client.Groups.ListServiceAccountPersonalAccessTokens and find the entry with the correct ID.
Edited by 🤖 GitLab Bot 🤖