Sign in
Menu
Sign up
Docs
Operational rulesRule catalog

No provisioners

For platform engineers deciding whether to apply provisioner_remove_blocks: what it changes, how to enable it, and how to check it worked.

Removes all provisioner blocks from resources. Provisioners bypass the declarative model and are a supply chain risk in shared modules.

What this rule does

Removes every provisioner block from every resource in the module source. The resource itself is kept; only the block is deleted.

Affected resources

ResourceServiceWhy
*All resourcesAny resource that contains provisioner blocks (local-exec, remote-exec, file)

Before and after

Before (module with provisioners):

resource "aws_instance" "this" {
  ami           = var.ami_id
  instance_type = var.instance_type

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

  provisioner "remote-exec" {
    inline = ["sudo apt-get update"]
  }
}

After (with No provisioners applied):

resource "aws_instance" "this" {
  ami           = var.ami_id
  instance_type = var.instance_type
}

The only change is the rule transformation. All existing arguments, outputs, and module behavior remain identical.

The example above is illustrative. To see this rule run for real, open it in the Rules Playground: it applies provisioner_remove_blocks to a reviewed upstream module that declares the resources this rule targets, and shows the diff the registry would serve. No account needed.

Why this rule exists

Terraform provisioners run arbitrary commands during terraform apply. They don't appear in terraform plan output. In a module sourced from a public registry, provisioner blocks are a supply chain risk: local-exec can modify local files, call APIs, or exfiltrate data.

HashiCorp's own documentation says "provisioners are a last resort." Many platform teams ban them entirely. This rule enforces that ban at the module level by removing all provisioner blocks before the module reaches the developer.

Default configuration

These are the values the rule ships with. Parameters never travel in a module source URL, so a rule named there runs with exactly these defaults. Your organization sets them per rule, in its Baseline or in a ruleset.

ParameterTypeDefaultDescription
resource_typeslist(string)["*"]Target resource type patterns

The HCL this rule is written in, with its parameters and the transformers it runs, is published at provisioner_remove_blocks.

How to enable

On a framework host, add the rule on top of your organization's Baseline with ?add_rules= in the HTTPS module source:

module "lambda" {
  source = "https://soc2.compliance.tf/terraform-aws-modules/lambda/aws?version=x.y.z&add_rules=provisioner_remove_blocks"

  create_package         = false
  local_existing_package = "build/function.zip"
}

On your organization's host, an org admin adds the rule to the Baseline, which reaches every module served from that host, or to a named ruleset that a module selects with ?ruleset=:

module "lambda" {
  source = "https://bobthecorp.compliance.tf/terraform-aws-modules/lambda/aws?version=x.y.z&ruleset=<ruleset-name>"

  create_package         = false
  local_existing_package = "build/function.zip"
}

The example turns off the module's own packaging first: with create_package = true, the default, terraform-aws-lambda builds its deployment package with a local-exec provisioner, and this rule removes it.

Replace x.y.z with the release you use and bobthecorp with your organization's alias. Which parameters each host accepts, and which it refuses with a 400, is on Rulesets in the console.

How to check it worked

Read compliancetf-manifest.json in the module you received. Its entry for this rule carries an outcome: enforced means the change was made, not_applicable means nothing matched - no resource of a targeted type, or the module already carried the identical change. Do not infer enforcement from a successful terraform init; a rule that changes nothing also inits cleanly.

Known limits

  • Removes the provisioner block, not the resource holding it. A null_resource that existed only to run provisioners is left behind as an empty resource that still appears in your plan and state.
  • Removes provisioner blocks only. A connection block that configured a remote-exec provisioner stays in the resource, where it now has nothing to configure.
  • Does not affect local-exec or remote-exec in root module code — only in the downloaded module source.
  • Does not detect or remove provisioner-like behavior implemented through external data sources or Lambda functions.
  • Removes provisioners a module depends on, too. terraform-aws-lambda builds its deployment package with a local-exec provisioner on null_resource.archive when create_package = true, its default, so with this rule the package is never built. Use the rule there only with create_package = false and a prebuilt package (local_existing_package or s3_existing_package).
  • Only .tf and .tofu files are read. Modules written in JSON syntax (.tf.json, .tofu.json) are left untouched.
  • Only the module's own directory and its first-level modules/* submodules are processed. Modules that this module calls by source URL are separate downloads and are handled on their own request.
  • Works with Terraform 1.x and OpenTofu 1.6 and later. The rule reads ordinary HCL, and anything it changes stays ordinary HCL.

Compliance framework support

This rule is not a compliance control. It supports these framework objectives as an operational safeguard:

FrameworkControlsRole
SOC 2CC8.1Supports change management by removing unreviewed imperative code from modules
NIST 800-53CM-3, SI-7Supports configuration management and software integrity

Failure modes

ScenarioResult
Module relies on a provisioner for required setupThe module may not function correctly after provisioner removal. This is rare in terraform-aws-modules. Test in non-production first.
A null_resource exists only to run a provisionerThe provisioner is removed; the null_resource is not. It survives as an empty resource in your plan and state. Removing the resource itself needs a different transformer, which this rule does not use.

On this page

Ask AI about this

Help improve this page