# release-terraform-polling
Source: https://speakeasy.com/blog/release-terraform-polling


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:

```go
// 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:

```yaml
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](https://spec.openapis.org/arazzo/latest.html#criterion-object))
- **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:

```yaml
/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](https://www.speakeasy.com/docs/terraform/customize/entity-mapping#api-operation-polling).

### Terraform resource behavior

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

```hcl
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:

- [Terraform provider generation](/docs/terraform/create-terraform)

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