How to Use TFLint to Check Errors in Your Terraform Code

A guide to checking errors in Terraform code using TFLint.

Nayan Tank
Cloud Native Daily
5 min readAug 18, 2023

--

What is TFLint?
TFLint is a linter that checks for possible errors, best practices, etc in your terraform code. It will also help identify provider-specific issues before errors occur during a Terraform run. For example, if Terraform is creating an EC2 instance and a developer accidentally references an invalid instance type, TFLint would flag it as an error.

TFLint performs the following checks on the code,

  • Find possible errors (like illegal instance types) for Major Cloud providers.
  • Warn about deprecated syntax, unused declarations.
  • Enforce best practices, naming conventions.

How to Install TFLint?
TFLint download and installation along with other terraform tools are documented here.

How to configure TFLint?
Once TFLint is downloaded and installed on our machine, we have make the following changes for each module repository.

  • Add a .tflint.hcl file for each module
  • Identify and add the rules specific to each module.

All the subsequent pull requests will have a flag which will tell us if the module has passed the lint check or not.

What is .tflint.hcl file?
By default, TFLint command looks for .tflint.hcl file if it was not specified explicitly. The default location for this file is the current directory (./.tflint.hcl). tflint.hcl is written HashiCorp Configuration Language (HCL) and can be easily deciphered by the tflint utility.

A sample .tflint.hcl file.

config {
module = true
force = false
disabled_by_default = false
varfile = ["example1.tfvars", "example2.tfvars"]
}

plugin "aws" {
enabled = true
version = "0.4.0"
source = "github.com/terraform-linters/tflint-ruleset-aws"
}

rule "aws_instance_invalid_type" { enabled = false }

Now let's look at each parameter and find out what they are used for,

module

By default, TFLint inspects only the root module, so if a resource to be inspected is cut out into a module, it will be ignored from inspection targets. To avoid such problems, TFLint can also inspect Module Calls. In this case, The check is based on the input variables passed to the calling module.

Ex:
module "aws_instance" {
source = "./module"
ami = "ami-b73b63a0"
instance_type = "t2.micro"
}

varfile

Set Terraform variables from tfvars files. If terraform.tfvars or any *.auto.tfvars files are present, they will be automatically loaded.

Rule blocks

You can configure TFLint rules using rule blocks. Each rule’s implementation specifies whether it will be enabled by default. In some rulesets, the majority of rules are disabled by default. Use rule blocks to enable them:

rule "terraform_unused_declarations" {
enabled = true
}

The enabled attribute is required for all rule blocks. For rules that are enabled by default, set enabled = false to disable the rule: rule “aws_instance_previous_type” { enabled = false }

Plugin blocks

You can extend TFLint by installing any plugin. After declaring the version and source, tflint --init can automatically install the plugin. More details about the plugins can be found here https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/plugins.md

Plugins are available for all all 3 major cloud providers,

  • Amazon Web Services
  • Microsoft Azure
  • Google Cloud Platform

For AWS users, there is an e-budled plugin built into the TFLint binary without installing the plugin separately for backward compatibility. Rules for the Terraform Language are built into the TFLint binary, so we don’t need to install any plugins. If we want to extend TFLint with other plugins, we can declare the plugins in the config file and easily install them with tflint –init command.

What are the available rules for AWS?
There are more than 700 rules available for AWS, we can add the rules required for our module in our .tflint.hcl file.

AWS Complete Ruleset: https://github.com/terraform-linters/tflint-ruleset-aws/blob/master/docs/rules/README.md

Sample tflint.hcl file 1

config {
#Enables module inspection
module = true
force = false
}

# Disallow deprecated (0.11-style) interpolation
rule "terraform_deprecated_interpolation" {
enabled = true
}

# Disallow legacy dot index syntax.
rule "terraform_deprecated_index" {
enabled = true
}

# Disallow variables, data sources, and locals that are declared but never used.
rule "terraform_unused_declarations" {
enabled = true
}

# Disallow // comments in favor of #.
rule "terraform_comment_syntax" {
enabled = false
}

# Disallow output declarations without description.
rule "terraform_documented_outputs" {
enabled = true
}

# Disallow variable declarations without description.
rule "terraform_documented_variables" {
enabled = true
}

# Disallow variable declarations without type.
rule "terraform_typed_variables" {
enabled = true
}

# Disallow specifying a git or mercurial repository as a module source without pinning to a version.
rule "terraform_module_pinned_source" {
enabled = true
}

# Enforces naming conventions
rule "terraform_naming_convention" {
enabled = true

#Require specific naming structure
variable {
format = "snake_case"
}

locals {
format = "snake_case"
}

output {
format = "snake_case"
}

#Allow any format
resource {
format = "none"
}

module {
format = "none"
}

data {
format = "none"
}

}

# Disallow terraform declarations without require_version.
rule "terraform_required_version" {
enabled = true
}

# Require that all providers have version constraints through required_providers.
rule "terraform_required_providers" {
enabled = true
}

# Ensure that a module complies with the Terraform Standard Module Structure
rule "terraform_standard_module_structure" {
enabled = true
}

# terraform.workspace should not be used with a "remote" backend with remote execution.
rule "terraform_workspace_remote" {
enabled = true
}

Further Reading:

--

--