There is a set of built-in rules that cover some best practices for AWS resources. There
config-lint -terraform <FILE_OR_DIRECTORY_OF_TF_FILES>
If you want to run most of the built-in rules, but not all, you can use a profile to exclude some rules or resources.
For Terraform files with Terraform 12 specific features, use the -terraform12
flag:
config-lint -terraform12 <FILE_OR_DIRECTORY_OF_TF_FILES>
The Terraform12 parser is fully backwards compatible with previous versions of Terraform.
If you wish to force a specific parser version, add the -tfparser tf11|tf12
flag. This is useful if you have a lot of rules with Type: Terraform
but your Terraform files include Terraform 12 syntax.
config-lint -rules <CUSTOM_RULE_YML_FILE> <FILE_OR_DIRECTORY_OF_TF_FILES>
You can specify the -rules option multiple times if you have multiple custom rule files. It is also possible to specify both the -terraorm option as well as one or more -rules options, if you want the built-in rules as well as some custom rules.
The default category for resources that can be linter is "resource", which covers the most common use case. This is for things like aws__instances, or s3_buckets, etc. But there are some additional categories available for Terraform linting. The current list of supported categories is:
- resource
- data
- provider
- module
---
version: 1
description: Check for tags in Terraform file
type: Terraform
files:
- "*.tf"
rules:
- id: REQUIRED_TAGS
message: "A required tag is missing"
resources:
- aws_s3_bucket
- aws_instance
assertions:
- key: tags[0]
op: has-properties
value: environment,cost_center
- id: VALID_ENVIRONMENT_TAG
message: "The environment tag is not valid"
resources:
- aws_s3_bucket
- aws_instance
assertions:
- key: tags[0].environment
op: in
value: dev,prod,stage
- id: VALID_COST_CENTER_TAG
message: "Cost center must be a 4 digit number"
resources:
- aws_s3_bucket
- aws_instance
assertions:
- key: tags[0].cost_center
op: regex
value: "^[0-9]{4}$"
For providers, set the category to "provider" and the resource attribute to the name of the provider.
---
version: 1
description: Terraform provider example
type: Terraform
files:
- "*.tf"
rules:
- id: NO_SECRETS_IN_AWS_PROVIDER
category: provider
resource: aws
assertions:
- key: access_key
op: absent
- key: secret_key
op: absent
For modules, use "module" for category, and for resource use the "source" attribute.
This allows checking of parameters being used when a module is referenced.
---
version: 1
description: Terraform module invocation example
type: Terraform
files:
- "*.tf"
rules:
- id: MODULE_EXAMPLE
message:
category: module
resource: "example/website"
assertions:
- key: num_servers
op: present
Note the type: Terraform12
item below. Rules targeting templates with Terraform 12-specific features must use the Terraform12 type.
version: 1
description: Rules for Terraform configuration files
type: Terraform12
files:
- "*.tf"
rules:
- id: CIDR_SET
message: Testing
resource: aws_security_group
assertions:
- every:
key: "ingress"
expressions:
# this says that it either must be a private IP, or not have IP regex (eg sg string, interpolation)
- every:
key: cidr_blocks
expressions:
- key: "@"
op: contains
value: "/24"
Dynamic blocks are a new feature introduced in Terraform 12 that enables users to dynamically construct repeatable nested blocks such as ingress rules in an AWS Security Group.
Writing rules for dynamic blocks is a little tricky, as the structure that Terraform parses the .tf file into is different than you may expect.
This Terraform config will generate an ingress
block for reach item in the service_ports
list variable.
variable "service_ports" {
default = [22, 80, 1433, 6379]
}
resource "aws_security_group" "example" {
name = "example"
dynamic "ingress" {
for_each = var.service_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
}
}
egress = "-1"
}
The following rule will result in an error if port 22 (SSH) is included as an ingress for the security group.
The JMESPATH expression refers to keys ("dynamic" and "for_each") that are generated by Terraform, rather than what is present in the configuration.
version: 1
description: Rules for Terraform configuration files
type: Terraform12
files:
- "dynamic_block.tf"
rules:
- id: NO_SSH_ACCESS
message: Testing
resource: aws_security_group
assertions:
- key: "dynamic[*].for_each[]"
op: not-contains
value: 22