Terraform local-exec Provisioner: A Short Guide for Infrastructure Teams

Table of Contents

  1. What is local-exec?
  2. Basic Syntax and Configuration
  3. Common Use Cases with Examples
  4. local-exec vs remote-exec
  5. Security Best Practices
  6. Enterprise Considerations
  7. Summary Comparison
  8. Conclusion

What is local-exec?

The Terraform local-exec provisioner executes commands on the machine running Terraform—not on the provisioned resources. While HashiCorp positions provisioners as a "last resort," local-exec provides practical solutions for bridging declarative infrastructure management with imperative operations.

Use local-exec for:

  • External system integrations lacking native Terraform providers
  • Post-deployment validation and health checks
  • Dynamic inventory generation for configuration management
  • Local file operations based on infrastructure outputs

Avoid local-exec for:

  • Operations with existing Terraform provider support
  • Resource configuration (use remote-exec or cloud-init)
  • Complex processes better handled by dedicated orchestration tools

Basic Syntax and Configuration

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  
  provisioner "local-exec" {
    command     = "echo 'Server deployed: ${self.private_ip}'"
    working_dir = "${path.module}/scripts"
    environment = {
      SERVER_IP = self.private_ip
      ENV       = var.environment
    }
    when        = "create"  # or "destroy"
    on_failure  = "fail"    # or "continue"
  }
}

Common Use Cases with Examples

Dynamic Ansible Inventory

resource "aws_instance" "web_servers" {
  count = var.server_count
  ami   = data.aws_ami.amazon_linux.id
  # ... configuration ...
}

resource "local_file" "inventory" {
  content = templatefile("inventory.tpl", {
    servers = aws_instance.web_servers[*].public_ip
  })
  filename = "ansible/inventory"

  provisioner "local-exec" {
    working_dir = "ansible"
    command     = "ansible-playbook -i inventory site.yml"
  }
}

Database Migrations

resource "null_resource" "db_migrations" {
  depends_on = [aws_db_instance.app_db]
  
  provisioner "local-exec" {
    environment = {
      DB_HOST = aws_db_instance.app_db.address
      DB_NAME = aws_db_instance.app_db.db_name
      DB_USER = var.db_username
      DB_PASS = var.db_password
    }
    command = "./run_migrations.sh"
  }
}

Health Check Validation

resource "aws_instance" "app" {
  # ... configuration ...
  
  provisioner "local-exec" {
    command = <<-EOT
      for i in {1..30}; do
        if curl -f http://${self.public_ip}:8080/health; then
          echo "Application ready!"
          exit 0
        fi
        sleep 10
      done
      exit 1
    EOT
  }
}

local-exec vs remote-exec

Aspect local-exec remote-exec
Execution Terraform runner machine Target resource
Network Local access only SSH/WinRM to resource
Use Cases External integrations, health checks Resource configuration
Security Terraform process permissions Remote user permissions
Debugging Easy local access Requires resource connectivity

Security Best Practices

Prevent Shell Injection

# ❌ VULNERABLE
provisioner "local-exec" {
  command = "process.sh --input '${var.user_input}'"
}

# ✅ SECURE
provisioner "local-exec" {
  environment = {
    USER_INPUT = var.user_input
  }
  command = "process.sh --input \"$USER_INPUT\""
}

Secure Credential Handling

data "aws_secretsmanager_secret_version" "creds" {
  secret_id = "api/credentials"
}

resource "null_resource" "secure_task" {
  provisioner "local-exec" {
    environment = {
      API_KEY = jsondecode(data.aws_secretsmanager_secret_version.creds.secret_string).api_key
    }
    command = "./secure_operation.sh"
  }
}

Enterprise Considerations

For organizations running Terraform at scale, local-exec usage benefits from centralized governance. Platforms like Scalr provide enhanced capabilities that address common enterprise challenges:

  • Centralized execution with comprehensive audit trails of all local-exec operations
  • Policy enforcement to monitor and restrict local-exec usage across teams
  • Consistent environments eliminating "works on my machine" issues
  • Role-based controls determining who can deploy configurations with local-exec

CI/CD Integration

resource "null_resource" "deployment_notification" {
  provisioner "local-exec" {
    environment = {
      PIPELINE_ID = var.pipeline_id
      COMMIT_SHA  = var.commit_sha
      SLACK_URL   = var.notification_webhook
    }
    command = "./notify_deployment.sh"
  }
}

Summary Comparison

Feature local-exec remote-exec Native Provider
Execution Local machine Target resource Terraform core
Security Risk Shell injection Remote access Provider-specific
Debugging Easy Moderate Easy
Enterprise Fit Good with governance Moderate Excellent
Maintenance High High Low

Conclusion

Terraform's local-exec provisioner serves as a practical bridge between declarative infrastructure and imperative operations. While it requires careful security and maintainability considerations, it remains essential for complex automation scenarios.

Key recommendations:

  1. Use judiciously - Prefer native providers when available
  2. Secure by default - Use environment variables, never direct interpolation
  3. Design for scale - Consider centralized execution platforms for enterprise governance
  4. Handle errors gracefully - Implement timeouts, retries, and cleanup procedures

For enterprise teams, centralized Terraform execution platforms can significantly improve the governance and reliability of local-exec usage while maintaining the flexibility that makes it valuable.