No Provisioners
Removes all provisioner blocks from resources. Provisioners bypass the declarative model and are a supply chain risk in shared modules.
When to use this rule
Use this when: You use community Terraform modules from public registries and want to ensure no arbitrary code runs during terraform apply. Or your platform team has banned provisioners as a policy.
Do not use this when: Your modules intentionally use provisioners for bootstrapping that cannot be replaced with user_data, cloud-init, or configuration management tools. This is rare in terraform-aws-modules.
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.
Affected resources
| Resource | Service | Why |
|---|---|---|
* | All resources | Any resource that contains provisioner blocks (local-exec, remote-exec, file) |
Known limits
- Only removes
provisionerblocks from resource definitions. Does not removenull_resourceresources that contain provisioners (the entire resource remains). - 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.
What this rule does
Removes all provisioner blocks from every resource in the module.
Before and after
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"]
}
}
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.
Real-world scenario
A community module included a local-exec provisioner that ran curl to an external API during terraform apply. The API was decommissioned, causing every apply to hang for 30 seconds before timing out. The provisioner was not visible in terraform plan output.
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 |
Default configuration
This rule ships with the following defaults. Custom parameterization via the registry is planned for a future release.
| Parameter | Type | Default | Description |
|---|---|---|---|
resource_types | list(string) | ["*"] | Target resource type patterns |
How to enable
Add ?rules=pofix/no_provisioners to your HTTPS module source:
module "example" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=pofix/no_provisioners"
}
Configure via the compliance.tf API. See Getting Started with Operational Rules.
Failure modes
| Scenario | Result |
|---|---|
| Module relies on a provisioner for essential setup | The module may not function correctly after provisioner removal. This is rare in terraform-aws-modules. Test in non-production first. |
| null_resource with provisioner is not removed | The null_resource itself is not deleted. Only provisioner blocks inside regular resources are removed. null_resource removal requires the block_delete transformer (not used by this rule). |
Terraform and OpenTofu compatible
This rule works with both Terraform (1.x+) and OpenTofu (1.6+). The generated HCL uses standard lifecycle meta-arguments supported by all versions.
Help us improve this page
Operational Rules are a new feature. We'd love your feedback on this rule page — what's useful, what's missing, what's confusing. Share feedback.