following the documentation
I tried several construct with terragrunt to prevent one of my volume ressource to be destroyed.
in my terragrun.hcl file I tried to separate two terraform { } blocks one with modules to destroy and other with volume/db which should survive the destroy.
Unfortunatelly those are not allowed.
Neither are multiple source= keyword.
I also investigated into 2 other topics, generating on destroy the concrete target list using
arguments = [
"-auto-approve"
"${run_cmd("terraform", "state", "list", "|", "grep", "-v", "openstack_blockstorage_volume_v2", "|", "xargs", "-n1", "echo", "-target")}
and use something as terraform state rm openstack_blockstorage_volume_v2 as a after hook
neither were working :(
Is there a concrete example of using this prevent_destroy on specific resources?
Also referring to the following terraform posts: https://github.com/hashicorp/terraform/issues/3874
Any idea how to achieve this with terragrunt as a super seed of terraform.
prevent_destroy is for a specific module and is not designed to handle a specific resource within a module:
to protect selected Terraform module
For this use case, I would suggest extracting the volume to its own module so that it has its own state file, and then use dependency blocks in terragrunt to string together the dependencies to and from the volume. That way, you can destroy everything else in a destroy-all, but keep the volume using the prevent_destroy flag.
To clarify further, terragrunt is optimized at the module level. Meaning, the smallest unit that it is designed to handle is a single state file and not a resource (it relies on terraform for further granularity). You should be looking at terraform features when dealing with actions on elements within a state file, and looking at terragrunt features when dealing with actions on the whole state file or multiple state files.
This use case is for actions on elements within a state file, so terragrunt does not handle that. However, you can turn it into an action on multiple state files: hence, the suggestion above to break out the volume into its own module.
Thanks for the answer, I will give it a try. When you mean:
extracting the volume to its own module (terragrunt)
This would be a second terragrunt.hcl file ?
tree *[master]
.
โโโ dev
โย ย โโโ nodes
โย ย โย ย โโโ terraform.tfvars
โย ย โย ย โโโ terragrunt.hcl
โย ย โโโ terraform.tfvars
โโโ export-inventories.py
โโโ generated
โย ย โโโ hosts-prod
โย ย โโโ inv-prod.yml
โย ย โโโ manag-prod.csv
โย ย โโโ ssh-config-prod
โโโ prod
โย ย โโโ code
โย ย โย ย โโโ terraform.tfvars
โย ย โย ย โโโ terragrunt.hcl
โย ย โโโ code-page
โย ย โย ย โโโ terraform.tfvars
โย ย โย ย โโโ terragrunt.hcl
โย ย โโโ terraform.tfvars
โโโ terragrunt.hcl
The main terragrun.hcl point to modules/os/ also including the volume part.
terraform {
source = "${path_relative_from_include()}/../modules/os"
...
modules currently look like:
modules
โโโ os
โโโ backend.tf
โโโ main.tf
โโโ provider.tf
โโโ var.tf
so you suggest to add a new module called: modules/persistent/volume.tf containing the volume code.
then have a second persistent/terragrunt.hcl refering the modules/persistent/ as source.
terraform {
source = "${path_relative_from_include()}/../modules/persistent"
...
}
prevent_destroy = true
with,
and then use dependency blocks in terragrunt to string together the dependencies to and from the volume.
you mean have a dependency definition in the code-page/terragrunt.hcl and code/terragrunt.hcl
include {
path = find_in_parent_folders()
}
dependencies {
paths = ["../../persistent/"]
}
Will check this out and update this post
I actually mean the dependency block (https://github.com/gruntwork-io/terragrunt#passing-outputs-between-modules), so that you can access the outputs of the volume module to refer to it.
ok thanks I will give it a try tomorrow.
sorry for the delay of my response, definitively 15 days after tomorrow :(
I played a bit with the dependency block.
And encountered some issue with input variables not overwriting the default.
cat persistent/var.tf
variable "volume_size" {
type = "string"
default = "10"
}
cat persistent/main.tf
# Create persistent vol
resource "openstack_blockstorage_volume_v2" "vol" {
name = "DATA-test"
description = "Data Vol for test"
size = var.volume_size
}
cat persistent/terragrunt.hcl
dependency "os" {
config_path = "../os"
}
inputs = {
volume_size = dependency.os.outputs.volume_size
}
cat os/main.tf
...
output "volume_size" {
value = "${var.volume_size}"
}
...
the output displays the wished volume size but it seems that the input variable is not overwritten.
Additionally the dependency does not first create the vm instances to then create and attach the persistent volume...
in the end I get some error:
[terragrunt] 2019/11/28 21:36:59 Encountered the following errors:
Module is protected by the prevent_destroy flag in /home/eric/live/prod/persistent/terragrunt.hcl. Set it to false or delete it to allow destroying of the module.
[terragrunt] 2019/11/28 21:36:59 Unable to determine underlying exit code, so Terragrunt will exit with error code 1
I did add some dependencies to these vm instance to fix the dependencies but this prevents me to destroy them.
cat live/prod/persistent/terragrunt.hcl
terraform {
source = "${path_relative_from_include()}/../../../modules/persistent"
}
dependencies {
paths = ["../code", "../code-page"]
}
prevent_destroy = true
results in the following error message:
Cannot process module Module /home/eric/live/prod/code-page (excluded: false, dependencies: []) because one of its dependencies, Module /home/eric/live/prod/persistent (excluded: false, dependencies: [/home/eric/live/prod/code, /home/eric/live/prod/code-page]), finished with an error: Module is protected by the prevent_destroy flag in /home/eric/live/prod/persistent/terragrunt.hcl. Set it to false or delete it to allow destroying of the module.
[terragrunt] 2019/11/28 21:43:45 Unable to determine underlying exit code, so Terragrunt will exit with error code 1
Are there some example using the prevent_destroy and the dependency construct in terragrunt?
in the end I get some error:
The error is intentional. As indicated in the error message, it is telling you that you requested to destroy the module persistent, but terragrunt prevented you from destroying it because you have the prevent_destroy flag set to true.
However, this makes me realize that this is conflicting with your use case because terragrunt does not continue to destroy the dependent resources since it assumes it can't destroy those unless the parent is destroyed, and the parent can't be destroyed. I am not sure we want to change this behavior since it indeed does prevent you from shooting yourself in the foot.
Perhaps we can support a warn_on_prevent_destroy flag, although its uses are limited enough that I am not sure we want to support it.
A workaround here would be to take advantage of the folder structure so that the persistent module is in a different folder tree than the instances and os modules, so that you can run destroy-all at the level for the instances. For example:
.
โโโ node
โ โโโ instance
โ โ โโโ os
โ โ โโโ code
โ โโโ volume
โโโ persistent
This way, when you run apply-all in node, it will deploy everything but you can run destroy-all in instance to destroy everything but the volume.
Are there some example using the prevent_destroy and the dependency construct in terragrunt?
We don't have any public examples that show those two in conjunction. Additionally, the examples we have address a completely different use case than here, where you want to intentionally destroy everything but those that have prevent_destroy set to true. Our examples address the use case where you want to avoid accidentally destroying certain resources with prevent_destroy when you run destroy-all.
Thanks for the explanation, I was able to make it work after some trial and errors.
I can post the implementation non some gist a bit later when it's cleaned up.
I did separate the modules into 2 directories:
Then have a live/prod directory:
I had to define the dependency and inputs in the live/prod/{code,code-page} like following terragrunt.hcl file
include {
path = find_in_parent_folders()
}
dependencies {
paths = ["../volume"]
}
dependency "volume" {
config_path = "../volume"
mock_outputs = {
volume_id = "temporary-dummy-id"
}
}
inputs = {
volume_id = dependency.volume.outputs.volume_id
}
the variables volume_id had to be defined in the respective module modules/os/var.tf consuming the dependency.
For the error message, I would argue that it shall be a "warning" message not an error. As I instruct the module to not be deleted meaning the return code shall be 0. or a flag like you mentioned warn_on_prevent_destroy would come handy.
Thanks again for your help.
Most helpful comment
prevent_destroyis for a specific module and is not designed to handle a specific resource within a module:For this use case, I would suggest extracting the volume to its own module so that it has its own state file, and then use
dependencyblocks in terragrunt to string together the dependencies to and from the volume. That way, you can destroy everything else in adestroy-all, but keep the volume using theprevent_destroyflag.To clarify further,
terragruntis optimized at the module level. Meaning, the smallest unit that it is designed to handle is a single state file and not a resource (it relies on terraform for further granularity). You should be looking at terraform features when dealing with actions on elements within a state file, and looking at terragrunt features when dealing with actions on the whole state file or multiple state files.This use case is for actions on elements within a state file, so terragrunt does not handle that. However, you can turn it into an action on multiple state files: hence, the suggestion above to break out the volume into its own module.