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 codefunc (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:
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:
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.
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.