Skip to content
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

Proposal: terraform validate for static validation only #15895

Closed
apparentlymart opened this issue Aug 23, 2017 · 7 comments
Closed

Proposal: terraform validate for static validation only #15895

apparentlymart opened this issue Aug 23, 2017 · 7 comments

Comments

@apparentlymart
Copy link
Member

apparentlymart commented Aug 23, 2017

The main way to check the validity and impact of a Terraform config is to run terraform plan, which validates the configuration and compares it to existing infrastructure in a single operation.

We also offer terraform validate, which is a strange subset of the plan functionality that does the validation step but then stops before computing the difference with existing state.

The validate command currently offers little advantage over plan, since it still fully instantiates the providers and thus, as described in #15811, requires working credentials and network access.

I'd like to propose that we refine the distinction between these commands, as follows:

  • terraform validate checks whether the configuration syntax is valid, whether constant argument values are suitable, whether all interpolations can be resolved to values of the correct type for their usage. This would not use the backend nor configure the providers and thus not reach out to the network at all.
  • terraform plan does all of the above checks for validate, then checks that provided variables are valid, configures the providers, refreshes existing state, does any additional validation that requires network access, and produces a diff showing changes from current state.

In short, terraform validate does static validation (configuration only) while terraform plan does dynamic validation (considering configuration along with the current state of the world, any variables set, etc).

Most notably:

  • It would no longer consider any existing values in state (would act as if the state is entirely empty, not contact the backend at all, etc).
  • It would not consider variable values, so you can ask generally "will this config work regardless of what variables I set?".
  • Would work without a backend configured at all, so validation is possible in the absence of credentials for the backend. (though a terraform init would still be required in order to get provider plugins installed; perhaps it could support a more limited validation pass if providers aren't installed, emitting a warning for things that can't be checked by core alone.)

A key goal would be that terraform validate is suitable to run, for example, as a hook in a text editor so that errors can be flagged automatically. For this to work well, it must be fast and require no context other than the configuration files.

A secondary motivation for making this distinction clear is that #8769 will more firmly establish that plan is not an offline-capable operation: to improve Terraform's ability to detect more problems during plan rather than during apply, it'll be necessary for Terraform to make API calls during plan in certain cases, so my hope is that this new role for terraform validate would compensate for that by clearly distinguishing the static and dynamic use-cases.

Finally, there are plans (already stubbed out in the backend/ code) to allow backends to run certain operations remotely, so that local credentials are not required. terrform validate, in that world, would always run locally, whereas terraform plan would run remotely to get access to any credentials provided by the remote backend.

@tomelliff
Copy link
Contributor

Any reason why you don't want to consider variable values as part of the terraform validate? I'd like it to be able to catch where a module has a required variable that's not set and probably a few variations on that which would be very useful inside my editor but also as part of our static analysis in our CI pipeline.

@apparentlymart
Copy link
Member Author

Hi @tomelliff. Thanks for that feedback/question.

Conceptually, variables are part of the "operation" rather than part of the config, though I understand that this feels a bit muddy when auto-loaded tfvars files are present since it's reasonable to think of them as "part of the config" (even though they aren't, strictly speaking) due to them being files on disk in the config directory.

The reason I wanted to take variables out of consideration here was so that terraform validate would no longer require -var and -var-file options to be set in order to consider a config valid, since a hypothetical generic text editor integration wouldn't know to provide these arguments.

To state the distinction I made in a different way, I think of it like this:

  • terraform plan asks "what will this configuration do in this context", where the context includes a particular state, set of variables, and a target workspace.
  • terraform validate asks "is this configuration valid regardless of context", so that it can run without variables set, without access to a state, and without a target workspace.

We could perhaps consider variable values when they are provided but ignore cases where they aren't, as a compromise. Generally-speaking though I would always suggest using terraform plan when you want to try a "real scenario" (including specific variable values) since the result will always be strictly more complete.

Thanks for sharing this use-case! We'll keep it in mind and see if there's a way to fold it in.

@tomelliff
Copy link
Contributor

Does the -check-variables option on terraform validate not already cover that use case? I'm liking the greater checking going on in validate in 0.10 (although it caused me some issues with references to binary files that are not present in the repo as they are symlinked in from outside or built on demand) and think it definitely makes it a more useful tool, in particular the checking of modules.

Of course nothing beats a plan which is what we do for some things but it's also much more time consuming than just running a validate so we validate a lot more than plan.

@apparentlymart
Copy link
Member Author

We could potentially swap the default on -check-variables so that it becomes an opt-in rather than an opt-out, I suppose.The default would be to ignore unset variables to enable the quick "is this configuration valid, regardless of context" check, which I think (in a future world where it doesn't try to fetch state, doesn't have access to per-workspace variables, may not have all the plugins, etc) is an easier mode to explain and thus a better default. Those who do wish to provide all the additional -var and -var-file options can then opt-in with -check-variables=true (or something more concise) to get a deeper check.

Nothing set it in stone here, anyway. Honestly a lot of this will depend on how validate ends up looking once it's refactored so it can operate in an "incomplete" (uninitialized, etc) working directory, and that will be clearer once we've been able to do some prototyping. A lot of what it does today comes "for free" by running same machinery that drives plan and apply, but that machinery depends on working backend, plugins, etc. Carving these parts out might require some other changes in behavior we haven't thought of yet. But we'll definitely try to fit this use-case in somehow.


While indeed it's not exactly the same, it's also worth noting the terraform plan -refresh=false option, which I've tended to use in the past for a faster "does this look right?" check... this assumes that the stored state is already up-to-date and just validates/diffs against it, so you don't need to wait for the often-time-consuming read of every resource before you get a result. In the future the diff itself is likely to do some network requests to predict better the outcome, but that should always be much lighter than the refresh pass which effectively adds a network request for every existing resource.

@tomelliff
Copy link
Contributor

While refresh=false is fine for local use I'm trying to run basic sanity tests in a CI pipeline where we have a fresh workspace each time (docker executors in Gitlab CI) so it's not useful for me in that case.

I'm mostly using terraform validate to make sure that modules are correctly defined and variables are being passed where needed just to give a thin layer of simple and quick tests that can easily be ran across a whole code base on a push/manually on demand.

The intention is to expand on this at some point, probably with plans on more/all the things and I've been toying with writing a testing framework for modules that are kept outside of other repos.

@teamterraform
Copy link
Contributor

This proposal was more-or-less implemented for Terraform 0.12. Although it wasn't originally intended to be part of the 0.12 scope, it turned out that the terraform validate command needed to be entirely reworked for 0.12 anyway, and so it worked out better to adopt this proposal and re-implement the static validation subset than to adapt this command's previously rather odd featureset into a new world that wasn't so ready to support it.

There is one remaining quirk that terraform validate still produces an error when no provider block is present for a provider that has a required argument, even though that is valid for a child module and thus should pass terraform validate in its new role. That should be fixed in a subsequent release.

Otherwise, terraform validate should now be ready to use in automatic checking situations like text editors, and as a bonus supports a -json option that produces output in a machine-readable way to make such integrations easier.

@ghost
Copy link

ghost commented Aug 17, 2019

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Aug 17, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants