Skip to content

Latest commit

 

History

History
144 lines (109 loc) · 4.73 KB

intro.md

File metadata and controls

144 lines (109 loc) · 4.73 KB

Introduction

This plugin is backed by Open Policy Agent (OPA) and allows you to write custom rules for TFLint in the policy language (Rego). This document will guide you through the step-by-step process of getting started writing policies.

First, refer to the official documentation for OPA concepts and Policy Language. The documentation that follows assumes familiarity with these concepts.

As an example, create the following policy as .tflint.d/policies/bucket.rego:

package tflint

import rego.v1

deny_invalid_s3_bucket_name contains issue if {
	buckets := terraform.resources("aws_s3_bucket", {"bucket": "string"}, {})
	name := buckets[_].config.bucket
	not startswith(name.value, "example-com-")

	issue := tflint.issue(`Bucket names should always start with "example-com-"`, name.range)
}

Suppose you apply a policy to the following files:

resource "aws_s3_bucket" "invalid" {
  bucket = "example-corp-assets"
}

resource "aws_s3_bucket" "valid" {
  bucket = "example-com-assets"
}

Let's go through it line by line.

package tflint

The first line is the package declaration. All valid policies must be described under the tflint package.

import rego.v1

This declaration ensures compatibility with future OPA v1 syntax. See The rego.v1 Import for details.

deny_invalid_s3_bucket_name contains issue if {

The next line is the rule declaration. A valid rule name must start with deny_, violation_, warn_ or notice_. The rule name in TFLint is the rule name with "opa_" prefix (e.g. opa_deny_invalid_s3_bucket_name), and the severity is error for deny_ or violation_, warning for warn_, and notice for notice_.

The rule should return a set of issue objects, not a boolean. An issue is created on the last line when all conditions are met.

buckets := terraform.resources("aws_s3_bucket", {"bucket": "string"}, {})

The next line is to retrieve aws_s3_bucket resources. The policy language written in this plugin primarily uses JSON retrieved by custom functions rather than input data. The terraform.resources is a custom function to retrieve resource blocks in Terraform configs. For more custom functions, see Functions.

Note that the schema must be declared when referencing inside a resource block. The example above declares that the bucket attribute exists as a string. See Terraform Schema for details.

The return value of this function will be the following JSON:

[
  {
    "type": "aws_s3_bucket",
    "name": "invalid",
    "config": {
      "bucket": {
        "value": "example-corp-assets",
        "unknown": false,
        "sensitive": false,
        "range": {
          "filename": "main.tf",
          "start": { "line": 2, "column": 12, "byte": 48 },
          "end": { "line": 2, "column": 33, "byte": 69 }
        }
      }
    },
    "decl_range": {
      "filename": "main.tf",
      "start": { "line": 1, "column": 1, "byte": 0 },
      "end": { "line": 1, "column": 35, "byte": 34 }
    }
  },
  {
    "type": "aws_s3_bucket",
    "name": "valid",
    "config": {
      "bucket": {
        "value": "example-com-assets",
        "unknown": false,
        "sensitive": false,
        "range": {
          "filename": "main.tf",
          "start": { "line": 6, "column": 12, "byte": 119 },
          "end": { "line": 6, "column": 32, "byte": 139 }
        }
      }
    },
    "decl_range": {
      "filename": "main.tf",
      "start": { "line": 5, "column": 1, "byte": 73 },
      "end": { "line": 5, "column": 33, "byte": 105 }
    }
  }
]

Attributes set in the schema are included under the config if they actually exist.

name := buckets[_].config.bucket
not startswith(name.value, "example-com-")

The next line is to get the bucket attributes. Note that the value is bucket.value and bucket is an object.

issue := tflint.issue(`Bucket names should always start with "example-com-"`, name.range)

The last line is to generate an issue. Use the tflint.issue function to specify the message and range.

This allows you to raise an issue for config that violates your policy:

$ tflint
1 issue(s) found:

Error: Bucket names should always start with "example-com-" (opa_deny_invalid_s3_bucket_name)

  on main.tf line 2:
   2:   bucket = "example-corp-assets"

Reference: .tflint.d/policies/main.rego:5

Note that this policy cannot enforce policy in all cases. See Handling unknown/null/undefined values for details.