Skip to content

Environments secret values are not updated even if the plan detected some change #8

@lepichon

Description

@lepichon

Summary

When a value used in a semaphoreui_project_environment secret resource is changing (rotated through another terraform provider, in my case a gitlab access token), the terraform plan is seeing the semaphoreui_project_environment update for the specific secret entry but the entry value is not updated properly after apply.

Versions

Semaphore server : v2.16.34-87a1c53-1760097739
Semaphore provider : semaphoreui/semaphore v0.2.1
Terraform : v1.13.3

Terraform Tests

resource "gitlab_personal_access_token" "this" {
  for_each = var.init_users_to_configure

  user_id = local.gitlab_users[each.key].id
  name    = "${each.value.gitlab_user} personal access token"
  scopes  = ["api", "read_api"]

  rotation_configuration = {
    expiration_days    = "365"
    rotate_before_days = "15"
  }
  depends_on = [module.register_users]
}
...
resource "semaphoreui_project_environment" "this" {
  for_each = local.semaphore_project_repositories

  project_id = each.value.project_id
  name       = "${each.value.key}-repo${each.value.project_repository_id}-terraform-env-secrets"

  secrets = [
    {
      name  = "AWS_ACCESS_KEY_ID"
      value = var.aws_access_key_id
      type  = "env"
    },
    {
      name  = "AWS_SECRET_ACCESS_KEY"
      value = var.aws_secret_access_id
      type  = "env"
    },
    {
      name  = "GITLAB_TOKEN_ADMIN"
      value = var.gitlab_token
      type  = "env"
    },
    {
      name  = "GITLAB_TOKEN_USER"
      value = gitlab_personal_access_token.this[each.value.key].token
      type  = "env"
    },
    {
      name  = "SEMAPHORE_API_TOKEN"
      value = var.semaphore_api_token
      type  = "env"
    },
    {
      name  = "VAULT_TOKEN"
      value = var.vault_token
      type  = "env"
    },
    {
      name  = "VAULT_APPROLE_ROLE_ID"
      value = module.register_users.approles_role_id[format("%s-%s", each.value.key, each.value.environment)]
      type  = "env"
    },
    {
      name  = "VAULT_APPROLE_SECRET_ID"
      value = module.register_users.approles_secret_id[format("%s-%s", each.value.key, each.value.environment)]
      type  = "env"
    },
    {
      # sshkeys also added in env secrets because of bash/ansible only use it for initial checkout
      name  = "SSH_KEYS_GITLAB"
      value = module.register_users.user_gitlab_keys[each.value.key]
      type  = "env"
    },
    {
      name  = "SSH_KEYS_SERVER"
      value = module.register_users.user_server_keys[format("%s-%s", each.value.key, each.value.environment)]
      type  = "env"
    }
  ]
}
  • Terraform plan apply
08:53:36.218 STDOUT terraform: Terraform will perform the following actions:
08:53:36.218 STDOUT terraform:   # gitlab_personal_access_token.this["user-test"] will be updated in-place
08:53:36.218 STDOUT terraform:   ~ resource "gitlab_personal_access_token" "this" {
08:53:36.218 STDOUT terraform:       ~ active                        = true -> (known after apply)
08:53:36.218 STDOUT terraform:       ~ created_at                    = "2025-10-28 08:43:57.007 +0000 UTC" -> (known after apply)
08:53:36.218 STDOUT terraform:       ~ id                            = "330:324" -> (known after apply)
08:53:36.218 STDOUT terraform:         name                          = "user-test-gitops personal access token"
08:53:36.218 STDOUT terraform:       ~ revoked                       = false -> (known after apply)
08:53:36.218 STDOUT terraform:       ~ rotation_configuration        = {
08:53:36.218 STDOUT terraform:           ~ rotate_before_days = 15 -> 10
08:53:36.218 STDOUT terraform:             # (1 unchanged attribute hidden)
08:53:36.218 STDOUT terraform:         }
08:53:36.218 STDOUT terraform:       ~ token                         = (sensitive value)
08:53:36.218 STDOUT terraform:         # (5 unchanged attributes hidden)
08:53:36.218 STDOUT terraform:     }
08:53:36.218 STDOUT terraform:   # semaphoreui_project_environment.this["user-test-0"] will be updated in-place
08:53:36.218 STDOUT terraform:   ~ resource "semaphoreui_project_environment" "this" {
08:53:36.218 STDOUT terraform:         id         = 342
08:53:36.218 STDOUT terraform:         name       = "user-test-repo242-terraform-env-secrets"
08:53:36.218 STDOUT terraform:       ~ secrets    = [
08:53:36.218 STDOUT terraform:           ~ {
08:53:36.218 STDOUT terraform:                 id    = 1110
08:53:36.219 STDOUT terraform:                 name  = "GITLAB_TOKEN_USER"
08:53:36.219 STDOUT terraform:               ~ value = (sensitive value)
08:53:36.219 STDOUT terraform:                 # (1 unchanged attribute hidden)
08:53:36.219 STDOUT terraform:             },
08:53:36.219 STDOUT terraform:             # (9 unchanged elements hidden)
08:53:36.219 STDOUT terraform:         ]
08:53:36.219 STDOUT terraform:         # (1 unchanged attribute hidden)
08:53:36.219 STDOUT terraform:     }
...
08:53:36.219 STDOUT terraform: 
08:53:37.085 STDOUT terraform: gitlab_personal_access_token.this["user-test"]: Modifying... [id=330:324]
08:53:37.236 STDOUT terraform: gitlab_personal_access_token.this["user-test"]: Modifications complete after 0s [id=330:325]
08:53:37.433 STDOUT terraform: semaphoreui_project_environment.this["user-test-0"]: Modifying... [name=user-test-repo242-terraform-env-secrets]
08:53:37.616 STDOUT terraform: semaphoreui_project_environment.this["user-test-0"]: Modifications complete after 1s [name=user-test-repo242-terraform-env-secrets]

Expected Behavior

I was expecting the environment secret entry GITLAB_TOKEN_USER to be updated with the new gitlab token value but it's not.

The same secret update execute through the Semaphore API is working as expected

curl -X 'PUT' \
  'https://semaphoreui-mysite.com/api/project/12/environment/134' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": 342,
  "name": "user-repo242-terraform-env-secrets",
  "project_id": 12,
  "password": null,
  "json": "{}",
  "env": "{}",
  "secrets": [
    {
      "id": 1110,
      "name": "GITLAB_TOKEN_USER",
      "secret": "glpat-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "type": "env",
      "operation": "update"
    }
  ]
}'

The env secret entry GITLAB_TOKEN_USER is well updated

More tests on the provider api calls

I'm not fluent in go and decided to intercept the provider API calls (using mitmproxy) to check the sent payload during that terraform environment secret update.

It seems that no secret values are sent in the JSON payload when the provider is trying to update an environment secret value.

PUT REQUEST

PUT /api/project/12/environment/134 HTTP/1.1
Host: semaphoreui-mysite.com:443
User-Agent: Go-http-client/1.1
Content-Length: 649
Accept: application/json
Accept: text/plain; charset=utf-8
Authorization: Bearer XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx=
Content-Type: application/json

Accept-Encoding: gzip{"env":"{}","id":134,"json":"{}","name":"user-repo23-terraform-env-secrets","project_id":12,"secrets":[{"id":514,"name":"AWS_ACCESS_KEY_ID","type":"env"},{"id":515,"name":"AWS_SECRET_ACCESS_KEY","type":"env"},{"id":51
6,"name":"GITLAB_TOKEN_ADMIN","type":"env"},{"id":517,"name":"GITLAB_TOKEN_USER","operation":"update","type":"env"},{"id":518,"name":"SEMAPHORE_API_TOKEN","type":"env"},{"id":519,"name":"VAULT_TOKEN","type":"env"},{"i
d":520,"name":"VAULT_APPROLE_ROLE_ID","type":"env"},{"id":521,"name":"VAULT_APPROLE_SECRET_ID","type":"env"},{"id":522,"name":"SSH_KEYS_GITLAB","type":"env"},{"id":523,"name":"SSH_KEYS_SERVER","type":"env"}]} 

PUT RESPONSE

HTTP/1.1 204 No Content
Content-Type: application/json
Date: Fri, 24 Oct 2025 11:01:17 GMT 

So I'm wondering is it intentional ?

in any case this is very annoying for the life cycle management of these secrets also stored in Semaphore environment secrets

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions