Code Monkey home page Code Monkey logo

rules_tf's Introduction

Tf Rules

The Tf rules are useful to validate, lint and format terraform code.

They can typically be used in a terraform monorepo of modules to lint, run validation tests, auto generate documentation and enforce the consistency of providers versions across all modules.

Why "Tf" and not "Terraform"

Because now you can either use "tofu" or "terraform" binary.

Getting Started

To import rules_tf in your project, you first need to add it to your MODULE.bazel file:

bazel_dep(name = "rules_tf", version = "0.0.7")
# git_override(
#     module_name = "rules_tf",
#     remote      = "https://github.com/yanndegat/rules_tf",
#     commit      = "...",
# )

tf = use_extension("@rules_tf//tf:extensions.bzl", "tf_repositories", dev_dependency = True)
tf.download( 
    version = "1.5.7", 
    tflint_version = "0.51.1", 
    tfdoc_version = "0.18.0", 
    use_tofu = False,
    versions = {
        "random" : "hashicorp/random:3.3.2",
        "null"   : "hashicorp/null:3.1.1",
    } 
)

# Switch to tofu
# tf = use_extension("@rules_tf//tf:extensions.bzl", "tf_repositories")
# tf.download( 
#    version = "1.6.0", 
#    use_tofu = True,
#    mirror = {
#        "random" : "hashicorp/random:3.3.2",
#        "null"   : "hashicorp/null:3.1.1",
#    }
# )

use_repo(tf, "tf_toolchains")
register_toolchains(
    "@tf_toolchains//:all",
    dev_dependency = True,
)

Once you've imported the rule set , you can then load the tf rules in your BUILD files with:

load("@rules_tf//tf:def.bzl", "tf_providers_versions", "tf_module")

tf_providers_versions(
    name = "providers",
    tf_version = "1.2.3",
    providers = {
        "random" : "hashicorp/random:>=3.3",
        "null"   : "hashicorp/null:>=3.1",
    },
)

tf_module(
    name = "root-mod-a",
    providers = [
        "random",
    ],
    deps = [
        "//tf/modules/mod-a",
    ],
    providers_versions = ":providers",
)

Using Tf Modules

  1. Using custom tflint config file
load("@rules_tf//tf:def.bzl", "tf_module")

filegroup(
    name = "tflint-custom-config",
    srcs = [
        "my-tflint-config.hcl",
    ],
)

tf_module(
    name = "mod-a",
    providers = [
        "random",
    ],
    ...
    tflint_config = ":tflint-custom-config"
    
)
  1. Generating versions.tf.json files

Terraform linter by default requires that all providers used by a module are versioned. It is possible to generate a versions.tf.json file by running a dedicated target:

load("@rules_tf//tf:def.bzl", "tf_providers_versions", "tf_module")

tf_providers_versions(
    name = "providers",
    tf_version = "1.2.3",
    providers = {
        "random" : "hashicorp/random:3.3.2",
        "null"   : "hashicorp/null:3.1.1",
    },
)

tf_module(
    name = "root-mod-a",
    providers = [
        "random",
    ],
    deps = [
        "//tf/modules/mod-a",
    ],
    
    providers_versions = ":providers",
)
bazel run //path/to/root-mod-a:gen-tf-versions

or generate all files of a workspace:

bazel cquery 'kind(tf_gen_versions, //...)' --output files | xargs -n1 bash
  1. Generating terraform doc files

It is possible to generate a README.md file by running a dedicated target for terraform modules:

load("@rules_tf//tf:def.bzl", "tf_gen_doc")

tf_gen_doc(
    name = "tfgendoc",
    modules = ["//{}/{}".format(package_name(), m) for m in subpackages(include = ["**/*.tf"])],
)

and run the following command to generate docs for all sub packages.

bazel run //path/to:tfgendoc

It is also possible to customize terraform docs config:

load("@rules_tf//tf:def.bzl", "tf_gen_doc")

filegroup(
    name = "tfdoc-config",
    srcs = [
        "my-tfdoc-config.yaml",
    ],
)

tf_gen_doc(
    name   = "custom-tfgendoc",
    modules = ["//{}/{}".format(package_name(), m) for m in subpackages(include = ["**/*.tf"])],
    config = ":tfdoc-config",
)
  1. Formatting terraform files

It is possible to format terraform files by running a dedicated target:

load("@rules_tf//tf:def.bzl", "tf_format")


tf_format(
    name = "tffmt",
    modules = ["//{}/{}".format(package_name(), m) for m in subpackages(include = ["**/*.tf"])],
)

and run the following command to generate docs for all sub packages.

bazel run //path/to:tffmt

rules_tf's People

Contributors

dependabot[bot] avatar yanndegat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

rules_tf's Issues

Discussion: `tf_module` to require explicitly provided srcs/data

Currently, the following definition is valid:

tf_module(
    name = "my-module",
)

since tf_module macro signature has data defaulting to []. However, this is not the effective default as :srcs is native.glob(["**/*"], exclude=bzl_files) + data. This is imho confusing.

I suggest requiring srcs in macro explicitly and not using native.glob(["**/*"], exclude=bzl_files) at all. What do you think?

`tf_validate`/`tf_lint` fails: No such file or directory

tf_validate fails due to invalid paths to tf runtime for me:

bazel test //terraform/module:validate
Starting local Bazel server and connecting to it...
...
(15:38:06) FAIL: //terraform/module:validate (see /private/var/tmp/_bazel_d0oldgr/9c1080b7fb0dff5bf53320f61219f86a/execroot/_main/bazel-out/darwin_arm64-fastbuild/testlogs/module:validate/test.log)
(15:38:06) INFO: From Testing //terraform/module:validate:
==================== Test output for //terraform/module:validate:
/private/var/tmp/_bazel_d0oldgr/9c1080b7fb0dff5bf53320f61219f86a/sandbox/darwin-sandbox/1/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/terraform/module/validate.runfiles/_main/terraform/module/validate: line 1: external/rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/terraform/terraform: No such file or directory
/private/var/tmp/_bazel_d0oldgr/9c1080b7fb0dff5bf53320f61219f86a/sandbox/darwin-sandbox/1/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/terraform/module/validate.runfiles/_main/terraform/module/validate: line 1: external/rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/terraform/terraform: No such file or directory

Indeed, there is no external/rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/terraform/terraform in runtime folder:

> tree bazel-bin/terraform/module/validate.runfiles/ -L 4
├── MANIFEST
├── _main
│   └── terraform
│       └── module
│           ├── validate
│           ├── data.tf
│           ├── deployment.tf
│           ├── locals.tf
│           ├── providers.tf
│           └── vars.tf
├── _repo_mapping
└── rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64
    ├── mirror -> /private/var/tmp/_bazel_d0oldgr/9c1080b7fb0dff5bf53320f61219f86a/external/rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/mirror
    └── terraform
        └── terraform -> /private/var/tmp/_bazel_d0oldgr/9c1080b7fb0dff5bf53320f61219f86a/external/rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/terraform/terraform

7 directories, 9 files

This is due to use of .path here:

tf = tf_runtime.tf.path,
plugins_mirror = tf_runtime.mirror.path,

Switching to .short_path fixed the issue.

I copied the rule in my workspace and added some debug statements:

print(tf_runtime.tf.path)
print(tf_runtime.tf.short_path)

print(tf_runtime.mirror.path)
print(tf_runtime.mirror.short_path)

the output is the following:

external/rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/terraform/terraform
../rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/terraform/terraform

external/rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/mirror
../rules_tf~~tf_repositories~main___download_tf_0_darwin_arm64/mirror

Update: tf_lint fails for me for the same reason

`tf_lint` toolchain wrapper should support passing options

There are plenty of options in https://github.com/terraform-linters/tflint that can be passed as tflint arguments, however, due to toolchain providing access not to the binary but a wrapper.sh, which is not expecting any args except {mod_dir} {config_file}, it is currently not possible to pass additional arguments even with a custom tf_lint rule implementation.

I suggest updating wrapper.sh to pass extra args (index 3 +) straight to tflint binary.

Question: `terraform apply` and co.

I recently started using the rules and it came in handy for linting and formatting. I was wondering if there are any plans to support terraform init / apply?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.