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
| Resource | Service | Why |
|---|---|---|
* | All resources | Any 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.
| Parameter | Type | Default | Description |
|---|---|---|---|
resource_types | list(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
provisionerblock, not the resource holding it. Anull_resourcethat existed only to run provisioners is left behind as an empty resource that still appears in your plan and state. - Removes
provisionerblocks only. Aconnectionblock that configured aremote-execprovisioner stays in the resource, where it now has nothing to configure. - Does not affect
local-execorremote-execin 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-execprovisioner onnull_resource.archivewhencreate_package = true, its default, so with this rule the package is never built. Use the rule there only withcreate_package = falseand a prebuilt package (local_existing_packageors3_existing_package). - Only
.tfand.tofufiles 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:
| Framework | Controls | Role |
|---|---|---|
| SOC 2 | CC8.1 | Supports change management by removing unreviewed imperative code from modules |
| NIST 800-53 | CM-3, SI-7 | Supports configuration management and software integrity |
Failure modes
| Scenario | Result |
|---|---|
| Module relies on a provisioner for required setup | The 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 provisioner | The 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. |
Related
- Rulesets in the console - where an org admin adds this rule to the Baseline or a ruleset
- Rule catalog - every rule, with what each one changes