terraform-provider-gitlab
terraform-provider-gitlab copied to clipboard
Perpetual diff when using multiple deploy_access_levels in gitlab_project_protected_environment resource
GitLab Provider version
3.15.1
GitLab version
gitlab.com
Terraform version
1.2.1
Relevant Terraform Configuration
resource "gitlab_project_protected_environment" "staging" {
project = gitlab_project.this.id
environment = "staging"
deploy_access_levels {
access_level = "maintainer"
}
deploy_access_levels {
group_id = var.sre_group_id
}
}
Description
When using a single deploy_access_group things work as expected. When using multiple deploy_access_levels however, there is a perpetual diff leading to the resource being recreate on every terraform apply.
-/+ resource "gitlab_project_protected_environment" "staging" {
~ id = "xxxxxxxx:staging" -> (known after apply)
# (2 unchanged attributes hidden)
~ deploy_access_levels {
~ access_level_description = "SRE" -> (known after apply)
- group_id = xxxxxxx -> null # forces replacement
- user_id = 0 -> null
# (1 unchanged attribute hidden)
}
~ deploy_access_levels {
~ access_level = "maintainer" -> (known after apply)
~ access_level_description = "Maintainers" -> (known after apply)
~ group_id = 0 -> xxxxxxx # forces replacement
- user_id = 0 -> null
}
}
Plan: 1 to add, 2 to change, 1 to destroy.
@dschaaff Is the group (with id var.sre_group_id) shared with the project gitlab_project.this.id?
It seems to be a current limitation of the GitLab API that in case it's not shared with the group it's just ignored without any errors and thus, the provider runs into those perpetual state diffs.
It's mentioned in the upstream GitLab API docs and I've just added a little note block to the gitlab_project_protected_environment resource in the provider with #1210
@dschaaff can you please confirm if this was or wasn't the issue for you? (Also if you feel like it, it would be great to create an upstream GitLab issue to properly report errors for this situation.)
I do have that project shared with the group directly
resource "gitlab_project_share_group" "sre" {
project_id = gitlab_project.this.id
group_id = var.sre_group_id
group_access = "developer"
}
@dschaaff and I assume the resources are applied in the correct order? (well at least it should apply the second time I suppose)
Can you post some debug logs using TF_LOG=debug (make sure to redact sensitive data) ?
I'm attaching a redacted trace log with the relevant portion of data gitlab_tf_log_trace.zip .
Here is the full source of the terraform code
resource "gitlab_project" "this" {
name = var.name
namespace_id = redacted
approvals_before_merge = 1
archive_on_destroy = true # archive project instead of delete on terraform destroy
ci_forward_deployment_enabled = true
description = var.description
forking_access_level = "disabled"
issues_enabled = false
merge_pipelines_enabled = true
merge_trains_enabled = true
only_allow_merge_if_all_discussions_are_resolved = true
only_allow_merge_if_pipeline_succeeds = true
packages_enabled = true
printing_merge_request_link_enabled = true
public_builds = false
push_rules {
prevent_secrets = true
}
remove_source_branch_after_merge = true
shared_runners_enabled = false
squash_option = "default_off"
# TODO: what changes to ignore
lifecycle {
ignore_changes = []
prevent_destroy = true
}
}
resource "gitlab_branch_protection" "this" {
for_each = toset(["master", "main"])
project = gitlab_project.this.id
branch = each.key
push_access_level = "maintainer"
merge_access_level = "maintainer"
unprotect_access_level = "maintainer"
allow_force_push = false
code_owner_approval_required = true
}
resource "gitlab_project_share_group" "sre" {
project_id = gitlab_project.this.id
group_id = var.sre_group_id
group_access = "developer"
}
# TODO: this resource doesn't fully work right now. It produces a perpetual diff
resource "gitlab_project_protected_environment" "staging" {
project = gitlab_project.this.id
environment = "test"
deploy_access_levels {
access_level = "maintainer"
}
deploy_access_levels {
group_id = var.sre_group_id
}
depends_on = [
gitlab_project_share_group.sre
]
}
module "services_repos" {
source = "./modules/microservice-project"
for_each = {
"my-app" = {
name = "my-app"
description = "test"
}
}
name = each.value["name"]
description = each.value["description"]
sre_group_id = gitlab_group.sre.id
}
Okay, according to the logs I think the problem is that the deploy_access_levels attribute is a list (which is ordered) and the GitLab API returns the deploy access levels in a different order than it previously did when they were first persistent in state.
This is the order it's returned by the API:
"deploy_access_levels": [
{
"access_level": 40,
"access_level_description": "SRE",
"user_id": null,
"group_id": 51509863,
"group_inheritance_type": 0
},
{
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
"group_id": null,
"group_inheritance_type": 0
}
]
The logs don't contain the API response for when it was queried the first time. Do you mind posting that parts of the logs, too to verify? And maybe also the part of the state file which contains the state for that particular resource.
The Terraform provider SDK supports a set type, but this wouldn't probably work either because of the computed attributes within the elements of the deploy_access_levels (I need to verify this). A possible solution might be to just deterministically order the deploy access levels before writing them to state and hack our way around with customize diff / suppress diff functions so that the user doesn't have to order it the same way in the HCL.
I always get the response in this order
{
"name": "test",
"deploy_access_levels": [
{
"access_level": 40,
"access_level_description": "SRE",
"user_id": null,
"group_id": 51509863,
"group_inheritance_type": 0
},
{
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
"group_id": null,
"group_inheritance_type": 0
}
],
"required_approval_count": 0,
"approval_rules": []
},
This always has the group before the maintainers access level. If I switch my terraform resource to match that order, like below, then I no longer receive a diff on every apply.
resource "gitlab_project_protected_environment" "staging" {
project = gitlab_project.this.id
environment = "test"
deploy_access_levels {
group_id = var.sre_group_id
}
deploy_access_levels {
access_level = "maintainer"
}
depends_on = [
gitlab_project_share_group.sre
]
}
The storage in the state file matches the order I put the deploy access levels in my resource. Here is what the state looked like originally
"instances": [
{
"schema_version": 0,
"attributes": {
"deploy_access_levels": [
{
"access_level": "maintainer",
"access_level_description": "SRE",
"group_id": 51509863,
"user_id": 0
},
{
"access_level": "maintainer",
"access_level_description": "Maintainers",
"group_id": 0,
"user_id": 0
}
],
The key for now seems to be to ensure that the ordering in the terraform resources exactly matches the API response from GitLab. I suppose I'd call that a bug as I wouldn't expect the order that deploy_access_levels are listed in terraform to matter.
@dschaaff thanks for the confirmation! Yes, indeed that's a bug - the order you define your deploy_access_levels in shouldn't matter.
This functionality has been released in v3.17.0 of the Terraform GitLab Provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.
For further feature requests or bug reports with this functionality, please create a new GitHub issue. Thank you!