hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
42.39k stars 9.49k forks source link

Mark resource as added or tainted during resource create function #16205

Open flynnhandley opened 6 years ago

flynnhandley commented 6 years ago

Hi There,

It would be nice if I could mark a resource as (partially) added or tainted during creation.

Given the pseudo code below, If we create VM, then the Terraform process is destroyed before resourceHypervVMCreate can return, we end up with a VM on the hypervisor which has not been added to the Terraform state, therefore Terraform -destroy will not remove the VM.

You can see the full code here

func resourceHypervVM() *schema.Resource {  
    return &schema.Resource{
        Create: resourceHypervVMCreate,
    }
}

func resourceHypervVMCreate(d *schema.ResourceData, meta interface{}) error {
    // Create VM on hypervisor here
    // Flag resource as tainted
    // Do more stuff
    // Flage resource as added (or un tainted)
    return
}
apparentlymart commented 6 years ago

Hi @flynnhandley!

We may be able to do something like this, though I'd want to think about it a bunch more to see if there are implications we're not thinking of.

The intended way to handle this today is to call d.SetId immediately after the resource is created, so that it gets recorded in the state, and then to progressively record more values in the state as you go, possibly using d.Partial and d.SetPartial to control what gets written to the state when you return. (Partial mode allows you to prevent certain values that were provided in config from being written to the state, to avoid creating the impression that certain things were committed when they actually weren't.)

If the right values are written to state at the right times then Terraform's usual diffing machinery can take care of this by detecting on the next run that the resource state doesn't completely match the configuration, and then the Update implementation can pick up where the Create left off, rather than replacing the resource entirely. (Though indeed, if one of the things that needs fixing is a ForceNew attribute then it will be marked as a replacement action, as expected.)

flynnhandley commented 6 years ago

Hi @apparentlymart, Thanks for the feedback.

I think that d.partial and d.setPartial solve a slightly different problem where Create completes but not all components of the resource have been created.

The problem I'm try to solve is where Create terminates before the state can be persisted (leaving a Zombie resource)

apparentlymart commented 6 years ago

Hi @flynnhandley,

Looking again at the full source-code you linked, I have some different suggestions that don't involve the use of d.Partial:

As soon as you call d.SetId with a non-empty string, Terraform will write the instance state out on exit.

You don't currently have a Read implementation, but if you implement it then Create only needs to save enough information to be able to read back the current "real" state and write it into Terraform using d.Set calls. This means it no longer matters if Create ends early, as long as you make sure you save all of the ids you need to implement Read.

With Read properly implemented, Terraform will be able to detect that the current "real" state is missing items compared to the config, and so Terraform will generate an "update" diff as expected, to finish constructing anything that wasn't built during Create.

I notice also that your Update implementation is just to delete and then create. In that case, you can omit the Update function altogether and just mark all of your attributes as ForceNew, which tells Terraform that any config change requires the resource to be destroyed and re-created.

With all of that done, you get the effect you were looking for:

The key thing to know here is that although Terraform writes the entire resource config into the state, most of it isn't really needed as long as the Read implementation is robust because it will automatically update the state to match the real world on the next run.