gitlab_project_variable: a version 0 state with a two-part ID is upgraded to a quoted scope and drops out of state

Bug Report

When a gitlab_project_variable is still at schema version 0 in the state with a two-part <project>:<key> ID, the first plan with 17.9.0 or later wants to create it again although it still exists in GitLab, and the apply fails and leaves it out of the state.

The version 0 upgrader builds the new ID with fmt.Sprintf("%s:%s:%s", project, key, environmentScope), where environmentScope is a types.String, which %s prints in Go quotes, so 123:MY_KEY becomes 123:MY_KEY:"*" (L334-341). Read then filters on "*", quotes included, gets a 404 and removes the resource from the state (L168-194).

16.0.0 and earlier wrote version 0, and a two-part ID comes from an import by <project>:<key>. The SDK upgrader added in 16.0.1 joined the plain scope; the framework one that replaced it in 17.9.0 adds the quotes. A three-part ID is not affected.

Steps to reproduce

  1. With provider 15.11.0, import an existing variable by its two-part ID, terraform import gitlab_project_variable.example '28:MY_KEY', with the configuration below. The state holds it at schema version 0 with "id": "28:MY_KEY".
  2. Change the provider version to 19.5.0, then run terraform init -upgrade and terraform plan.

I reproduced it with Terraform 1.4.0 against GitLab CE 19.5.0.

Current behaviour

The refresh reads the variable as 28:MY_KEY:"*", finds nothing, and the plan wants to create it. terraform apply then fails with (MY_KEY) has already been taken, and the variable is no longer in the state.

Expected behaviour

The state is upgraded to the ID 28:MY_KEY:*, and the plan shows no changes.

Workaround

Once the variable is out of the state (after that apply, or with terraform state rm), import it again by its three-part ID: terraform import gitlab_project_variable.example '28:MY_KEY:*'. With 19.5.0 the plan then shows no changes.

Relevant Terraform Configuration

The main.tf I ran, with the token left out; step 1 used the same file with version = "15.11.0".

terraform {
  required_providers {
    gitlab = {
      source  = "gitlabhq/gitlab"
      version = "19.5.0"
    }
  }
}

provider "gitlab" {
  base_url = "http://127.0.0.1:18524/api/v4"
  token    = "<token>"
}

resource "gitlab_project_variable" "example" {
  project = "28"
  key     = "MY_KEY"
  value   = "my_value"
}

Relevant Terraform Command

terraform plan

Relevant Log Output

terraform plan and terraform apply with 19.5.0, on the state 15.11.0 wrote:
gitlab_project_variable.example: Refreshing state... [id=28:MY_KEY:"*"]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # gitlab_project_variable.example will be created
  + resource "gitlab_project_variable" "example" {
      + description       = (known after apply)
      + environment_scope = "*"
      + hidden            = (known after apply)
      + id                = (known after apply)
      + key               = "MY_KEY"
      + masked            = (known after apply)
      + project           = "28"
      + protected         = (known after apply)
      + raw               = (known after apply)
      + value             = "my_value"
      + variable_type     = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
gitlab_project_variable.example: Creating...

Error: GitLab API error occurred

  with gitlab_project_variable.example,
  on main.tf line 15, in resource "gitlab_project_variable" "example":
  15: resource "gitlab_project_variable" "example" {

Unable to create project variable: POST
http://127.0.0.1:18524/api/v4/projects/28/variables: 400 {message: {key:
[(MY_KEY) has already been taken]}}

Additional Details

  • GitLab Terraform Provider Version: 17.9.0 and later; reproduced with 19.5.0 and with main at dba01c37
  • GitLab Instance Version: 19.5.0 (CE)
  • Terraform Version: 1.4.0
  • License Tier: Free

Possible fix

Build the ID from data.EnvironmentScope.ValueString() in the upgrader. I have a merge request ready for it.

Edited by 🤖 GitLab Bot 🤖