Best practice for resources with no static ID

I have a platform whose REST API has a resource (let’s call it A). The REST API allows CRUD of this resource. Upon creation, the resource is automatically assigned an ID. RUD of the resource is possible using this ID. I started developing a Terraform resource corresponding to A.

But my problem started when I realized that the ID of A is not static throughout its lifecycle. When an A is created, an ID “a-0” is always assigned to it. If I create another A, the new A will be assigned ID “a-0” and the old A’s ID will be changed to “a-1”. Similarly, if I delete the “a-0” A, the ID of “a-1” will be changed to “a-0”. Also, there is no other way to uniquely identify an A since its absolutely possible to have As which are similar in every aspect except their IDs.

Also, FYI, it is possible to read the entire list of A’s using REST from the platform, so I have considered creating a resource that represents the entire list of A’s on the platform, but I would consider that a last resort.

Please suggest ways to design a Terraform solution for managing As on such a platform.

Hi @rounak-adhikary :wave:t6:,

Reading what you wrote here, the first thing that comes to mind would be to create a Terraform resource that represents the collection of API resources (all A’s) instead of representing a single API resource (one A instance). The users of your provider would declare their desired state of all A resource’s in their configuration and you can easily account for differences in id in your provider as being semantically equal as long as the number of A’s and each A’s attributes are the same as what’s in configuration.

While I think it’s still possible to create a Terraform resource that models a single A instance, I think you would still need to call your list API in the resource’s read method in order to reconcile the changes in ID to ensure that the Terraform resources conceptually map to the same API resource instance. And if this isn’t done carefully then you can end up creating duplicate API resources or unintentionally modifying different resources.

Hi @SBGoods Thanks for your input! I am interested to understand more about the

Let me see if I can get your inputs using an example.
Say my resource has 3 fields, id, start and end.
Say, in the beginning, my server looks like

[
    {
        "id": "a-1",
        "start": 10,
        "end": 20
    },
    {
        "id": "a-0",
        "start": 30,
        "end": 40
    }
]

Now I create a resource with config

resource platform_A A {
    start = 50
    end = 60
}

My server becomes

[
    {
        "id": "a-2",
        "start": 10,
        "end": 20
    },
    {
        "id": "a-1",
        "start": 30,
        "end": 40
    },
    {
        "id": "a-0",
        "start": 50,
        "end": 60
    }
]

and my resource state becomes

{
    "id": "a-0",
    "start": 50,
    "end": 60
}

So far, all is good.

Now suppose, someone creates (outside terraform) an A starting at 70 and ending at 80. Then deletes the A starting at 10 and ending at 20.

Now my server becomes

[
    {
        "id": "a-2",
        "start": 30,
        "end": 40
    },
    {
        "id": "a-1",
        "start": 50,
        "end": 60
    },
    {
        "id": "a-0",
        "start": 70,
        "end": 80
    }
]

After this, if I run terraform plan, my resource will try to read the “a-0” A, and go ahead and edit it, since config and infra will not match.
Whereas it should have read “a-1” A and done nothing.

How can I prevent my resources from running into this issue? Even if I read the entire array of As from the server, I can’t figure out what algorithm
will allow me to recognize that my resource should latch onto the “a-1” A.

Please note that it’s possible to have multiple As on the server with same start and end values.

Hmm… Now thinking on this again, maybe it’s not possible to always map the Terraform resource to the same server resource in the provider if modeled that way. I think that if you want to still have a single Terraform resource model a single A server resource, you’ll have to make a behavior tradeoff.

Option A:

The provider can guarantee that the server resource of the specified ID has attributes that matches the Terraform configuration. So if a practitioners specifies:

resource platform_A A {
    id = "a-1"
    start = 50
    end = 60
}

The provider will either create an “a-1” resource with those attributes or update the “a-1” resource to have a start value of 50 and an end value of 60.

Option 2:

The provider can guarantee that there is a server resource that has the same attributes as the Terraform configuration. So if a practitioners specifies:

resource platform_A A {
    start = 50
    end = 60
}

The provider will get the list of all A server resources and if there is not a server resource with the a start value of 50 and an end value of 60, it will create a new resource with those attributes. Note that this will match any resource with the same attributes (excluding ID) not necessarily the one that Terraform created.

I think the only way to really have Terraform match the logical Terraform resource and the actual server resource 1-1, is to define the entire list of server resources as a list or set nested attribute:

resource platform_A_resources A {
   A_resources = [
    {
        start = 10
        end = 20
    },
    {
        start = 30
        end = 40
    },
   {
        start = 50
        end = 60
    },
  ]
}

Modeled like this, Terraform can detect changes to the entire list of A resources outside of Terraform without you, the provider developer, having to make arbitrary decisions on what server resource to create/update. It will be the the practitioners responsibility to declare the exact end state of all of the server’s A resources, and they could import the existing A server resources into Terraform.

1 Like

Thanks @SBGoods . That makes perfect sense to me.