Ignore the order of a complex typed list

We are currently trying to support the following syntax in the GitLab Terraform provider:

resource "gitlab_project" "this" {
  name                   = "some-project"
  // Protected branches
  protected_branch {
    branch             = "main"
    merge_access_level = "maintainer"
    push_access_level  = "maintainer"

    allowed_to_push {
      user_id = 1
    allowed_to_merge {
      user_id = 2
  protected_branch {
    branch             = "release"
    merge_access_level = "developer"
    push_access_level  = "developer"

The logical schema for the protected_branch should be some kind of collection of resources which in turn have some computed and non-computed attributes - amongst them is e.g. the allowed_to_push which again is a collection of resources with computed and non-computed attributes. The branch attribute is required and unique and can therefore be treated as some kind of key.

These collections must be unordered so that no diff should occur if the order of the protected_branch changes or an protected branch is inserted between two already existing ones.

Naturally, a schema.TypeSet seems to be the most fitting for both the protected_branch, but also the allowed_to_push and allowed_to_merge sub-resources.
However, this doesn’t work because some attributes are computed and if I use a schema.TypeSet I’m also having non-empty plans after an apply (see Custom Provider: How to reference computed attribute of TypeMap/List/Set defined as nested block - #2 by apparentlymart) . In addition with a set the entire block would be tread as “changed” even if only a single attribute changes - which would be kinda okay, but rather unfortunate.

Now, if I change this to a list I can make a create and update work properly, but using an update logic as proposed here, TypeSet is not picking up correct changes - #4 by apparentlymart.
The problem now is that I want this list to be unordered - I’ve tried several things like using a suppress function or a customized diff.

The problem with a suppress function is, that I can only suppress an entire attribute - and even if I define it on the protected_branch list attribute the function is called for all the sub-resource attributes which doesn’t help, because the logic to figure out if the list was reordered requires access to the entire lists old and new values at the same time.

Neither can I make a customized diff function work, because I don’t see a way how to “discard” a planned changed.
I’ve tried to use a modified version of my update function to figure out if an actual change to the protected branch has been made or if it was a reordering of some sort combined with then calling rd.SetNew() in many ways - but this doesn’t seem to have an effect.
Probably because rd.SetNew() only works for computed attributes.

We are using the latest version of the SDK v2.

Any ideas what the best approach here is? Is there any solution to this problem? Do you know of any resources in any providers which do the same thing which I can get inspired by?

1 Like