Speakeasy Logo
Skip to Content

Terraform

Automatic polling support for Terraform providers

Brian Flad

Brian Flad

November 24, 2025 - 5 min read


Long running requests are a pattern that appears throughout infrastructure APIs. That’s why Terraform providers generated with Speakeasy will now include automatic polling support for API operations that perform background processing. This eliminates the need to manually implement waiter logic when resources need time to reach a desired state.

⏱️ Automatic polling for long-running operations

Many infrastructure API operations perform work asynchronously. When creating a database instance, spinning up compute resources, or processing batch jobs, the API returns immediately with an identifier, but the actual work happens in the background. The resource must repeatedly poll a status endpoint until the operation completes.

Before polling support, Terraform provider developers had to manually implement waiter logic for each resource:

// Manual waiter implementation in Terraform provider code func (r *DatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var data DatabaseResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } client := r.client // Create the resource instance, err := client.CreateDatabase(ctx, data) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create database, got error: %s", err)) return } data.ID = types.StringValue(instance.ID) // Manual polling loop timeout := time.After(10 * time.Minute) ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { select { case <-timeout: resp.Diagnostics.AddError("Timeout", "timeout waiting for database to be ready") return case <-ticker.C: status, err := client.GetDatabase(ctx, instance.ID) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get database status: %s", err)) return } if status.State == "ready" { resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) return } if status.State == "failed" { resp.Diagnostics.AddError("Database Creation Failed", "database creation failed") return } } } }

This pattern appears across infrastructure providers: databases, compute instances, networking resources, and any API that performs background work. Each resource requires careful handling of timeouts, intervals, success conditions, and failure states.

How it works

Polling support uses the x-speakeasy-polling OpenAPI extension to define success criteria, failure criteria, and timing configuration for operations:

paths: /databases/{id}: get: operationId: getDatabase x-speakeasy-polling: - name: WaitForCompleted delaySeconds: 2 intervalSeconds: 5 limitCount: 120 successCriteria: - condition: $statusCode == 200 - condition: $response.body#/state == "ready" failureCriteria: - condition: $statusCode == 200 - condition: $response.body#/state == "failed"

Configuration options

Each polling option supports:

  • delaySeconds: Initial delay before first poll (default: 1)
  • intervalSeconds: Time between subsequent polls (default: 1)
  • limitCount: Maximum number of polling attempts (default: 60)
  • successCriteria: Conditions that indicate operation completion (using Arazzo criterion objects )
  • failureCriteria: Conditions that indicate operation failure (using Arazzo criterion objects)

Multiple polling options can be defined for the same operation, each with different success criteria for different use cases.

Entity operation configuration

To wire polling configurations to Terraform resources, you’ll also need to configure the x-speakeasy-entity-operation extension. This tells the generator how and when to use the polling configuration for each resource operation:

/task: post: x-speakeasy-entity-operation: Task#create#1 /task/{id}: get: responses: "200": description: OK content: application/json: schema: type: object properties: name: type: string status: type: string enum: - completed - errored - pending - running required: - name - status x-speakeasy-polling: - name: WaitForCompleted failureCriteria: - condition: $statusCode == 200 - condition: $response.body#/status == "errored" successCriteria: - condition: $statusCode == 200 - condition: $response.body#/status == "completed" x-speakeasy-entity-operation: - Task#read - entityOperation: Task#create#2 options: polling: name: WaitForCompleted

This configuration provides the flexibility to use different polling settings for different operations on the same resource. For example, creation might need a delay before starting polls and take longer to complete, while deletion can start immediately and finish quicker.

For more details on entity operation configuration, see our entity mapping documentation .

Terraform resource behavior

After defining polling configuration in your OpenAPI specification, Terraform resources automatically handle state transitions:

resource "myapi_database" "example" { name = "production-db" region = "us-west-2" # Terraform automatically waits for the database to be ready # using the polling configuration from your OpenAPI spec }

When a resource is created, updated, or deleted, Terraform uses the polling configuration to wait for the operation to complete before proceeding. This provides a seamless experience for Terraform users without requiring custom waiter implementations in provider code.

Resource lifecycle integration

Polling integrates with all phases of the Terraform resource lifecycle:

  • Create: Wait for new resources to reach a ready state
  • Update: Wait for modifications to be applied
  • Delete: Wait for resources to be fully removed
  • Read/Refresh: Ensure resources are in expected states during plan operations

Error handling

When polling encounters issues, Terraform receives clear error messages:

  • Failure criteria met: If the API indicates the operation failed (status reaches “failed”, “error”, etc.), Terraform immediately stops and reports the failure
  • Timeout reached: If polling exceeds the configured limit count, Terraform reports a timeout error with context about how many attempts were made

This allows Terraform users to understand what went wrong and take appropriate action, whether that’s investigating API issues or modifying resource configuration.

🚀 Eliminating manual waiter logic

Polling support eliminates one of the most tedious aspects of Terraform provider development. By embedding this pattern directly in generated providers, Speakeasy removes the need for custom waiter implementations.

Key benefits

  • Eliminate boilerplate: No manual polling loops in Terraform provider code
  • Consistent behavior: Same polling pattern across all resources
  • Flexible configuration: Define timing and criteria in OpenAPI specifications
  • Proper error handling: Distinguish between operation failures and timeouts
  • Seamless user experience: Terraform resources “just work” with long-running operations
  • Declarative configuration: Polling behavior lives in API specifications, not code

Next steps

Ready to add polling support to your Terraform providers? Check out our documentation:

Polling support works alongside other Speakeasy features like retries, globals, pagination, and authentication to provide a complete Terraform provider generation solution.

Last updated on

Build with
confidence.

Ship what's next.