» Import: tfconfig

The tfconfig import provides access to a Terraform configuration.

The Terraform configuration is the set of *.tf files that are used to describe the desired infrastructure state. Policies using the tfconfig import can access all aspects of the configuration: providers, resources, data sources, modules, and variables.

Some use cases for tfconfig include:

  • Organizational naming conventions: requiring that configuration elements are named in a way that conforms to some organization-wide standard.
  • Required inputs and outputs: organizations may require a particular set of input variable names across all workspaces or may require a particular set of outputs for asset management purposes.
  • Enforcing particular modules: organizations may provide a number of "building block" modules and require that each workspace be built only from combinations of these modules.
  • Enforcing particular providers or resources: an organization may wish to require or prevent the use of providers and/or resources so that configuration authors cannot use alternative approaches to work around policy restrictions.

Note with these use cases that this import is concerned with object names in the configuration. Since this is the configuration and not an invocation of Terraform, you can't see values for variables, the state, or the diff for a pending plan. If you want to write policy around expressions used within configuration blocks, you likely want to use the tfplan import.

» Namespace Overview

The following is a tree view of the import namespace. For more detail on a particular part of the namespace, see below.

tfconfig
├── module() (function)
│   └── (module namespace)
│       ├── data
│       │   └── TYPE.NAME
│       │       ├── config (map of keys)
│       │       ├── references (map of keys) (TF 0.12 and later)
│       │       └── provisioners
│       │           └── NUMBER
│       │               ├── config (map of keys)
│       │               ├── references (map of keys) (TF 0.12 and later)
│       │               └── type (string)
│       ├── modules
│       │   └── NAME
│       │       ├── config (map of keys)
│       │       ├── references (map of keys) (TF 0.12 and later)
│       │       ├── source (string)
│       │       └── version (string)
│       ├──outputs
│       │   └── NAME
│       │       ├── depends_on (list of strings)
│       │       ├── description (string)
│       │       ├── sensitive (boolean)
│       │       ├── references (list of strings) (TF 0.12 and later)
│       │       └── value (value)
│       ├── providers
│       │   └── TYPE
│       │       ├── alias
│       │       │   └── ALIAS
│       │       │       ├── config (map of keys)
│       │       |       ├── references (map of keys) (TF 0.12 and later)
│       │       │       └── version (string)
│       │       ├── config (map of keys)
│       │       ├── references (map of keys) (TF 0.12 and later)
│       │       └── version (string)
│       ├── resources
│       │   └── TYPE.NAME
│       │       ├── config (map of keys)
│       │       ├── references (map of keys) (TF 0.12 and later)
│       │       └── provisioners
│       │           └── NUMBER
│       │               ├── config (map of keys)
│       │               ├── references (map of keys) (TF 0.12 and later)
│       │               └── type (string)
│       └── variables
│           └── NAME
│               ├── default (value)
│               └── description (string)
├── module_paths ([][]string)
│
├── data (root module alias)
├── modules (root module alias)
├── outputs (root module alias)
├── providers (root module alias)
├── resources (root module alias)
└── variables (root module alias)

» references with Terraform 0.12

With Terraform 0.11 or earlier, if a configuration value is defined as an expression (and not a static value), the value will be accessible in its raw, non-interpolated string (just as with a constant value).

As an example, consider the following resource block:

resource "local_file" "accounts" {
  content  = "some text"
  filename = "${var.subdomain}.${var.domain}/accounts.txt"
}

In this example, one might want to ensure domain and subdomain input variables are used within filename in this configuration. With Terraform 0.11 or earlier, the following policy would evaluate to true:

import "tfconfig"

# filename_value is the raw, non-interpolated string
filename_value = tfconfig.resources.local_file.accounts.config.filename

main = rule {
    filename_value contains "${var.domain}" and
    filename_value contains "${var.subdomain}"
}

With Terraform 0.12 or later, any non-static values (such as interpolated strings) are not present within the configuration value and references should be used instead:

import "tfconfig"

# filename_references is a list of string values containing the references used in the expression
filename_references = tfconfig.resources.local_file.accounts.references.filename

main = rule {
  filename_references contains "var.domain" and
  filename_references contains "var.subdomain"
}

The references value is present in any namespace where non-constant configuration values can be expressed. This is essentially every namespace which has a config value as well as the outputs namespace.

» Namespace: Root

The root-level namespace consists of the values and functions documented below.

In addition to this, the root-level data, modules, providers, resources, and variables keys all alias to their corresponding namespaces within the module namespace.

» Function: module()

module = func(ADDR)

The module() function in the root namespace returns the module namespace for a particular module address.

The address must be a list and is the module address, split on the period (.), excluding the root module.

Hence, a module with an address of simply foo (or root.foo) would be ["foo"], and a module within that (so address foo.bar) would be read as ["foo", "bar"].

null is returned if a module address is invalid, or if the module is not present in the configuration.

As an example, given the following module block:

module "foo" {
  # ...
}

If the module contained the following content:

resource "null_resource" "foo" {
  triggers = {
    foo = "bar"
  }
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { subject.module(["foo"]).resources.null_resource.foo.config.triggers[0].foo is "bar" }

» Value: module_paths

  • Value Type: List of a list of strings.

The module_paths value within the root namespace is a list of all of the modules within the Terraform configuration.

Modules not present in the configuration will not be present here, even if they are present in the diff or state.

This data is represented as a list of a list of strings, with the inner list being the module address, split on the period (.).

The root module is included in this list, represented as an empty inner list.

As an example, if the following module block was present within a Terraform configuration:

module "foo" {
  # ...
}

The value of module_paths would be:

[
    [],
    ["foo"],
]

And the following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.module_paths contains ["foo"] }

» Iterating through modules

Iterating through all modules to find particular resources can be useful. This example shows how to use module_paths with the module() function to retrieve all resources of a particular type from all modules (in this case, the azurerm_virtual_machine resource). Note the use of else [] in case some modules don't have any resources; this is necessary to avoid the function returning undefined.

import "tfconfig"

get_vms = func() {
    vms = []
    for tfconfig.module_paths as path {
        vms += values(tfconfig.module(path).resources.azurerm_virtual_machine) else []
    }
    return vms
}

» Namespace: Module

The module namespace can be loaded by calling module() for a particular module.

It can be used to load the following child namespaces:

» Root Namespace Aliases

The root-level data, modules, providers, resources, and variables keys all alias to their corresponding namespaces within the module namespace, loaded for the root module. They are the equivalent of running module([]).KEY.

» Namespace: Resources/Data Sources

The resource namespace is a namespace type that applies to both resources (accessed by using the resources namespace key) and data sources (accessed using the data namespace key).

Accessing an individual resource or data source within each respective namespace can be accomplished by specifying the type and name, in the syntax [resources|data].TYPE.NAME.

In addition, each of these namespace levels is a map, allowing you to filter based on type and name. Some examples of multi-level access are below:

  • To fetch all aws_instance resources within the root module, you can specify tfconfig.resources.aws_instance. This would give you a map of resource namespaces indexed off of the names of each resource (foo, bar, and so on).
  • To fetch all resources within the root module, irrespective of type, use tfconfig.resources. This is indexed by type, as shown above with tfconfig.resources.aws_instance, with names being the next level down.

As an example, perhaps you wish to deny use of the local_file resource in your configuration. Consider the following resource block:

resource "local_file" "foo" {
    content     = "foo!"
    filename = "${path.module}/foo.bar"
}

The following policy would fail:

import "tfconfig"

main = rule { tfconfig.resources not contains "local_file" }

Further explanation of the namespace will be in the context of resources. As mentioned, when operating on data sources, use the same syntax, except with data in place of resources.

» Value: config

  • Value Type: A string-keyed map of values.

The config value within the resource namespace is a map of key-value pairs that directly map to Terraform config keys and values.

As an example, consider the following resource block:

resource "local_file" "accounts" {
  content  = "some text"
  filename = "accounts.txt"
}

In this example, one might want to access filename to validate that the correct file name is used. Given the above example, the following policy would evaluate to true:

import "tfconfig"

main = rule {
    tfconfig.resources.local_file.accounts.config.filename is "accounts.txt"
}

» Value: references

  • Value Type: A string-keyed map of list values containing strings.

The references value within the resource namespace contains the identifiers within non-constant expressions found in config. See the documentation on references for more information.

» Value: provisioners

The provisioners value within the resource namespace represents the provisioners within a specific resource.

Provisioners are listed in the order they were provided in the configuration file.

The data within a provisioner can be inspected via the returned provisioner namespace.

» Namespace: Provisioners

The provisioner namespace represents the configuration for a particular provisioner within a specific resource or data source.

» Value: config

  • Value Type: A string-keyed map of values.

The config value within the provisioner namespace represents the values of the keys within the provisioner.

As an example, given the following resource block:

resource "null_resource" "foo" {
  # ...

  provisioner "local-exec" {
    command = "echo ${self.private_ip} > file.txt"
  }
}

The following policy would evaluate to true:

import "tfconfig"

main = rule {
    tfconfig.resources.null_resource.foo.provisioners[0].config.command is "echo ${self.private_ip} > file.txt"
}

» Value: references

  • Value Type: A string-keyed map of list values containing strings.

The references value within the provisioner namespace contains the identifiers within non-constant expressions found in config. See the documentation on references for more information.

» Value: type

  • Value Type: String.

The type value within the provisioner namespace represents the type of the specific provisioner.

As an example, in the following resource block:

resource "null_resource" "foo" {
  # ...

  provisioner "local-exec" {
    command = "echo ${self.private_ip} > file.txt"
  }
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.resources.null_resource.foo.provisioners[0].type is "local-exec" }

» Namespace: Module Configuration

The module configuration namespace displays data on module configuration as it is given within a module block. This means that the namespace concerns itself with the contents of the declaration block (example: the source parameter and variable assignment keys), not the data within the module (example: any contained resources or data sources). For the latter, the module instance would need to be looked up with the module() function.

» Value: source

  • Value Type: String.

The source value within the module configuration namespace represents the module source path as supplied to the module configuration.

As an example, given the module declaration block:

module "foo" {
  source = "./foo"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.modules.foo.source is "./foo" }

» Value: version

  • Value Type: String.

The version value within the module configuration namespace represents the version constraint for modules that support it, such as modules within the Terraform Module Registry or the Terraform Enterprise private module registry.

As an example, given the module declaration block:

module "foo" {
  source  = "foo/bar"
  version = "~> 1.2"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.modules.foo.version is "~> 1.2" }

» Value: config

  • Value Type: A string-keyed map of values.

The config value within the module configuration namespace represents the values of the keys within the module configuration. This is every key within a module declaration block except source and version, which have their own values.

As an example, given the module declaration block:

module "foo" {
  source = "./foo"

  bar = "baz"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.modules.foo.config.bar is "baz" }

» Value: references

  • Value Type: A string-keyed map of list values containing strings.

The references value within the module configuration namespace contains the identifiers within non-constant expressions found in config. See the documentation on references for more information.

» Namespace: Outputs

The output namespace represents declared output data within a configuration. As such, configuration for the value attribute will be in its raw form, and not yet interpolated. For fully interpolated output values, see the tfstate import.

This namespace is indexed by output name.

» Value: depends_on

  • Value Type: A list of strings.

The depends_on value within the output namespace represents any explicit dependencies for this output. For more information, see the depends_on output setting within the general Terraform documentation.

As an example, given the following output declaration block:

output "id" {
  depends_on = ["null_resource.bar"]
  value      = "${null_resource.foo.id}"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.outputs.id.depends_on[0] is "null_resource.bar" }

» Value: description

  • Value Type: String.

The description value within the output namespace represents the defined description for this output.

As an example, given the following output declaration block:

output "id" {
  description = "foobar"
  value       = "${null_resource.foo.id}"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.outputs.id.description is "foobar" }

» Value: sensitive

  • Value Type: Boolean.

The sensitive value within the output namespace represents if this value has been marked as sensitive or not.

As an example, given the following output declaration block:

output "id" {
  sensitive = true
  value     = "${null_resource.foo.id}"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { subject.outputs.id.sensitive }

» Value: value

  • Value Type: Any primitive type, list or map.

The value value within the output namespace represents the defined value for the output as declared in the configuration. Primitives will bear the implicit type of their declaration (string, int, float, or bool), and maps and lists will be represented as such.

As an example, given the following output declaration block:

output "id" {
  value = "${null_resource.foo.id}"
}

With Terraform 0.11 or earlier the following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.outputs.id.value is "${null_resource.foo.id}" }

» Value: references

  • Value Type:. List of strings.

The references value within the output namespace contains the names of any referenced identifiers when value is a non-constant expression.

As an example, given the following output declaration block:

output "id" {
  value = "${null_resource.foo.id}"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.outputs.id.references contains "null_resource.foo.id" }

» Namespace: Providers

The provider namespace represents data on the declared providers within a namespace.

This namespace is indexed by provider type and only contains data about providers when actually declared. If you are using a completely implicit provider configuration, this namespace will be empty.

This namespace is populated based on the following criteria:

  • The top-level namespace config and version values are populated with the configuration and version information from the default provider (the provider declaration that lacks an alias).
  • Any aliased providers are added as namespaces within the alias value.
  • If a module lacks a default provider configuration, the top-level config and version values will be empty.

» Value: alias

The alias value within the provider namespace represents all declared non-default provider instances for a specific provider type, indexed by their specific alias.

The return type is a provider namespace with the data for the instance in question loaded. The alias key will not be available within this namespace.

As an example, given the following provider declaration block:

provider "aws" {
  alias  = "east"
  region = "us-east-1"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.providers.aws.alias.east.config.region is "us-east-1" }

» Value: config

  • Value Type: A string-keyed map of values.

The config value within the provider namespace represents the values of the keys within the provider's configuration, with the exception of the provider version, which is represented by the version value. alias is also not included when the provider is aliased.

As an example, given the following provider declaration block:

provider "aws" {
  region = "us-east-1"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.providers.aws.config.region is "us-east-1" }

» Value: references

  • Value Type: A string-keyed map of list values containing strings.

The references value within the provider namespace contains the identifiers within non-constant expressions found in config. See the documentation on references for more information.

» Value: version

  • Value Type: String.

The version value within the provider namespace represents the explicit expected version of the supplied provider. This includes the pessimistic operator.

As an example, given the following provider declaration block:

provider "aws" {
  version = "~> 1.34"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.providers.aws.version is "~> 1.34" }

» Namespace: Variables

The variable namespace represents declared variable data within a configuration. As such, static data can be extracted, such as defaults, but not dynamic data, such as the current value of a variable within a plan (although this can be extracted within the tfplan import).

This namespace is indexed by variable name.

» Value: default

  • Value Type: Any primitive type, list, map, or null.

The default value within the variable namespace represents the default for the variable as declared in the configuration.

The actual value will be as configured. Primitives will bear the implicit type of their declaration (string, int, float, or bool), and maps and lists will be represented as such.

If no default is present, the value will be null (not to be confused with undefined).

As an example, given the following variable blocks:

variable "foo" {
  default = "bar"
}

variable "number" {
  default = 42
}

The following policy would evaluate to true:

import "tfconfig"

default_foo = rule { tfconfig.variables.foo.default is "bar" }
default_number = rule { tfconfig.variables.number.default is 42 }

main = rule { default_foo and default_number }

» Value: description

  • Value Type: String.

The description value within the variable namespace represents the description of the variable, as provided in configuration.

As an example, given the following variable block:

variable "foo" {
  description = "foobar"
}

The following policy would evaluate to true:

import "tfconfig"

main = rule { tfconfig.variables.foo.description is "foobar" }