# Generate Terraform provider from OpenAPI / Swagger

Terraform is an infrastructure-as-code tool that uses providers to manage cloud infrastructure through API calls. Creating and maintaining Terraform providers, which are typically written in Go, requires specialized skills and frequent updates to keep up with API changes.

Speakeasy simplifies creating and maintaining Terraform providers by generating providers from OpenAPI documents. This eliminates the need for Go expertise, keeps providers up-to-date, and reduces the complexity of developing and maintaining providers for cloud environments.

For a detailed overview of supported features, refer to the [Terraform support matrix](/docs/speakeasy-reference/supported/terraform).

## Prerequisites

Creating a Terraform provider with Speakeasy requires:

- The [Speakeasy CLI](/docs/speakeasy-reference/cli/getting-started)
- An API spec in a supported format:

<Callout title="Tip" >
If you are using an unsupported spec format, use these tools to help you convert to a supported format:

- [Swagger2.0 -> OpenAPI v3.0](https://github.com/swagger-api/swagger-converter)
- [Postman -> OpenAPI v3.0](https://kevinswiber.github.io/postman2openapi/)
</Callout>

## Add annotations

Use the `x-speakeasy-entity` annotation to specify objects to be included as Terraform entities in the provider.
  
```yaml
paths:
 /pet:
   post:
     ...
     x-speakeasy-entity-operation: Pet#create
     ...
Pet:
 x-speakeasy-entity: Pet
 ...
```

Terraform usage:

```HCL
resource "petstore_pet" "myPet" {
  ...
}
```

Speakeasy infers Terraform types from the JSON Schema, focusing on the semantics of the `CREATE` and `UPDATE` requests and responses. No specific Terraform types need to be defined in the OpenAPI document.

1. **Required vs optional:** If a property is required in the `CREATE` request body, it's marked as `Required: true`; otherwise, it's `Optional: true`.
2. **Computed properties:** Properties that appear in a response body but are absent from the `CREATE` request are marked as `Computed: true`. This indicates that the provider may compute or return a value for this property.
3. **Optional and computed together:** When a property has both `Optional: true` and `Computed: true`, either the user or the provider can supply the value. If the user doesn't specify a value, the provider will compute or return one.
4. **The `ForceNew` property:** If a property exists in the `CREATE` request but is not present in the `UPDATE` request, it's labeled `ForceNew`.
5. **Enum validation:** When an attribute is defined as an enum, Speakeasy configures a `Validator` for runtime type checks. This ensures that all request properties precisely match one of the enumerated values.
6. **`READ`, `UPDATE`, and `DELETE` dependencies**: Every parameter essential for `READ`, `UPDATE`, or `DELETE` operations must either be part of the `CREATE` API response body or be consistently required in the `CREATE` API request. This ensures that all necessary parameters are available for these operations.

<Callout title="Tip" >
  Use additional `x-speakeasy` [annotations](/docs/terraform) to
  customize the provider as necessary.
</Callout>

## Enhance generated documentation

Speakeasy helps you autogenerate documentation using the HashiCorp `terraform-plugin-docs` tools and packages. For best results, we recommend that you:

1. **Include descriptions:** Ensure the OpenAPI document contains detailed descriptions of resources, attributes, and operations. Clear and concise descriptions help understand the purpose and use of each component.
2. **Provide examples:** Use examples in the OpenAPI document to illustrate how resources and attributes should be configured. Speakeasy leverages these examples to generate usage snippets for reference when starting with the provider.

The Swagger Petstore generates a usage snippet for the pet resource similar to the following:

```go 
"petstore_pet" "my_pet" {
    id   = 10
    name = "doggie"
    photo_urls = [
        "...",
    ]
}.
```

## Generate a Terraform provider

Run the Speakeasy `quickstart` command:

```bash
speakeasy quickstart
```

Follow the interactive guide, providing the necessary information when prompted, including the path to the spec. Select `terraform` as the language. 

After completing the quickstart, regenerate the Terraform provider at any point by running `speakeasy run`.

## Guidance on modeling entities

### Repository naming

Name the provider and GitHub repository `terraform-provider-XXX`, where `XXX` becomes the short name of the provider, also known as the **provider type name**.

The provider type name should preferably be `[a-z][a-z0-9]`, although hyphens and underscores are also valid and can be included in the name if necessary.

### Entity naming

When naming entities that you want Speakeasy to convert to Terraform resources, use PascalCase to ensure the names are translated to Terraform's underscore naming. For list endpoints, pluralize the PascalCase name.

After completing the quickstart, regenerate the Terraform provider at any time by running `speakeasy run`.

### Modeling entities

First, find the list operation for an API entity or resource. Usually, it is a `GET` on `/something`. Annotate the list operation with `x-speakeasy-entity-operation: XXX#read`.

Now, find the CREATE, READ, UPDATE, and DELETE (CRUD) operations for an API resource. Usually, these take the form of a `POST` on `/something` and operations on `/something/{id}`. Annotate the CRUD operations with `x-speakeasy-entity-operation: XXX#create`.

Ensure the CREATE response returns data. Some API frameworks don't output it, even though they generally have to return data such as an identifier for the resource.

Finally, check whether the `GET` (not list) read response includes an extra data property or similar element between the root of the response schema and the actual data. If the `GET` read response does have an additional data property, add the `x-speakeasy-entity: XXX` annotation to the object beneath that data property (not on the data itself). Most APIs use a shared `component`, which is often the best place for entity annotation.

## Frequently asked questions

**Do the generated Terraform providers support importing resources?**

Yes, generated Terraform providers support importing resources. However, certain prerequisites and considerations must be taken into account:

**Prerequisites**

1. **API specification:** Ensure the OpenAPI document defines an annotated and type-complete API operation for reading each resource. Tag the operation with `x-speakeasy-entity-operation: MyEntity#read`.
2. **Complete `READ` operation:** Attributes of a resource not defined in the `READ` API are set to `null` by Terraform during the import process.

**Simple keys**

A simple key is a single required ID field that is directly exposed to `terraform import` operations. For example, if the `pet` resource has a single `id` field, the import command will look like this: `terraform import petstore_pet.my_pet my_pet_id`.

**Handling composite keys**

Speakeasy natively supports the direct import of resources with multiple ID fields. Speakeasy generates code that enables import functionality by requiring users to provide a JSON-encoded object with all necessary parameters. In addition to generating documentation, Speakeasy generates appropriate error messages to be displayed if the proper syntax is not followed.

**Import composite keys by block**

An import block allows you to import a resource into the Terraform state by generating the corresponding Terraform configuration. Using a composite key, the import block will look like this:

```hcl filename="test.tf"
import {
  id = jsonencode({
    primary_key_one: "9cedad30-2a8a-40f7-9d65-4fabb04e54ff"
    primary_key_two: "e20c40a0-40e8-49ac-b5d0-6e2f41f9e66f"
  })
  to = my_test_resource.my_example
}
```

```bash
terraform plan -generate-config-out=generated.tf
```
**Import composite keys using the CLI**

To import a resource with composite keys using the Terraform CLI, use the `terraform import` command:

```bash
terraform import my_test_resource.my_example '{ "primary_key_one": "9cedad30-2a8a-40f7-9d65-4fabb04e54ff", "primary_key_two": "e20c40a0-40e8-49ac-b5d0-6e2f41f9e66f" }'
```
