From ebb990d7da154f27e6f2226eaaa071724ede7edd Mon Sep 17 00:00:00 2001 From: frmadem Date: Tue, 31 Mar 2026 12:35:12 +0200 Subject: [PATCH 1/8] feat(dummy) : add new controls for the destroy phase --- modules/dummy/README.md | 5 ++++ modules/dummy/main.tf | 48 ++++++++++++++++++++++++++++++-------- modules/dummy/variables.tf | 13 +++++++++++ 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/modules/dummy/README.md b/modules/dummy/README.md index aa740c9e3..773a0170d 100644 --- a/modules/dummy/README.md +++ b/modules/dummy/README.md @@ -32,6 +32,11 @@ module "diagnostic_test" { # APPLY TIME CONTROLS sleep_on_apply = 10 # Delays 'terraform apply' by 10 seconds crash_on_apply = false # Set to true to force 'terraform apply' failure + + # DESTROY TIME CONTROLS + sleep_on_destroy = 15 # Delays 'terraform destroy' by 15 seconds + crash_on_destroy = false # Set to true to force 'terraform destroy' failure + } output "plan_status" { diff --git a/modules/dummy/main.tf b/modules/dummy/main.tf index bf1c9e70c..7157f517f 100644 --- a/modules/dummy/main.tf +++ b/modules/dummy/main.tf @@ -3,13 +3,13 @@ data "external" "script_executor" { # Execute the node binary directly for better reliability and bypass complex shell nesting issues. program = [ - "node", + "node", "${path.module}/script.js" ] # Pass only the variables relevant to the PLAN phase to the script query = { - instance_name = var.instance_name + instance_name = var.instance_name sleep_duration = var.sleep_on_plan enable_crash = var.crash_on_plan } @@ -30,11 +30,11 @@ resource "null_resource" "conditional_sleep" { triggers = { run_always = timestamp() } - + provisioner "local-exec" { # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - + interpreter = ["sh", "-c"] + # Echo message and then sleep. We use count[0] to access the instance. command = "echo '--- APPLY-TIME DELAY --- Sleeping for ${var.sleep_on_apply} seconds...' && sleep ${var.sleep_on_apply}" when = create @@ -44,7 +44,7 @@ resource "null_resource" "conditional_sleep" { # 3. Conditional Crash: This resource is only created if 'crash_on_apply' is true. resource "null_resource" "conditional_crash" { count = var.crash_on_apply ? 1 : 0 - + # Force re-creation on every apply to ensure the provisioner runs triggers = { run_always = timestamp() @@ -52,14 +52,39 @@ resource "null_resource" "conditional_crash" { provisioner "local-exec" { # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - + interpreter = ["sh", "-c"] + # Echo message and then use 'exit 1' to deliberately cause failure command = "echo '--- APPLY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" when = create } } + +# 4. Conditional Sleep on DESTROY +resource "null_resource" "conditional_destroy_sleep" { + count = var.sleep_on_destroy > 0 ? 1 : 0 + + # No create provisioner needed – this resource only exists to run destroy logic + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = "echo '--- DESTROY-TIME DELAY --- Sleeping for ${var.sleep_on_destroy} seconds...' && sleep ${var.sleep_on_destroy}" + } +} + +# 5. Conditional Crash on DESTROY +resource "null_resource" "conditional_destroy_crash" { + count = var.crash_on_destroy ? 1 : 0 + + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = "echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" + } +} + + # --- Module Outputs --- # Output from the PLAN-TIME script @@ -76,6 +101,9 @@ output "script_timestamp" { # Check to ensure the base apply-time resource was created output "apply_resource_id" { - description = "The ID of the base null resource, indicating apply logic executed." - value = null_resource.diagnostic_base_logic.id + description = "The ID of the base null resource, indicating apply logic executed." + value = null_resource.diagnostic_base_logic.id } + + + diff --git a/modules/dummy/variables.tf b/modules/dummy/variables.tf index 09fa11316..84b954bf4 100644 --- a/modules/dummy/variables.tf +++ b/modules/dummy/variables.tf @@ -32,3 +32,16 @@ variable "crash_on_plan" { default = false } +# 5. Sleep during 'terraform destroy' +variable "sleep_on_destroy" { + description = "Number of seconds to sleep during the 'destroy' phase." + type = number + default = 0 +} + +# 6. Crash during 'terraform destroy' +variable "crash_on_destroy" { + description = "Set to true to force a non-zero exit code during the 'destroy' phase." + type = bool + default = false +} From 1bf6da9d4f1e928268211e10e3563db5c042d217 Mon Sep 17 00:00:00 2001 From: frmadem Date: Tue, 31 Mar 2026 12:55:59 +0200 Subject: [PATCH 2/8] Update modules/dummy/variables.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- modules/dummy/variables.tf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/dummy/variables.tf b/modules/dummy/variables.tf index 84b954bf4..1c6212e43 100644 --- a/modules/dummy/variables.tf +++ b/modules/dummy/variables.tf @@ -37,6 +37,11 @@ variable "sleep_on_destroy" { description = "Number of seconds to sleep during the 'destroy' phase." type = number default = 0 + + validation { + condition = var.sleep_on_destroy >= 0 && floor(var.sleep_on_destroy) == var.sleep_on_destroy + error_message = "sleep_on_destroy must be a non-negative integer number of seconds." + } } # 6. Crash during 'terraform destroy' From bbf7bc9466c52f6a2130afc597017ef0d155a482 Mon Sep 17 00:00:00 2001 From: frmadem Date: Tue, 31 Mar 2026 12:57:30 +0200 Subject: [PATCH 3/8] Update modules/dummy/main.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- modules/dummy/main.tf | 220 +++++++++++++++++++++--------------------- 1 file changed, 112 insertions(+), 108 deletions(-) diff --git a/modules/dummy/main.tf b/modules/dummy/main.tf index 7157f517f..f71e8521c 100644 --- a/modules/dummy/main.tf +++ b/modules/dummy/main.tf @@ -1,109 +1,113 @@ -# The 'external' data source executes the script for plan-time calculations and side-effects. -# This runs during both 'plan' and 'apply'. -data "external" "script_executor" { - # Execute the node binary directly for better reliability and bypass complex shell nesting issues. - program = [ - "node", - "${path.module}/script.js" - ] - - # Pass only the variables relevant to the PLAN phase to the script - query = { - instance_name = var.instance_name - sleep_duration = var.sleep_on_plan - enable_crash = var.crash_on_plan - } -} - -# 1. Base Resource: Executes if apply logic is present, and provides an ID output. -resource "null_resource" "diagnostic_base_logic" { - triggers = { - run_always = timestamp() - } -} - -# 2. Conditional Sleep: This resource is only created if 'sleep_on_apply' > 0. -resource "null_resource" "conditional_sleep" { - count = var.sleep_on_apply > 0 ? 1 : 0 - - # Force re-creation on every apply to ensure the provisioner runs - triggers = { - run_always = timestamp() - } - - provisioner "local-exec" { - # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - - # Echo message and then sleep. We use count[0] to access the instance. - command = "echo '--- APPLY-TIME DELAY --- Sleeping for ${var.sleep_on_apply} seconds...' && sleep ${var.sleep_on_apply}" - when = create - } -} - -# 3. Conditional Crash: This resource is only created if 'crash_on_apply' is true. -resource "null_resource" "conditional_crash" { - count = var.crash_on_apply ? 1 : 0 - - # Force re-creation on every apply to ensure the provisioner runs - triggers = { - run_always = timestamp() - } - - provisioner "local-exec" { - # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - - # Echo message and then use 'exit 1' to deliberately cause failure - command = "echo '--- APPLY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" - when = create - } -} - - -# 4. Conditional Sleep on DESTROY -resource "null_resource" "conditional_destroy_sleep" { - count = var.sleep_on_destroy > 0 ? 1 : 0 - +# The 'external' data source executes the script for plan-time calculations and side-effects. +# This runs during both 'plan' and 'apply'. +data "external" "script_executor" { + # Execute the node binary directly for better reliability and bypass complex shell nesting issues. + program = [ + "node", + "${path.module}/script.js" + ] + + # Pass only the variables relevant to the PLAN phase to the script + query = { + instance_name = var.instance_name + sleep_duration = var.sleep_on_plan + enable_crash = var.crash_on_plan + } +} + +# 1. Base Resource: Executes if apply logic is present, and provides an ID output. +resource "null_resource" "diagnostic_base_logic" { + triggers = { + run_always = timestamp() + } +} + +# 2. Conditional Sleep: This resource is only created if 'sleep_on_apply' > 0. +resource "null_resource" "conditional_sleep" { + count = var.sleep_on_apply > 0 ? 1 : 0 + + # Force re-creation on every apply to ensure the provisioner runs + triggers = { + run_always = timestamp() + } + + provisioner "local-exec" { + # Executes the command using a standard sh interpreter + interpreter = ["sh", "-c"] + + # Echo message and then sleep. We use count[0] to access the instance. + command = "echo '--- APPLY-TIME DELAY --- Sleeping for ${var.sleep_on_apply} seconds...' && sleep ${var.sleep_on_apply}" + when = create + } +} + +# 3. Conditional Crash: This resource is only created if 'crash_on_apply' is true. +resource "null_resource" "conditional_crash" { + count = var.crash_on_apply ? 1 : 0 + + # Force re-creation on every apply to ensure the provisioner runs + triggers = { + run_always = timestamp() + } + + provisioner "local-exec" { + # Executes the command using a standard sh interpreter + interpreter = ["sh", "-c"] + + # Echo message and then use 'exit 1' to deliberately cause failure + command = "echo '--- APPLY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" + when = create + } +} + + +# 4. Conditional Sleep on DESTROY +resource "null_resource" "conditional_destroy_sleep" { + # Always create this resource so that destroy-time behavior is reliably available. + # The actual sleep is gated inside the destroy-time command based on 'sleep_on_destroy'. + count = 1 + # No create provisioner needed – this resource only exists to run destroy logic - provisioner "local-exec" { - when = destroy - interpreter = ["sh", "-c"] - command = "echo '--- DESTROY-TIME DELAY --- Sleeping for ${var.sleep_on_destroy} seconds...' && sleep ${var.sleep_on_destroy}" - } -} - -# 5. Conditional Crash on DESTROY -resource "null_resource" "conditional_destroy_crash" { - count = var.crash_on_destroy ? 1 : 0 - - provisioner "local-exec" { - when = destroy - interpreter = ["sh", "-c"] - command = "echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" - } -} - - -# --- Module Outputs --- - -# Output from the PLAN-TIME script -output "script_message" { - description = "The message returned by the external script (ran during plan)." - value = data.external.script_executor.result.message -} - -# Output from the PLAN-TIME script -output "script_timestamp" { - description = "The timestamp returned by the external script (ran during plan)." - value = data.external.script_executor.result.timestamp -} - -# Check to ensure the base apply-time resource was created -output "apply_resource_id" { - description = "The ID of the base null resource, indicating apply logic executed." - value = null_resource.diagnostic_base_logic.id -} - - - + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = "if [ \"${var.sleep_on_destroy}\" -gt 0 ]; then echo '--- DESTROY-TIME DELAY --- Sleeping for ${var.sleep_on_destroy} seconds...' && sleep ${var.sleep_on_destroy}; else echo '--- DESTROY-TIME DELAY --- Skipping sleep; sleep_on_destroy <= 0 ---'; fi" + } +} + +# 5. Conditional Crash on DESTROY +resource "null_resource" "conditional_destroy_crash" { + # Always create this resource so that destroy-time behavior is reliably available. + # The actual crash is gated inside the destroy-time command based on 'crash_on_destroy'. + count = 1 + + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = "if [ \"${var.crash_on_destroy}\" = \"true\" ]; then echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1; else echo '--- DESTROY-TIME CRASH --- Skipping crash; crash_on_destroy = false ---'; fi" + } +} + + +# --- Module Outputs --- + +# Output from the PLAN-TIME script +output "script_message" { + description = "The message returned by the external script (ran during plan)." + value = data.external.script_executor.result.message +} + +# Output from the PLAN-TIME script +output "script_timestamp" { + description = "The timestamp returned by the external script (ran during plan)." + value = data.external.script_executor.result.timestamp +} + +# Check to ensure the base apply-time resource was created +output "apply_resource_id" { + description = "The ID of the base null resource, indicating apply logic executed." + value = null_resource.diagnostic_base_logic.id +} + + + From 8206868115c32e753226e0875755b7dba7b981bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:58:57 +0000 Subject: [PATCH 4/8] fix: normalize line endings in main.tf (CRLF -> LF) Agent-Logs-Url: https://github.com/prefapp/tfm/sessions/0dcdb3f7-e35b-422f-8cca-4b005150b46c Co-authored-by: frmadem <6397709+frmadem@users.noreply.github.com> --- modules/dummy/main.tf | 226 +++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/modules/dummy/main.tf b/modules/dummy/main.tf index f71e8521c..2ea4cd75c 100644 --- a/modules/dummy/main.tf +++ b/modules/dummy/main.tf @@ -1,113 +1,113 @@ -# The 'external' data source executes the script for plan-time calculations and side-effects. -# This runs during both 'plan' and 'apply'. -data "external" "script_executor" { - # Execute the node binary directly for better reliability and bypass complex shell nesting issues. - program = [ - "node", - "${path.module}/script.js" - ] - - # Pass only the variables relevant to the PLAN phase to the script - query = { - instance_name = var.instance_name - sleep_duration = var.sleep_on_plan - enable_crash = var.crash_on_plan - } -} - -# 1. Base Resource: Executes if apply logic is present, and provides an ID output. -resource "null_resource" "diagnostic_base_logic" { - triggers = { - run_always = timestamp() - } -} - -# 2. Conditional Sleep: This resource is only created if 'sleep_on_apply' > 0. -resource "null_resource" "conditional_sleep" { - count = var.sleep_on_apply > 0 ? 1 : 0 - - # Force re-creation on every apply to ensure the provisioner runs - triggers = { - run_always = timestamp() - } - - provisioner "local-exec" { - # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - - # Echo message and then sleep. We use count[0] to access the instance. - command = "echo '--- APPLY-TIME DELAY --- Sleeping for ${var.sleep_on_apply} seconds...' && sleep ${var.sleep_on_apply}" - when = create - } -} - -# 3. Conditional Crash: This resource is only created if 'crash_on_apply' is true. -resource "null_resource" "conditional_crash" { - count = var.crash_on_apply ? 1 : 0 - - # Force re-creation on every apply to ensure the provisioner runs - triggers = { - run_always = timestamp() - } - - provisioner "local-exec" { - # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - - # Echo message and then use 'exit 1' to deliberately cause failure - command = "echo '--- APPLY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" - when = create - } -} - - -# 4. Conditional Sleep on DESTROY -resource "null_resource" "conditional_destroy_sleep" { - # Always create this resource so that destroy-time behavior is reliably available. - # The actual sleep is gated inside the destroy-time command based on 'sleep_on_destroy'. - count = 1 - - # No create provisioner needed – this resource only exists to run destroy logic - provisioner "local-exec" { - when = destroy - interpreter = ["sh", "-c"] - command = "if [ \"${var.sleep_on_destroy}\" -gt 0 ]; then echo '--- DESTROY-TIME DELAY --- Sleeping for ${var.sleep_on_destroy} seconds...' && sleep ${var.sleep_on_destroy}; else echo '--- DESTROY-TIME DELAY --- Skipping sleep; sleep_on_destroy <= 0 ---'; fi" - } -} - -# 5. Conditional Crash on DESTROY -resource "null_resource" "conditional_destroy_crash" { - # Always create this resource so that destroy-time behavior is reliably available. - # The actual crash is gated inside the destroy-time command based on 'crash_on_destroy'. - count = 1 - - provisioner "local-exec" { - when = destroy - interpreter = ["sh", "-c"] - command = "if [ \"${var.crash_on_destroy}\" = \"true\" ]; then echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1; else echo '--- DESTROY-TIME CRASH --- Skipping crash; crash_on_destroy = false ---'; fi" - } -} - - -# --- Module Outputs --- - -# Output from the PLAN-TIME script -output "script_message" { - description = "The message returned by the external script (ran during plan)." - value = data.external.script_executor.result.message -} - -# Output from the PLAN-TIME script -output "script_timestamp" { - description = "The timestamp returned by the external script (ran during plan)." - value = data.external.script_executor.result.timestamp -} - -# Check to ensure the base apply-time resource was created -output "apply_resource_id" { - description = "The ID of the base null resource, indicating apply logic executed." - value = null_resource.diagnostic_base_logic.id -} - - - +# The 'external' data source executes the script for plan-time calculations and side-effects. +# This runs during both 'plan' and 'apply'. +data "external" "script_executor" { + # Execute the node binary directly for better reliability and bypass complex shell nesting issues. + program = [ + "node", + "${path.module}/script.js" + ] + + # Pass only the variables relevant to the PLAN phase to the script + query = { + instance_name = var.instance_name + sleep_duration = var.sleep_on_plan + enable_crash = var.crash_on_plan + } +} + +# 1. Base Resource: Executes if apply logic is present, and provides an ID output. +resource "null_resource" "diagnostic_base_logic" { + triggers = { + run_always = timestamp() + } +} + +# 2. Conditional Sleep: This resource is only created if 'sleep_on_apply' > 0. +resource "null_resource" "conditional_sleep" { + count = var.sleep_on_apply > 0 ? 1 : 0 + + # Force re-creation on every apply to ensure the provisioner runs + triggers = { + run_always = timestamp() + } + + provisioner "local-exec" { + # Executes the command using a standard sh interpreter + interpreter = ["sh", "-c"] + + # Echo message and then sleep. We use count[0] to access the instance. + command = "echo '--- APPLY-TIME DELAY --- Sleeping for ${var.sleep_on_apply} seconds...' && sleep ${var.sleep_on_apply}" + when = create + } +} + +# 3. Conditional Crash: This resource is only created if 'crash_on_apply' is true. +resource "null_resource" "conditional_crash" { + count = var.crash_on_apply ? 1 : 0 + + # Force re-creation on every apply to ensure the provisioner runs + triggers = { + run_always = timestamp() + } + + provisioner "local-exec" { + # Executes the command using a standard sh interpreter + interpreter = ["sh", "-c"] + + # Echo message and then use 'exit 1' to deliberately cause failure + command = "echo '--- APPLY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" + when = create + } +} + + +# 4. Conditional Sleep on DESTROY +resource "null_resource" "conditional_destroy_sleep" { + # Always create this resource so that destroy-time behavior is reliably available. + # The actual sleep is gated inside the destroy-time command based on 'sleep_on_destroy'. + count = 1 + + # No create provisioner needed – this resource only exists to run destroy logic + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = "if [ \"${var.sleep_on_destroy}\" -gt 0 ]; then echo '--- DESTROY-TIME DELAY --- Sleeping for ${var.sleep_on_destroy} seconds...' && sleep ${var.sleep_on_destroy}; else echo '--- DESTROY-TIME DELAY --- Skipping sleep; sleep_on_destroy <= 0 ---'; fi" + } +} + +# 5. Conditional Crash on DESTROY +resource "null_resource" "conditional_destroy_crash" { + # Always create this resource so that destroy-time behavior is reliably available. + # The actual crash is gated inside the destroy-time command based on 'crash_on_destroy'. + count = 1 + + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = "if [ \"${var.crash_on_destroy}\" = \"true\" ]; then echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1; else echo '--- DESTROY-TIME CRASH --- Skipping crash; crash_on_destroy = false ---'; fi" + } +} + + +# --- Module Outputs --- + +# Output from the PLAN-TIME script +output "script_message" { + description = "The message returned by the external script (ran during plan)." + value = data.external.script_executor.result.message +} + +# Output from the PLAN-TIME script +output "script_timestamp" { + description = "The timestamp returned by the external script (ran during plan)." + value = data.external.script_executor.result.timestamp +} + +# Check to ensure the base apply-time resource was created +output "apply_resource_id" { + description = "The ID of the base null resource, indicating apply logic executed." + value = null_resource.diagnostic_base_logic.id +} + + + From 5e02d34d15f5b410a0522eda1e4e136338dd5b95 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:48:51 +0000 Subject: [PATCH 5/8] docs: add sleep_on_destroy and crash_on_destroy to README Inputs table Agent-Logs-Url: https://github.com/prefapp/tfm/sessions/82b9221c-3a39-4398-88ac-681579282c9e Co-authored-by: frmadem <6397709+frmadem@users.noreply.github.com> --- modules/dummy/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/dummy/README.md b/modules/dummy/README.md index 773a0170d..46762ac76 100644 --- a/modules/dummy/README.md +++ b/modules/dummy/README.md @@ -56,6 +56,8 @@ output "plan_status" { | `crash_on_plan` | If set to true, forces `terraform plan` to fail immediately. | `bool` | `false` | Plan | | `sleep_on_apply` | Duration in seconds to delay the `terraform apply` execution. | `number` | `0` | Apply | | `crash_on_apply` | If set to true, forces `terraform apply` to fail immediately. | `bool` | `false` | Apply | +| `sleep_on_destroy` | Duration in seconds to delay the `terraform destroy` execution. Must be a non-negative integer. | `number` | `0` | Destroy | +| `crash_on_destroy` | If set to true, forces `terraform destroy` to fail immediately. | `bool` | `false` | Destroy | ## Outputs From 3f1d4426b5f0407005c2bf1698e55ce4b7ee6877 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:38:42 +0000 Subject: [PATCH 6/8] feat(dummy): make module compliant with CONTRIBUTING.md guide Agent-Logs-Url: https://github.com/prefapp/tfm/sessions/8c483352-1ba3-4d90-95cd-287c8a014c3b Co-authored-by: frmadem <6397709+frmadem@users.noreply.github.com> --- modules/dummy/.terraform-docs.yml | 48 +++++++++++ modules/dummy/README.md | 110 +------------------------- modules/dummy/_examples/basic/main.tf | 29 +++++++ modules/dummy/docs/footer.md | 15 ++++ modules/dummy/docs/header.md | 63 +++++++++++++++ modules/dummy/main.tf | 22 ------ modules/dummy/outputs.tf | 14 ++++ 7 files changed, 171 insertions(+), 130 deletions(-) create mode 100644 modules/dummy/.terraform-docs.yml create mode 100644 modules/dummy/_examples/basic/main.tf create mode 100644 modules/dummy/docs/footer.md create mode 100644 modules/dummy/docs/header.md create mode 100644 modules/dummy/outputs.tf diff --git a/modules/dummy/.terraform-docs.yml b/modules/dummy/.terraform-docs.yml new file mode 100644 index 000000000..3a69365ff --- /dev/null +++ b/modules/dummy/.terraform-docs.yml @@ -0,0 +1,48 @@ +formatter: "markdown" + +version: "" + +header-from: docs/header.md +footer-from: docs/footer.md + +recursive: + enabled: false + path: modules + include-main: true + +sections: + hide: [] + show: [] + +content: "" + +output: + file: "README.md" + mode: inject + template: |- + + {{ .Content }} + + +output-values: + enabled: false + from: "" + +sort: + enabled: true + by: name + +settings: + anchor: true + color: true + default: true + description: false + escape: true + hide-empty: false + html: true + indent: 2 + lockfile: true + read-comments: true + required: true + sensitive: true + type: true diff --git a/modules/dummy/README.md b/modules/dummy/README.md index 46762ac76..7ddc91d71 100644 --- a/modules/dummy/README.md +++ b/modules/dummy/README.md @@ -1,109 +1,3 @@ -# Terraform Diagnostic Executor Module - -This module is designed for DevOps testing and CI/CD pipeline validation. It provides features to simulate network latency (sleep) and catastrophic failures (crash) at both the terraform plan and terraform apply stages. - -It leverages the external data source (for plan-time checks) and null_resource with local-exec provisioners (for apply-time actions). - -## Prerequisites - -- Terraform: Required version ~> 1.0. - -- Node.js: Must be installed and accessible via the PATH on the machine running terraform plan and terraform apply. - -- Required Providers: hashicorp/external and hashicorp/null. - -## Module Usage - -The module requires four primary boolean and numeric variables to control its diagnostic behavior. - -Example - -```terraform -module "diagnostic_test" { - source = "./node-executor" - - # Base input - instance_name = "ci-pipeline-check" - - # PLAN TIME CONTROLS - sleep_on_plan = 5 # Delays 'terraform plan' by 5 seconds - crash_on_plan = false # Set to true to force 'terraform plan' failure - - # APPLY TIME CONTROLS - sleep_on_apply = 10 # Delays 'terraform apply' by 10 seconds - crash_on_apply = false # Set to true to force 'terraform apply' failure - - # DESTROY TIME CONTROLS - sleep_on_destroy = 15 # Delays 'terraform destroy' by 15 seconds - crash_on_destroy = false # Set to true to force 'terraform destroy' failure - -} - -output "plan_status" { - value = module.diagnostic_test.plan_message -} -``` - - -## Inputs - -## Inputs - -| Name | Description | Type | Default | Control Phase | -|---|---|---|---|---| -| `instance_name` | A unique identifier for the diagnostic run. | `string` | `"default-test"` | Both | -| `sleep_on_plan` | Duration in seconds to delay the `terraform plan` execution. | `number` | `0` | Plan | -| `crash_on_plan` | If set to true, forces `terraform plan` to fail immediately. | `bool` | `false` | Plan | -| `sleep_on_apply` | Duration in seconds to delay the `terraform apply` execution. | `number` | `0` | Apply | -| `crash_on_apply` | If set to true, forces `terraform apply` to fail immediately. | `bool` | `false` | Apply | -| `sleep_on_destroy` | Duration in seconds to delay the `terraform destroy` execution. Must be a non-negative integer. | `number` | `0` | Destroy | -| `crash_on_destroy` | If set to true, forces `terraform destroy` to fail immediately. | `bool` | `false` | Destroy | - - -## Outputs - -| Name | Description | Value Source | -| ----- | ----- | ----- | -| `plan_message` | A status message from the plan-time script execution. | `data.external` | -| `plan_timestamp` | The timestamp when the plan-time script executed. | `data.external` | -| `plan_slept_for_s` | The actual seconds the script slept during plan. | `data.external` | -| `plan_crashed` | Status indicating if the crash logic was executed during plan. | `data.external` | - - -## Diagnostic Scenarios - -### 1. Test Plan Failure - -To test how your CI/CD pipeline handles a plan failure: - -```terraform -# Set crash_on_plan to true via command line -terraform plan -var="crash_on_plan=true" -``` - - - -Expected Result: The terraform plan command will exit with a non-zero exit code (failure) as the data "external" source executes the Node.js script, which calls process.exit(1). - -### 2. Test Apply Failure - -To test infrastructure deployment failure: - -```terraform -# Set crash_on_apply to true via command line -terraform apply -auto-approve -var="crash_on_apply=true" -``` - -Expected Result: The terraform apply command will fail when it attempts to create the null_resource.diagnostic_crash_logic because its local-exec provisioner executes the exit 1 shell command. - -### 3. Test Apply Timeout/Latency - -To test pipeline timeouts during execution: - -``` -# Set a 30 second delay during the apply phase -terraform apply -auto-approve -var="sleep_on_apply=30" -``` - - + + diff --git a/modules/dummy/_examples/basic/main.tf b/modules/dummy/_examples/basic/main.tf new file mode 100644 index 000000000..d8d3cb816 --- /dev/null +++ b/modules/dummy/_examples/basic/main.tf @@ -0,0 +1,29 @@ +# Basic example: simulate a 5-second delay during apply. +# Useful for validating CI/CD pipeline timeout and failure-handling logic. +# Set crash_on_destroy = true to test destroy-time failure behavior. + +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + # PLAN TIME CONTROLS + sleep_on_plan = 0 + crash_on_plan = false + + # APPLY TIME CONTROLS + sleep_on_apply = 5 # Delays 'terraform apply' by 5 seconds + crash_on_apply = false + + # DESTROY TIME CONTROLS + sleep_on_destroy = 0 + crash_on_destroy = false # Set to true to force 'terraform destroy' failure +} + +output "script_message" { + value = module.dummy.script_message +} + +output "apply_resource_id" { + value = module.dummy.apply_resource_id +} diff --git a/modules/dummy/docs/footer.md b/modules/dummy/docs/footer.md new file mode 100644 index 000000000..38fafb99a --- /dev/null +++ b/modules/dummy/docs/footer.md @@ -0,0 +1,15 @@ +## Examples + +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples): + +- [Basic](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples/basic) - Minimal configuration showing plan, apply, and destroy controls + +## Resources + +- **Terraform null provider**: [https://registry.terraform.io/providers/hashicorp/null/latest](https://registry.terraform.io/providers/hashicorp/null/latest) +- **Terraform external provider**: [https://registry.terraform.io/providers/hashicorp/external/latest](https://registry.terraform.io/providers/hashicorp/external/latest) +- **Terraform local-exec provisioner**: [https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec](https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec) + +## Support + +For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues). diff --git a/modules/dummy/docs/header.md b/modules/dummy/docs/header.md new file mode 100644 index 000000000..08d599f6f --- /dev/null +++ b/modules/dummy/docs/header.md @@ -0,0 +1,63 @@ +# **Dummy Terraform Module** + +## Overview + +This Terraform module is designed for DevOps testing and CI/CD pipeline validation. It provides controls to simulate latency and failures at every phase of the Terraform lifecycle — plan, apply, and destroy — without affecting real infrastructure. + +The module uses the `external` data source to execute a Node.js script for plan-time diagnostics and `null_resource` with `local-exec` provisioners for apply-time and destroy-time behavior. This makes it easy to reproduce slow or failing pipeline runs in a controlled, repeatable way. + +Typical use cases include testing CI/CD pipeline timeouts, verifying failure-handling logic, and validating destroy-phase behavior in automated workflows. + +## Key Features + +- **Plan-time controls**: Simulate slow or failing `terraform plan` runs via a Node.js script executed by the `external` data source. +- **Apply-time controls**: Introduce configurable delays or intentional failures during `terraform apply` using `null_resource` provisioners. +- **Destroy-time controls**: Reliably gate sleep and crash behavior during `terraform destroy`, always present in state so destroy provisioners execute as expected. +- **Deterministic destroy ordering**: The crash resource depends on the sleep resource, ensuring sleep always runs before crash when both are enabled. +- **Input validation**: The `sleep_on_destroy` variable is validated to require a non-negative integer, preventing silent misconfigurations. + +## Basic Usage + +### Simulate apply-time delay and destroy-time failure + +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + sleep_on_apply = 10 + crash_on_destroy = true +} +``` + +### Simulate plan-time crash and destroy-time delay + +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + crash_on_plan = true + sleep_on_destroy = 30 +} +``` + +## File Structure + +``` +dummy/ +├── .terraform-docs.yml # terraform-docs configuration +├── README.md # Auto-generated documentation +├── main.tf # Module resources and provisioners +├── variables.tf # Input variable definitions +├── outputs.tf # Output value definitions +├── script.js # Node.js script for plan-time diagnostics +├── _examples/ # Usage examples +│ └── basic/ # Basic configuration example +│ └── main.tf +└── docs/ # Documentation source files + ├── header.md # This file + └── footer.md # Additional resources and support +``` diff --git a/modules/dummy/main.tf b/modules/dummy/main.tf index 2ea4cd75c..acd7d49ee 100644 --- a/modules/dummy/main.tf +++ b/modules/dummy/main.tf @@ -89,25 +89,3 @@ resource "null_resource" "conditional_destroy_crash" { } -# --- Module Outputs --- - -# Output from the PLAN-TIME script -output "script_message" { - description = "The message returned by the external script (ran during plan)." - value = data.external.script_executor.result.message -} - -# Output from the PLAN-TIME script -output "script_timestamp" { - description = "The timestamp returned by the external script (ran during plan)." - value = data.external.script_executor.result.timestamp -} - -# Check to ensure the base apply-time resource was created -output "apply_resource_id" { - description = "The ID of the base null resource, indicating apply logic executed." - value = null_resource.diagnostic_base_logic.id -} - - - diff --git a/modules/dummy/outputs.tf b/modules/dummy/outputs.tf new file mode 100644 index 000000000..4d5cb96d5 --- /dev/null +++ b/modules/dummy/outputs.tf @@ -0,0 +1,14 @@ +output "script_message" { + description = "The message returned by the external script (ran during plan)." + value = data.external.script_executor.result.message +} + +output "script_timestamp" { + description = "The timestamp returned by the external script (ran during plan)." + value = data.external.script_executor.result.timestamp +} + +output "apply_resource_id" { + description = "The ID of the base null resource, indicating apply logic executed." + value = null_resource.diagnostic_base_logic.id +} From 291a1aa8f13f279867c1689f7701f802548adf4c Mon Sep 17 00:00:00 2001 From: frmadem Date: Wed, 1 Apr 2026 18:06:37 +0200 Subject: [PATCH 7/8] Generated README --- modules/dummy/README.md | 125 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/modules/dummy/README.md b/modules/dummy/README.md index 7ddc91d71..4985cad71 100644 --- a/modules/dummy/README.md +++ b/modules/dummy/README.md @@ -1,3 +1,128 @@ +# **Dummy Terraform Module** + +## Overview + +This Terraform module is designed for DevOps testing and CI/CD pipeline validation. It provides controls to simulate latency and failures at every phase of the Terraform lifecycle — plan, apply, and destroy — without affecting real infrastructure. + +The module uses the `external` data source to execute a Node.js script for plan-time diagnostics and `null_resource` with `local-exec` provisioners for apply-time and destroy-time behavior. This makes it easy to reproduce slow or failing pipeline runs in a controlled, repeatable way. + +Typical use cases include testing CI/CD pipeline timeouts, verifying failure-handling logic, and validating destroy-phase behavior in automated workflows. + +## Key Features + +- **Plan-time controls**: Simulate slow or failing `terraform plan` runs via a Node.js script executed by the `external` data source. +- **Apply-time controls**: Introduce configurable delays or intentional failures during `terraform apply` using `null_resource` provisioners. +- **Destroy-time controls**: Reliably gate sleep and crash behavior during `terraform destroy`, always present in state so destroy provisioners execute as expected. +- **Deterministic destroy ordering**: The crash resource depends on the sleep resource, ensuring sleep always runs before crash when both are enabled. +- **Input validation**: The `sleep_on_destroy` variable is validated to require a non-negative integer, preventing silent misconfigurations. + +## Basic Usage + +### Simulate apply-time delay and destroy-time failure + +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + sleep_on_apply = 10 + crash_on_destroy = true +} +``` + +### Simulate plan-time crash and destroy-time delay + +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + crash_on_plan = true + sleep_on_destroy = 30 +} +``` + +## File Structure + +``` +dummy/ +├── .terraform-docs.yml # terraform-docs configuration +├── README.md # Auto-generated documentation +├── main.tf # Module resources and provisioners +├── variables.tf # Input variable definitions +├── outputs.tf # Output value definitions +├── script.js # Node.js script for plan-time diagnostics +├── _examples/ # Usage examples +│ └── basic/ # Basic configuration example +│ └── main.tf +└── docs/ # Documentation source files + ├── header.md # This file + └── footer.md # Additional resources and support +``` + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [external](#provider\_external) | n/a | +| [null](#provider\_null) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [null_resource.conditional_crash](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.conditional_destroy_crash](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.conditional_destroy_sleep](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.conditional_sleep](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.diagnostic_base_logic](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [external_external.script_executor](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [crash\_on\_apply](#input\_crash\_on\_apply) | Set to true to force a non-zero exit code during the 'apply' phase. | `bool` | `false` | no | +| [crash\_on\_destroy](#input\_crash\_on\_destroy) | Set to true to force a non-zero exit code during the 'destroy' phase. | `bool` | `false` | no | +| [crash\_on\_plan](#input\_crash\_on\_plan) | Set to true to force a non-zero exit code during the 'plan' phase. | `bool` | `false` | no | +| [instance\_name](#input\_instance\_name) | A unique identifier for this module instance. | `string` | n/a | yes | +| [sleep\_on\_apply](#input\_sleep\_on\_apply) | Number of seconds to sleep during the 'apply' phase. | `number` | `0` | no | +| [sleep\_on\_destroy](#input\_sleep\_on\_destroy) | Number of seconds to sleep during the 'destroy' phase. | `number` | `0` | no | +| [sleep\_on\_plan](#input\_sleep\_on\_plan) | Number of seconds to sleep during the 'plan' phase. | `number` | `0` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [apply\_resource\_id](#output\_apply\_resource\_id) | The ID of the base null resource, indicating apply logic executed. | +| [script\_message](#output\_script\_message) | The message returned by the external script (ran during plan). | +| [script\_timestamp](#output\_script\_timestamp) | The timestamp returned by the external script (ran during plan). | + +## Examples + +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples): + +- [Basic](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples/basic) - Minimal configuration showing plan, apply, and destroy controls + +## Resources + +- **Terraform null provider**: [https://registry.terraform.io/providers/hashicorp/null/latest](https://registry.terraform.io/providers/hashicorp/null/latest) +- **Terraform external provider**: [https://registry.terraform.io/providers/hashicorp/external/latest](https://registry.terraform.io/providers/hashicorp/external/latest) +- **Terraform local-exec provisioner**: [https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec](https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec) + +## Support + +For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues). From 0acd4a0a31c92526eedd30cfbf9cabd33edcc87d Mon Sep 17 00:00:00 2001 From: frmadem Date: Wed, 1 Apr 2026 18:19:56 +0200 Subject: [PATCH 8/8] Fix --- modules/dummy/main.tf | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/modules/dummy/main.tf b/modules/dummy/main.tf index acd7d49ee..9d34dca0e 100644 --- a/modules/dummy/main.tf +++ b/modules/dummy/main.tf @@ -60,32 +60,46 @@ resource "null_resource" "conditional_crash" { } } - # 4. Conditional Sleep on DESTROY resource "null_resource" "conditional_destroy_sleep" { - # Always create this resource so that destroy-time behavior is reliably available. - # The actual sleep is gated inside the destroy-time command based on 'sleep_on_destroy'. count = 1 - # No create provisioner needed – this resource only exists to run destroy logic + triggers = { + sleep_on_destroy = var.sleep_on_destroy + } + provisioner "local-exec" { when = destroy interpreter = ["sh", "-c"] - command = "if [ \"${var.sleep_on_destroy}\" -gt 0 ]; then echo '--- DESTROY-TIME DELAY --- Sleeping for ${var.sleep_on_destroy} seconds...' && sleep ${var.sleep_on_destroy}; else echo '--- DESTROY-TIME DELAY --- Skipping sleep; sleep_on_destroy <= 0 ---'; fi" + command = <<-EOT + if [ "${self.triggers.sleep_on_destroy}" -gt 0 ]; then + echo '--- DESTROY-TIME DELAY --- Sleeping for ${self.triggers.sleep_on_destroy} seconds...' + sleep ${self.triggers.sleep_on_destroy} + else + echo '--- DESTROY-TIME DELAY --- Skipping sleep; sleep_on_destroy <= 0 ---' + fi + EOT } } # 5. Conditional Crash on DESTROY resource "null_resource" "conditional_destroy_crash" { - # Always create this resource so that destroy-time behavior is reliably available. - # The actual crash is gated inside the destroy-time command based on 'crash_on_destroy'. count = 1 + triggers = { + crash_on_destroy = var.crash_on_destroy + } + provisioner "local-exec" { when = destroy interpreter = ["sh", "-c"] - command = "if [ \"${var.crash_on_destroy}\" = \"true\" ]; then echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1; else echo '--- DESTROY-TIME CRASH --- Skipping crash; crash_on_destroy = false ---'; fi" + command = <<-EOT + if [ "${self.triggers.crash_on_destroy}" = "true" ]; then + echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' + exit 1 + else + echo '--- DESTROY-TIME CRASH --- Skipping crash; crash_on_destroy = false ---' + fi + EOT } } - -