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.4k stars 9.5k forks source link

Terraform test execution doesn't handle `prevent_destroy=true` #34960

Open daveS2 opened 5 months ago

daveS2 commented 5 months ago

Terraform Version

Terraform v1.7.5
on darwin_amd64
+ provider registry.terraform.io/hashicorp/google v5.23.0

Terraform Configuration Files

Terraform conifg:

terraform {
  required_version = "1.7.5"
}

resource "google_kms_crypto_key" "key" {
  name            = "resource-name"
  key_ring        = "keyring"
  rotation_period = "10000000s"

  lifecycle {
    prevent_destroy = true
  }
}

tftest.hcl file:

mock_provider "google" {

}

run "test"{
    assert {
    condition     = google_kms_crypto_key.key.name == "resource-name"
    error_message = "Name is not correct"
  }
}

Debug Output

https://gist.github.com/daveS2/fd251cc2cb61af3c8df0f3966aadd253

Expected Behavior

The prevent destroy should be handled by either expect failures:

run "test"{

    assert {
    condition     = google_kms_crypto_key.key.name == "resource-name"
    error_message = "Name is not correct"
  }
  expect_failures = [
    google_kms_crypto_key.key
  ]
}

Which doesn't work or it should overridable:

mock_provider "google" {
 override_resource{
    target = google_kms_crypto_key.key
    values = {
        lifecycle = {prevent_destroy=false}
    }
 }
}

Actual Behavior

The tests always fail

Steps to Reproduce

  1. terraform init
  2. terraform test

Additional Context

For resources where we want destroy protection, terraform mocks always attempt to destroy but we cannot capture the error thrown or override the value

References

No response

liamcervante commented 5 months ago

Hi @daveS2, thanks for filing this.

The override_resource block will only provide values for computed attributes. It won't override attributes that are supplied from the config. The aim of the mock provider is to replace the behaviour of the provider, in this case providing values for computed attributes that would normally be provided by the underlying cloud provider. Unfortunately, you cannot change the values within the configuration using the mocking framework.

Potentially, you could introduce a variable into your module that defaults to true and you can override it within your tests?

# main.tf

variable "prevent_destroy" {
  type = boolean
  default = true
}

resource "google_kms_crypto_key" "key" {
  name            = "resource-name"
  key_ring        = "keyring"
  rotation_period = "10000000s"

  lifecycle {
    prevent_destroy = var.prevent_destroy
  }
}
# main.tftest.hcl

run "test" {
  variables = {
    prevent_destroy = false
  }

  assert {
    // ...
  }
}

I appreciate changing the configuration to support the tests isn't always the best solution, but it might work for you here?

daveS2 commented 5 months ago

Thank you for your response, this would work, but I am not sure you can pass a variable into a lifecycle block? If I change my terraform code:

resource "google_kms_crypto_key" "key" {
  name            = "resource-name"
  key_ring        = "keyring"
  rotation_period = "10000000s"

  lifecycle {
    prevent_destroy = var.prevent_destroy
  }
}

variable "prevent_destroy" {
  default = true
}

And then run terraform test, I get the following errors:

│ Error: Variables not allowed
│ 
│   on demo.tf line 11, in resource "google_kms_crypto_key" "key":
│   11:     prevent_destroy = var.prevent_destroy
│ 
│ Variables may not be used here.
╵
╷
│ Error: Unsuitable value type
│ 
│   on demo.tf line 11, in resource "google_kms_crypto_key" "key":
│   11:     prevent_destroy = var.prevent_destroy
│ 
│ Unsuitable value: value must be known
liamcervante commented 5 months ago

Apologies, I didn't know that you couldn't use variables for this. Unfortunately, I can't think of a way to handle this within the current capabilities of the testing framework.

I'll retag this as an enhancement request. I'm not sure the best way to handle this currently. Potentially, we could make the testing framework ignore the prevent_destroy attribute within the lifecycle block.

Sorry for the missing functionality!