-
Notifications
You must be signed in to change notification settings - Fork 9.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to raise an error #15469
Comments
Hi @timgurney-ee! Thanks for this suggestion. It sounds like what you're looking to do here is to guarantee certain invariants that your configuration depends on and prevent Terraform from trying to process a configuration if those invariants don't hold. I've thought before about the idea of having a way to make "assertions" in the Terraform config that get tested before Terraform will take any further action. For example (made-up syntax here just for illustration purposes): require "matching_arrays" {
test = "${length(var.array1) == length(var.array2)}"
message = "array1 and array2 must be the same length"
} I'd imagined this working by creating a new node in the graph representing the requirement, and then visiting that node during all graph walks. If the test expression returns In the above example where only The following case is trickier: require "distinct_foo_and_baz" {
test = "${aws_instance.foo.id != aws_instance.baz.id}"
message = "instances foo and baz must be distinct"
} This is a contrived example, but it illustrates a case where we'd be unable to return the error until after both instances are created in the Another part of this would be defining which resources should only be processed if the invariant is satisfied. This could be achieved by a new special node type in resource "aws_instance" "bar" {
# ... (normal attributes) ...
depends_on = [
"require.matching_arrays",
"require.distinct_foo_and_baz",
]
} This would then let Terraform know that it mustn't try to real with this instance until after the requirement has been checked. Without this, Terraform's normal concurrent processing of resources could allow the instance to get processed before the assertion is processed, in the event that the assertion is being processed at apply time due to referring to other computed resource attributes. This is all just a sketch for now. Not sure what this would actually look like, but I'm curious to hear if you think the above would help solve the problem you're trying to solve. I think a first-class block would work better for this than an interpolation function because it gives us this ability to control the processing order via normal dependencies, which would be much harder with an interpolation function. |
I think you have definitely hit on what I was trying to do with the suggestion. For the use case I was thinking of it would normally be known vars however the extended examples with interpolated results also make a lot of sense but also highlight the complexity of the problem. The overall descriptions would solve the problem I am looking at, assert is effectively what I am thinking of. I didn't take my thoughts as deep or as detailed you, as I was only considering a smaller subset of issues. I would be happy if this came in in stages, maying stage one just being pre-defined vars with values, and then maybe extending it to handle interpolation at a later stage? |
Hey guys, I found a way today how you can hack in asserts into the current version of Terraform. TL;DR variable "environment_list" {
description = "Environment ID"
type = "list"
default = ["dev", "qa", "prod"]
}
variable "env" {
description = "Which environment do you want (options: dev, qa, prod):"
}
resource "null_resource" "is_environment_name_valid" {
count = "${contains(var.environment_list, var.env) == true ? 0 : 1}"
"ERROR: The env value can only be: dev, qa or prod" = true
} or to use your test: resource "null_resource" "is_array_length_correct" {
count = "${length(var.array1) == length(var.array2) ? 0 : 1}"
"array1 and array2 must be the same length" = true
} |
Nice work around! |
Based on discussion in #16848, it would be valuable for it to be possible to raise errors even when terraform is run using If the |
Why not using a data source from a provider instead ? An assert provider with an assert_equals data source for exemple (can be extended later if required) ? We can still create explicit dependencies if necessary. Main advantage I see is that most data sources can be checked during the refresh phase for sanity checks (not resource dependent). It also allows to reuse existing terraform logic without adding new grammar to the language. |
Does this hack still work with terraform v0.11.10. I keep getting the invalid key error every single time when I run terraform plan. Is this the expected behavior? |
@nandac It doesn't work for me either. I've got around it by conditionally executing a failing command with resource "null_resource" "dns_check" {
count = "${data.external.check_dns_setup.result.valid == true ? 0 : 1}"
provisioner "local-exec" {
command = "false"
interpreter = ["bash", "-c"]
}
} |
Hey everyone, I was using @Jamie-BitFlight's workaround but it does not work anymore on Terraform 0.12. I also tried the "local-exec" workaround but it's only executed when applying which is too late for me. I found a similar approach that works with Terraform 0.12. I first tried with an empty string: resource "null_resource" "assert_something" {
triggers = <my_assertion> ? {} : ""
} But Terraform want the ternary to be consistent with types. i got this error:
Then i tried with the "file" function and it worked: resource "null_resource" "assert_something" {
triggers = <my_assertion> ? {} : file("ERROR: your assertion is not ok")
} The output is a bit weird due to the fact it tries to open a file, but it works ... After that i noticed that using triggers will generate a change each time you apply. The final version of the workaround: resource "null_resource" "assert_something" {
triggers = <my_assertion> ? {} : file("your assertion is not ok")
lifecycle {
ignore_changes = [
triggers
]
}
} |
May be useful to folks here: I've created a custom provider specifically for triggering failures on plan: https://github.com/rhythmictech/terraform-provider-errorcheck example:####code: locals {
compare = "success"
testSuccess = "success"
testFail = "fail"
}
resource "errorcheck_is_valid" "shouldMatch" {
name = "shouldMatch"
test = local.compare == local.testSuccess
}
resource "errorcheck_is_valid" "Not_valid_if_not_match" {
name = "Not_valid_if_not_match"
test = local.compare == local.testFail
} output:terraform validate .
Error: Not Valid
on main.tf line 11, in resource "errorcheck_is_valid" "Not_valid_if_not_match":
11: resource "errorcheck_is_valid" "Not_valid_if_not_match" { |
At some point I'd like to look at setting up a custom error message, but it's already much cleaner and more future-proof than hacking around the HCL parser |
@smiller171 looks like we had the same idea :) however I went with data_source as there is no need to save any values in state. It would be nice to have assert-like functionality in terraform as default especially now with all the additional functions in terraform 0.12x |
Some time ago I tried to solve it with similar data source approach, but inside a module https://github.com/gordonbondon/terraform-common-verify |
@bwoznicki @gordonbondon The problem with doing it in a data source instead of a provider is that it won't error until you try to apply. Doing it in a provider lets you throw an error on |
@smiller171 data source is validated early look at this: https://www.terraform.io/docs/configuration/data-sources.html#data-source-lifecycle and https://www.terraform.io/docs/extend/writing-custom-providers.html#data-sources unless you are targeting a resource output that is know after apply it will fail at the plan stage. Run an example of workspace test in https://github.com/bwoznicki/terraform-provider-assert and you will see that it fails regardless of how many resources you have there. |
@bwoznicki interesting. I went with a provider specifically because I thought data sources weren't evaluated during planning. |
I am also struggling with this problem during ansible provisioning inside local_exec. if there are ansible errors, I'd like them to be able to stop terraform from continuing provisioning to make it easier to find out what went wrong (otherwise the log is massive, and no colors doesn't make it easy). Is it not possible to raise an error to terraform and stop it from continuing provisioning? |
Thanks @Vince-Chenal for the new workaround. AFAICT the following simpler variation seems to also work with Terraform 0.12.5: locals {
assert_not_default_workspace = terraform.workspace == "default" ? file("ERROR: default workspace not allowed") : null
}
Honestly if we just had an |
Apologies, I see the value in that too. I was stumped on how to ensure ansible playbooks that failed on apply would stop the rollout from continuing. |
This seems like a pretty important feature to have. Are there any thoughts on providing this within Terraform itself in a way that is both compatible with 0.12.x and that doesn't require opening a non-existent file? I'll definitely take a look at https://github.com/bwoznicki/terraform-provider-assert as well as its fork https://github.com/rvodden/terraform-provider-assert |
While this may not completely solve the issue, just wanted to add this reference for anyone looking at the ticket: https://www.terraform.io/docs/configuration/variables.html#custom-validation-rules |
Another workaround is to use the regex function as "If the given pattern does not match at all, the regex raises an error.". One downside of this approach is that the error message tells you what pattern was not matched but doesn't tell you why the pattern is not permitted. |
@adamday2 it works for basic assertion, but you cannot cross reference other variables. If you must provide either variable a or b, you can't check in a if b has also been provided. I am just pointing this out for anyone reading this thread, as you said it yourself that it doesn't solve the issue. |
I've added a feature proposal in #24269 which (in combination with the newly added The core idea in my proposal is that we can keep this very simple: Please raise a vote there and join the discussion on #24269 if you think this would be a valuable addition. Thanks! |
I am actually more in favour of having |
@aaronsteers @syedrakib Do |
@smiller171 - My understanding is that |
I have created a new PR to add the |
@syedrakib - The new PR #25088 attempts to provide this. It still needs a bunch more testing but functionality-wise, I think it's mostly there. If anyone has Golang experience and can help continue to testing/review, this would be very much appreciated! |
May be resolved by: #25088 (PR waiting feedback, CI tests passing) @timgurney-ee (OP) and subscribers to this issue:
|
Protip: instead of inserting
Hopefully, whoever implemented it, won't add paradoxes other than Hilbert's Hotel. |
While waiting for the
|
@joeyciechanowicz the errorcheck module I wrote is cleaner https://registry.terraform.io/modules/rhythmictech/errorcheck/terraform/latest |
It absolutely is, it's great. However it does requires adding a 3rd party dependency, whereas the |
Fair enough, though I'd argue my 15 lines of Python are simple enough to audit. |
Hi to those following this thread 👋 The Terraform Core team is currently researching this issue, and we'd love to get more feedback from those interested in this functionality. If you'd like to participate in the research, you can comment on this Discuss topic. It'd be great to setup a call and get your use-cases -- thanks so much advance! |
For anyone crossing this issue, Terraform is going to have something like this implemented on 1.2.0. See https://discuss.hashicorp.com/t/update-ability-to-raise-an-error/37732 |
Terraform 1.2 will include a language change which aims to address the use cases described in this issue: preconditions and postconditions for resources, data sources, and outputs. This is now available for testing in the first Terraform 1.2.0 alpha release. Draft documentation is also available. If you're tracking this issue, I'd encourage you to try out the alpha release. Feedback is welcome on the Terraform discussion forum, and if you encounter bugs please file a GitHub issue. Thanks! |
A small addition to @alisdair's previous note: At the time of writing this the most recent alpha release v1.2.0-alpha-20220328 still has the feature behind an opt-in-experiment guard, so you'll need to enable it to use the features described in the draft document: terraform {
experiments = [preconditions_postconditions]
} The current implementation on the main branch has it no longer marked as experimental and so any subsequent alpha releases, and eventually the beta release, will not require this opt-in. However, note that although the design is relatively finalized now we may still make some minor changes to it before it's included in a final release. |
One feature I have not been able to find (or code around) is the lack of ability to raise an intentional error.
There are times where I need certain things to be correct for example 2 lists to be of the same size, or a variable to be one of a certain set of values etc.
So the addition of a raise_error function or similar would be useful. I was thinking of it being an additional interpolation function something that works like this:
raise_error(condition, message)
for example
raise_error(${length(var.array1) == length(var.array2)}, 'The arrays need to be the same length')
if the condition fails, then the message is displayed otherwise the processing continues.
The text was updated successfully, but these errors were encountered: