diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 271d828..e0b1533 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -9,9 +9,10 @@ jobs: steps: - name: Generate Matrix id: matrix - uses: Invicton-Labs/terraform-module-testing/matrix@v0.2.0 + uses: Invicton-Labs/terraform-module-testing/matrix@v0.3.0 with: - minimum_tf_version: '1.9.0' + minimum_tf_version: '1.10.3' + additional_runners: 'macos-13, windows-2019' - name: Output Matrix run: | @@ -28,25 +29,119 @@ jobs: steps: - name: Initialize - Pass id: init-pass - uses: Invicton-Labs/terraform-module-testing/initialize@v0.2.0 + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 with: tf_path: tests/pass - name: Run Tests - Pass id: tests-pass - uses: Invicton-Labs/terraform-module-testing/apply-destroy@v0.2.0 + uses: Invicton-Labs/terraform-module-testing/apply-destroy@v0.3.0 with: tf_path: tests/pass + - name: Initialize - Pass (With Output) + id: init-pass-output + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/pass-output + - name: Run Tests - Pass (With Output) + id: tests-pass-output + uses: Invicton-Labs/terraform-module-testing/apply-destroy@v0.3.0 + with: + tf_path: tests/pass-output + + - name: Initialize - Pass - Condition Delayed + id: init-pass-condition-delayed + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/pass-condition-delayed + - name: Run Tests - Pass - Condition Delayed + id: tests-pass-condition-delayed + uses: Invicton-Labs/terraform-module-testing/apply-destroy@v0.3.0 + with: + tf_path: tests/pass-condition-delayed + + - name: Initialize - Pass - Message Delayed + id: init-pass-message-delayed + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/pass-message-delayed + - name: Run Tests - Pass - Message Delayed + id: tests-pass-message-delayed + uses: Invicton-Labs/terraform-module-testing/apply-destroy@v0.3.0 + with: + tf_path: tests/pass-message-delayed + stderr_contains: Unsuitable value for error message + + - name: Initialize - Pass - Condition & Message Delayed + id: init-pass-condition-message-delayed + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/pass-condition-message-delayed + - name: Run Tests - Pass - Condition & Message Delayed + id: tests-pass-condition-message-delayed + uses: Invicton-Labs/terraform-module-testing/apply-destroy@v0.3.0 + with: + tf_path: tests/pass-condition-message-delayed + - name: Initialize - Fail id: init-fail - uses: Invicton-Labs/terraform-module-testing/initialize@v0.2.0 + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 with: tf_path: tests/fail - name: Run Tests - Fail id: tests-fail - uses: Invicton-Labs/terraform-module-testing/apply-failure@v0.2.0 + uses: Invicton-Labs/terraform-module-testing/apply-failure@v0.3.0 with: tf_path: tests/fail + stderr_contains: sample error + + - name: Initialize - Fail (Output) + id: init-fail-output + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/fail-output + - name: Run Tests - Fail (Output) + id: tests-fail-output + uses: Invicton-Labs/terraform-module-testing/apply-failure@v0.3.0 + with: + tf_path: tests/fail-output + stderr_contains: sample error + + - name: Initialize - Fail - Condition Delayed + id: init-fail-condition-delayed + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/fail-condition-delayed + - name: Run Tests - Fail - Condition Delayed + id: tests-fail-condition-delayed + uses: Invicton-Labs/terraform-module-testing/apply-failure@v0.3.0 + with: + tf_path: tests/fail-condition-delayed + stderr_contains: sample error + + - name: Initialize - Fail - Message Delayed + id: init-fail-message-delayed + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/fail-message-delayed + - name: Run Tests - Fail - Message Delayed + id: tests-fail-message-delayed + uses: Invicton-Labs/terraform-module-testing/apply-failure@v0.3.0 + with: + tf_path: tests/fail-message-delayed + stderr_contains: Unsuitable value for error message + + - name: Initialize - Fail - Condition & Message Delayed + id: init-fail-condition-message-delayed + uses: Invicton-Labs/terraform-module-testing/initialize@v0.3.0 + with: + tf_path: tests/fail-condition-message-delayed + - name: Run Tests - Fail - Condition & Message Delayed + id: tests-fail-condition-message-delayed + uses: Invicton-Labs/terraform-module-testing/apply-failure@v0.3.0 + with: + tf_path: tests/fail-condition-message-delayed + stderr_contains: sample error # This job just waits for all other jobs to pass. We have it here # so our branch protection rule can reference a single job, instead diff --git a/.gitignore b/.gitignore index 7a3e2fd..3017b18 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ override.tf.json # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan # example: *tfplan* + +/tests/**/.terraform.lock.hcl \ No newline at end of file diff --git a/README.md b/README.md index 4649d15..09454f6 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,25 @@ var.error_message is "This Terraform configuration can only be run on Unix-based // Ignore this part There is no function named "SEE_ABOVE_ERROR_MESSAGE". ``` + +## Plan, Apply, and Unknown Values + +There are several edge cases regarding conditions that aren't known until apply and error messages that aren't known until apply. + + +### Conditions not known during planning + +When the `condition` variable doesn't have a known value during the plan step, the assertion will not be checked during the plan step. The `error_message` variable will not be used or evaluated. + +The `condition` _will_ still be checked during the apply step, and the `error_message` variable will be used if `condition` evaluates to `false`. + + +### Error messages not known during planning + +When the `error_message` variable doesn't have a known value during the plan step, the behaviour will vary depending on whether the `condition` value is known during the plan step. + +If `condition` is not known during planning, then the `error_message` will not be evaluated/used until the apply step, and only if `condition` evaluates to `false`. + +If the `condition` _is_ known during planning and it evaluates to `false`, then the Terraform operation will fail during the plan step and the output error message will explain that the `error_message` variable value is invalid (not known). + +If `condition` evaluates to `true` as soon as it's known, then `error_message` will never be used or evaluated, so it doesn't matter if it's known or not. diff --git a/tests/fail-condition-delayed/main.tf b/tests/fail-condition-delayed/main.tf new file mode 100644 index 0000000..2965a87 --- /dev/null +++ b/tests/fail-condition-delayed/main.tf @@ -0,0 +1,6 @@ +module "fail" { + source = "../../" + // Use a condition that isn't known until the apply step + condition = uuid() == "" + error_message = "sample error" +} diff --git a/tests/fail-condition-message-delayed/main.tf b/tests/fail-condition-message-delayed/main.tf new file mode 100644 index 0000000..a8527d4 --- /dev/null +++ b/tests/fail-condition-message-delayed/main.tf @@ -0,0 +1,7 @@ +module "fail" { + source = "../../" + // Use a condition that isn't known until the apply step + condition = uuid() == "" + // Use a message that isn't known until the apply step + error_message = "sample error: ${uuid()}" +} diff --git a/tests/fail-message-delayed/main.tf b/tests/fail-message-delayed/main.tf new file mode 100644 index 0000000..f711e3f --- /dev/null +++ b/tests/fail-message-delayed/main.tf @@ -0,0 +1,6 @@ +module "fail" { + source = "../../" + condition = false + // Use a message that isn't known until the apply step + error_message = "sample error: ${uuid()}" +} diff --git a/tests/fail-output/.terraform.lock.hcl b/tests/fail-output/.terraform.lock.hcl new file mode 100644 index 0000000..db7a7c3 --- /dev/null +++ b/tests/fail-output/.terraform.lock.hcl @@ -0,0 +1,22 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/cloudinit" { + version = "2.3.2" + constraints = ">= 2.3.1" + hashes = [ + "h1:2jb+BfT5T96dXxUD2LQ6MtVHpXErd7ZybmMvdWE2jd4=", + "zh:2487e498736ed90f53de8f66fe2b8c05665b9f8ff1506f751c5ee227c7f457d1", + "zh:3d8627d142942336cf65eea6eb6403692f47e9072ff3fa11c3f774a3b93130b3", + "zh:434b643054aeafb5df28d5529b72acc20c6f5ded24decad73b98657af2b53f4f", + "zh:436aa6c2b07d82aa6a9dd746a3e3a627f72787c27c80552ceda6dc52d01f4b6f", + "zh:458274c5aabe65ef4dbd61d43ce759287788e35a2da004e796373f88edcaa422", + "zh:54bc70fa6fb7da33292ae4d9ceef5398d637c7373e729ed4fce59bd7b8d67372", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:893ba267e18749c1a956b69be569f0d7bc043a49c3a0eb4d0d09a8e8b2ca3136", + "zh:95493b7517bce116f75cdd4c63b7c82a9d0d48ec2ef2f5eb836d262ef96d0aa7", + "zh:9ae21ab393be52e3e84e5cce0ef20e690d21f6c10ade7d9d9d22b39851bfeddc", + "zh:cc3b01ac2472e6d59358d54d5e4945032efbc8008739a6d4946ca1b621a16040", + "zh:f23bfe9758f06a1ec10ea3a81c9deedf3a7b42963568997d84a5153f35c5839a", + ] +} diff --git a/tests/fail-output/main.tf b/tests/fail-output/main.tf new file mode 100644 index 0000000..16ca369 --- /dev/null +++ b/tests/fail-output/main.tf @@ -0,0 +1,11 @@ +module "fail" { + source = "../../" + condition = false + error_message = "sample error" +} + +// Force the module and conditions to be evaluated +// by using the module output in a config output. +output "result" { + value = module.fail.checked +} diff --git a/tests/pass-condition-delayed/main.tf b/tests/pass-condition-delayed/main.tf new file mode 100644 index 0000000..75a20a3 --- /dev/null +++ b/tests/pass-condition-delayed/main.tf @@ -0,0 +1,6 @@ +module "pass" { + source = "../../" + // Use a condition that isn't known until the apply step + condition = uuid() != "" + error_message = "sample error" +} diff --git a/tests/pass-condition-message-delayed/main.tf b/tests/pass-condition-message-delayed/main.tf new file mode 100644 index 0000000..2760912 --- /dev/null +++ b/tests/pass-condition-message-delayed/main.tf @@ -0,0 +1,7 @@ +module "pass" { + source = "../../" + // Use a condition that isn't known until the apply step + condition = uuid() != "" + // Use a message that isn't known until the apply step + error_message = "sample error: ${uuid()}" +} diff --git a/tests/pass-message-delayed/main.tf b/tests/pass-message-delayed/main.tf new file mode 100644 index 0000000..9a526eb --- /dev/null +++ b/tests/pass-message-delayed/main.tf @@ -0,0 +1,6 @@ +module "pass" { + source = "../../" + condition = true + // Use a message that isn't known until the apply step + error_message = "sample error: ${uuid()}" +} diff --git a/tests/pass-output/.terraform.lock.hcl b/tests/pass-output/.terraform.lock.hcl new file mode 100644 index 0000000..db7a7c3 --- /dev/null +++ b/tests/pass-output/.terraform.lock.hcl @@ -0,0 +1,22 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/cloudinit" { + version = "2.3.2" + constraints = ">= 2.3.1" + hashes = [ + "h1:2jb+BfT5T96dXxUD2LQ6MtVHpXErd7ZybmMvdWE2jd4=", + "zh:2487e498736ed90f53de8f66fe2b8c05665b9f8ff1506f751c5ee227c7f457d1", + "zh:3d8627d142942336cf65eea6eb6403692f47e9072ff3fa11c3f774a3b93130b3", + "zh:434b643054aeafb5df28d5529b72acc20c6f5ded24decad73b98657af2b53f4f", + "zh:436aa6c2b07d82aa6a9dd746a3e3a627f72787c27c80552ceda6dc52d01f4b6f", + "zh:458274c5aabe65ef4dbd61d43ce759287788e35a2da004e796373f88edcaa422", + "zh:54bc70fa6fb7da33292ae4d9ceef5398d637c7373e729ed4fce59bd7b8d67372", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:893ba267e18749c1a956b69be569f0d7bc043a49c3a0eb4d0d09a8e8b2ca3136", + "zh:95493b7517bce116f75cdd4c63b7c82a9d0d48ec2ef2f5eb836d262ef96d0aa7", + "zh:9ae21ab393be52e3e84e5cce0ef20e690d21f6c10ade7d9d9d22b39851bfeddc", + "zh:cc3b01ac2472e6d59358d54d5e4945032efbc8008739a6d4946ca1b621a16040", + "zh:f23bfe9758f06a1ec10ea3a81c9deedf3a7b42963568997d84a5153f35c5839a", + ] +} diff --git a/tests/pass-output/main.tf b/tests/pass-output/main.tf new file mode 100644 index 0000000..0ec99a3 --- /dev/null +++ b/tests/pass-output/main.tf @@ -0,0 +1,11 @@ +module "pass" { + source = "../../" + condition = true + error_message = "sample error" +} + +// Force the module and conditions to be evaluated +// by using the module output in a config output. +output "result" { + value = module.pass.checked +} diff --git a/variables.tf b/variables.tf index 0350283..e99decf 100644 --- a/variables.tf +++ b/variables.tf @@ -12,7 +12,7 @@ variable "condition" { // until var.error_message is known. Otherwise, it can fail during the validation // phase but won't output the proper error message. // https://github.com/hashicorp/terraform/issues/35397 - condition = var.error_message != null && var.condition == true + condition = var.error_message == "" ? var.condition : var.condition error_message = var.error_message } } diff --git a/versions.tf b/versions.tf index bfb98ab..12bb31a 100644 --- a/versions.tf +++ b/versions.tf @@ -1,3 +1,3 @@ terraform { - required_version = ">= 1.9.0" + required_version = ">= 1.10.3" }