Skip to content

Latest commit

 

History

History
202 lines (162 loc) · 5.22 KB

terraform.md

File metadata and controls

202 lines (162 loc) · 5.22 KB

Terraform Linting

Validate Terraform files with built-in rules

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.

Custom Terraform rules for your project or organization

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.

Categories

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

Resource Example

---
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}$"

Provider Example

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

Module Example

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

Terraform 12 Example

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"

Evaluating Terraform 12 Dynamic Blocks

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