Terraform null_resource Deprecated Warning — What to Use Instead
Terraform's null_resource is deprecated in favor of terraform_data. Learn what changed, how to migrate your existing null_resource blocks, and common patterns that now have better native solutions.
If you've run terraform plan recently on an older codebase and seen this warning:
Warning: Deprecated resource
null_resource has been deprecated in favour of terraform_data.
Please update the resource type from null_resource to terraform_data.
You're not alone. The null_resource that everyone's been using for years for local-exec provisioners and depends_on tricks is being replaced.
Here's what changed and how to migrate.
What Was null_resource Used For?
null_resource has always been a workaround — a resource that does nothing on its own but lets you attach provisioners and triggers to it.
Common patterns:
- Running local scripts or commands after a resource is created
- Forcing re-runs when certain values change (triggers)
- Creating explicit depends_on relationships that aren't captured by resource references
# Old pattern with null_resource
resource "null_resource" "run_migration" {
triggers = {
always_run = timestamp()
}
provisioner "local-exec" {
command = "kubectl apply -f migrations/"
}
depends_on = [aws_eks_cluster.main]
}The Replacement: terraform_data
terraform_data is a built-in resource (available since Terraform 1.4) that replaces null_resource without requiring the hashicorp/null provider.
The migration is mostly a find-and-replace:
# New pattern with terraform_data
resource "terraform_data" "run_migration" {
triggers_replace = [timestamp()] # Note: triggers_replace, not triggers
provisioner "local-exec" {
command = "kubectl apply -f migrations/"
}
depends_on = [aws_eks_cluster.main]
}Key differences:
triggersbecomestriggers_replace- No provider required (
terraform_datais built in) - The
inputandoutputattributes let you store and reference values
Migration Guide
Pattern 1: Triggers Map → triggers_replace
# Before
resource "null_resource" "example" {
triggers = {
cluster_id = aws_eks_cluster.main.id
config_hash = md5(file("config.yaml"))
}
}
# After
resource "terraform_data" "example" {
triggers_replace = [
aws_eks_cluster.main.id,
md5(file("config.yaml"))
]
}Note: triggers_replace is a list, not a map. If you were using the trigger key names in your provisioner commands, you'll need to adjust.
Pattern 2: Storing Values with input/output
terraform_data has an input attribute that stores arbitrary values, accessible via output:
resource "terraform_data" "bootstrap" {
input = {
cluster_endpoint = aws_eks_cluster.main.endpoint
cluster_name = aws_eks_cluster.main.name
}
provisioner "local-exec" {
command = "aws eks update-kubeconfig --name ${self.output.cluster_name}"
}
}
# Reference the output elsewhere
output "cluster_info" {
value = terraform_data.bootstrap.output
}This is more powerful than null_resource's triggers, which could only store values for trigger purposes.
Pattern 3: depends_on — No Change
# This works exactly the same
resource "terraform_data" "wait_for_cluster" {
depends_on = [
aws_eks_cluster.main,
aws_eks_node_group.workers
]
provisioner "local-exec" {
command = "sleep 30 && kubectl get nodes"
}
}When null_resource Actually Makes Sense Still
The null_resource isn't removed — just deprecated. You can keep using it if:
- You're on Terraform < 1.4 (terraform_data isn't available)
- You have a massive codebase and can't migrate everything at once
- Your organization hasn't upgraded the null provider and you don't want to add a new dependency
The deprecation warning won't cause your plans to fail. It's just a warning.
Better Alternatives to Both
For some null_resource use cases, there are better native solutions in modern Terraform:
Running post-deployment scripts: Consider using local-exec provisioners on the actual resource instead of a separate null_resource.
Waiting for resources: Use timeouts blocks on resources that support them, or the terraform_data with a sleep command.
Complex dependencies: If you're using null_resource purely for depends_on, check if you can express the dependency through resource references directly — Terraform tracks these automatically.
Fixing the Warning in CI
If your CI pipeline fails on warnings, add this to suppress during migration:
TF_CLI_ARGS_plan="-no-color" terraform plan 2>&1 | grep -v "Warning: Deprecated"But the right fix is migration. In most codebases, replacing null_resource with terraform_data is a 5-minute change per occurrence.
More Terraform troubleshooting? Check our Terraform state lock error fix and Terraform plan unexpected destroy fix.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Terraform Accidentally Destroyed Resources — How to Recover
You ran terraform apply and it deleted something it shouldn't have. Here's how to recover from accidental Terraform destroys before they become a disaster.
Terraform Backend S3 Init Failed — Every Cause and Fix (2026)
terraform init fails with S3 backend errors — access denied, bucket does not exist, state lock issues, wrong region. Here's every cause and the exact fix for each one.
Terraform Import: Fixing State Conflicts with Existing Resources
Getting state conflicts or duplicate resource errors with terraform import? Learn how to import existing AWS resources, fix config mismatches, and use moved blocks to rename state entries.