Custom terraform provider, update issue

Hi, I created custom terraform provider.

POST, GET works,

How to enforce update to be called?
Problem that REST API created field “guil_id” which is described as “computed”
In Update I need to pass this guid_id as parameter.
However terraform declined it with the message:
Can’t configure a value for “guid_id”: its value will be decided automatically based on the result of applying this configuration.
It is correct because guid_id described as “computed”.

Is it possible to describe schema separately for every operation( GET, DELETE, POST,PUT)?

Thanks,
Dmitriy

HI @dtrvno,

If an attribute is computed, that attribute should be stored when the resource is first created, and the user should not have to later manually add that attribute to the configuration. You then use that stored attribute for all subsequent operations to read and modify the resource.

The schema itself must be static, as it’s always used to decode the configuration and state for that object. If something could be either computed or present in the configuration, you can combine Computed and Optional, but that seems to be a different use case from what you’re describing since guid_id will be created by the remote service.

Hi, Thanks for the response

It is exactly what I did- I described guid_id attribute as “calculated”
REST API POST generated that resource and I can see it in terrasform.

Then I need to use that attribute as a key in PUT for update and terraform throws an error:

Can’t configure a value for “guid_id”: its value will be decided automatically based on the result of applying this configuration.

That is why I asked question about separate schema for update.

How to describe guid attribute in this case?.Might be optional?

Best regards,
Dmitriy

Somehow you are inserting the computed guid_id value into the configuration, which is what triggers the error. Perhaps someone with more experience with the SDK can recognize the mistake from the error message, but the problem is generally caused when writing that value directly into the configuration.

The computed attribute should definitely not be Optional if it’s only generated by the provider. This is how nearly every single resource in existence works, so there is more than likely a simple oversight going on here here. Perhaps a more complete, reproducible example would help.

Thank for the response.

Yes, I tried in the client setup guid_id as a parameter and passed into provider.
Provider complaining because value should be calculated.

So , answer still not clear.
I will describe the case more precisely.

I have a REST API POST which generates guid_id.
guid_id described as “calculated”

I have REST API PUT which uses guid_id as a key to update resource.
I want to have this as an input.

How to describe guid_id attribute?

Best regards,
Dmitriy

Please show what you are trying to do with a real example from the provider and configuration, it is not clear what you mean by “client setup guid_id as a parameter and passed into provider”.

Hi, here is main.tf
terraform {
required_providers {
orch = {
version = “1.0.1”
source = “localhost/company/orch”
}
}
}

provider “orch” {

}
variable “iaas_name” {
description=“Name of iaas provider”
}

variable “iaas_url” {
description=“Url of iaas provider”
}
variable “iaas_update_url” {
description=“Url of iaas provider”
}
variable “iaas_type” {
description=“Url of iaas provider”
}
variable “iaas_guid” {
description=“guild of iaas provider”
}
resource “orch_iaas_resource” “my_iaas” {
type=var.iaas_type
name=var.iaas_name
url=var.iaas_url
}
output “my_iaas_output” {
value=[orch_iaas_resource.my_iaas.guid_id,orch_iaas_resource.my_iaas.name]
}
resource “orch_iaas_resource” “my_iaas1” {
guid_id=orch_iaas_resource.my_iaas.guid_id
type=var.iaas_type
name=var.iaas_name
url=var.iaas_update_url

}

output “my_iaas_output1” {
value=[orch_iaas_resource.my_iaas.guid_id,orch_iaas_resource.my_iaas.url]
}
I created resource my_iaas. It is calls POST REST API.
Resource generates guid_id. I can print it.

Then I want to update resource. I described as my_iaas1, but got error if I described guid_id as required.
If I describe as Optional I got another error
Error: Provider produced inconsistent final plan

│ When expanding the plan for orch_iaas_resource.my_iaas1 to include new values learned so far during apply, provider "localhost/company/orch
" produced an
│ invalid new value for .guid_id: was null, but now cty.StringVal(“a93a5416-d9fa-4191-8fd7-11751449ea20”).

How to describe guid_id in this case?

Best regards,
Dmitriy

If my_iaas and my_iaas1 represent the same logical object, then they should be represented by the same block of configuration in Terraform. Putting

guid_id = orch_iaas_resource.my_iaas.guid_id

is not valid, because that attribute is computed by the provider. If you want to update that resource instance, then you would modify the configuration block for that instance, not write a second block.

Thanks, I saw that approach in your hashicups example.
I tested it is works. I tried to separate it - looks it is impossible.

In your example you do not use calculated attributes.
So, my plan is- I will use attribute as calculated and will try to use it in the same block.
Will see how it is works.

Thanks for the help!

Best regards,
Dmitriy

Hi,

Based in our last conversation I need to create blocks to update object.
However , update values are unknown at start.

Is it block supports “depends_on” attribute?

Let say, I create object in one block, then in second block I put depends_on and that means that block will not start until other resource will not be created.
In second block I will put input from other resources…

Best regards,
Dmitriy

depends_on is usually not needed, and only required when there are no existing references between the resources.

I don’t fully understand what it is you are trying to do here, but I have the feeling that you are trying to approach Terraform as if it’s a procedural language, when it is not. In Terraform you declare the desired state via the configuration, and the order of operations is determined by the dependencies of the resources. You would not normally write a resource with the intention of immediately updating that resource with a different value via the same overall configuration.

Hi,

Yes, I do not plan to update resource immediately after creation.
That is why I’m asking: will terraform follow some dependencies.

What I’m trying to achieve? Let’s we have a server with REST API interfaces.
Server has objects inside which execute create , update based on the REST API

So, my expected flow:
create object1
create object2
create object3 using info from object1
create object4 using object 3
update object2 using object4

I agreed , terraform is not procedure language.
However , based on my experience with aws provider terraform capable to do lot of things.

So, is it possible above scenario?

Best regards,
Dmitriy

I guess the only advice I can offer here is that you cannot have “create object2” at the same time you have “update object2”, those must be from separate plans. If that’s what you envision here, then it’s perfectly fine.

Thanks for the advice.

Best regards,
Dmitriy

Hi,
followed your advice:

I created object in one plan.
Then I moved to another plan(directory)
I imported object with terraform import using generated ID.
Import was successful. I see guid_id in terraform state file…
Then I run apply:
resource “orch_iaas_resource” “my_iaas” {
type=var.iaas_type
name=var.iaas_name
url=var.iaas_url
}
Update has been called(!) by terraform.
However guid_id is not passed.
I cannot put guid_id as parameter because it is calculated.

Here is an another idea , please advice.

Let say I describe resource1 as before with guid_id as calculated.
Then I describe resource2 with guid_id as required.
I can create resource1.
Then , when I need to update it - I will call resource2.
In resource2 I will implement Create and instead of POST will use PUT, Then it is suppose to work.

Of course , I need to implement two resources serving one object , but it is suppose to work. And we are going to use one plan… Any comments?

Best regards,
Dmitriy

Hi @dtrvno ,

Despite the length of this thread, and the other topic you posted about it as well, I’m still completely confused what you are trying to achieve with your extremely unusual usage of Terraform.

I think it would potentially be very helpful if you could take a step back, and explain what you are actually trying to achieve - in plain English, with no reference to Terraform specifics. Currently, you seem to be approaching Terraform provider development with certain rather odd preconceptions, which as far as I can tell, seem to disagree with how Terraform is designed. If you could explain your high level goal, people in this forum might be able to suggest a better approach.

Well, yes, of course it has, this is an update operation after all?

But you’ve indicated it is present in the state… that makes me think its absence is probably due to some kind of bug in your custom provider code. I think it could be very important for you to put this custom provider code somewhere on GitHub so people can look at it, and comment directly.

This would be a very peculiar provider design, contrary to how almost all public Terraform providers work.

The only circumstance in which such a pattern would normally be used, is if “resource1” and “resource2” managed different subsets of the configuration of the underlying thing being managed, and there was a requirement to make some of the settings at a different time.

In the example you previously gave, both of your resources had the same set of attributes (type, name, url) so I do not think this circumstance applies here.

Please explain why you cannot just modify the configuration of your “resource1” when you need to update the managed object - why you think you need a separate “resource2” at all?

Hi,

Here is explanation:

We have a REST API with POST, GET, PUT , DELETE
REST API POST has parameters like name, url and so on but does not have guid_id
Result of this API - guid_id has been created and return as an output.

I described guid_is as Computed.

Now I want to update that object. REST API requires guid_id as a parameter together with other attributes.
guid_id is required for PUT/DELETE.

question: How to describe guid_id in terraform- it is common description for all methods(actually I started with this).
If I describe as “Computed”- terraform does not accept as a parameter.
Optional- got inconsitent state.
Required- then POST will fail.
That is why I came up with two resources serving the same object…

Any other idea?
Best regards,
Dmitriy

I think maybe you are not understanding how Terraform is supposed to be used.

Terraform is not a tool where users express exactly which HTTP API methods should be called.

Terraform is a tool where users express what they want the end state to be, and Terraform takes care of figuring out the mix of creates, updates and deletes needed to get there.

The schema of a Terraform resource defines the fields that a user of Terraform can set, and the fields the provider will store in the state. It does NOT directly control the shape of data that the provider sends to REST APIs.

You should have one Terraform resource type.

The various methods that need to be defined for a Terraform resource will directly map to REST methods:

  • Create - POST
  • Update - PUT
  • Delete - DELETE
  • Read - GET

You will store the ID returned during Create in the Terraform state and re-use it from there in the other methods.

This is all very standard normal use of Terraform, replicated in very many providers.

There will never be any direct specification of your guid_id in user .tf files.




HOWEVER much earlier in this thread you mentioned something unusual:

Terraform will never do this.

Terraform will re-order the sequence of operations so that object2 is only created after object4, when all the information is available, so that object2 can be created directly in its final state.

Hi, Thanks for the explanation.
However here are results of my investigation.

Terraform using ID to identify objects regardless it is belonging to the same type of resources or not. It was surprise for me . I though if we have resource of type A and resource of type B and both of them have the same id- terraform consider them as a different resources. (I’m not sure about it, but looks like).

So, here is idea I implemented.

We have resource1 of type A, we have resource 2 of type B.
Resource A has implementation of method Create and empty implementation for other methods. Resource A does not have guid_id as a parameter

Resource B has update functionality in both Create and Update and has guid_id as a parameter

Main point- both resources has id generated based on name of resources, like
d.SetId(“A_”+iaas_output.ID) and d.SetId(“B_”+iaas_output.ID)

So, terraform consider them as separate resources and does whatever I want.
So , by doing that I can implement scenario with update.

Here is a terraform script which (works!) does create resource and update

terraform {
required_providers {
orch = {
version = “1.0.1”
source = “localhost/company/orch”
}
}
}

provider “orch” {

}
variable “iaas_name” {
description=“Name of iaas provider”
}

variable “iaas_url” {
description=“Url of iaas provider”
}
variable “iaas_update_url” {
description=“Url of iaas provider”
}
variable “iaas_type” {
description=“Url of iaas provider”
}
variable “iaas_guid” {
description=“guild of iaas provider”
}
resource “orch_iaas_resource” “my_iaas” {
type=var.iaas_type
name=var.iaas_name
url=var.iaas_url

}

resource “orch_iaas_update_resource” “my_iaas_update” {
guid_id=orch_iaas_resource.my_iaas.guid_id
type=var.iaas_type
name=var.iaas_name
url=var.iaas_update_url

}
output “my_iaas_output” {
value=[orch_iaas_update_resource.my_iaas_update.guid_id,orch_iaas_update_resource.my_iaas_update.url]
}

So, I can do update any time and manage dependencies…

What do you think?
Best regards,
Dmitriy

This is not correct. Terraform has no concept of unique identity between resources, the id attribute is just a computed string, and only in the legacy SDK does it have special meaning.

You still have not explained why you think you need separate resource definitions or types for creating and updating the same resource. A single logical resource should only correlate to a single configuration block.