# docker start -ai sebox
```

In another terminal on your host machine (not inside Docker), run the code below to create folders to work in for the Speakeasy and HashiCorp examples:

```sh
cd code
mkdir hashi # hashicorp
mkdir se # speakeasy
cd se
touch openapi.yaml
```

You need to give your host user write permissions to files created in the container to edit them. On Unix-like systems, use the following command on your host machine, replacing `myusername` with your user name:

```
sudo chown -R myusername:myusername code
```

Now insert the Swagger Petstore example schema (available [here](https://github.com/speakeasy-api/examples-repo/blob/main/how-to-build-terraform-providers/original-openapi.yaml)) into the `openapi.yaml` file. The Swagger Petstore example schema is the API description of a service that manages pets and orders for pets at a store.


## Create a Terraform Provider With the HashiCorp Provider Spec Generator

The [HashiCorp automated Terraform provider creator](https://developer.hashicorp.com/terraform/plugin/code-generation/design) works differently from Speakeasy. HashiCorp provides an API SDK to convert your OpenAPI schema into an intermediate format, called the provider code specification (JSON). This document is then transformed into a provider by Terraform SDK. These transforming SDKs are both CLI tools.

```mermaid
flowchart TD
  subgraph 0[" "]
    1(📜 OpenAPI schema)
    1b(📜 Generator configuration)
  end
  0 --⚙️ API SDK--> 2(📃 Provider code specification)
  2 --⚙️ Terraform SDK --> 3(🗝️ Go provider)
```

An OpenAPI schema does not describe which Terraform resource each operation maps to. So, like Speakeasy extension attributes, you need to include mapping information in a generator configuration file along with your schema.

You can create a provider by starting at any one of the three steps:
- [Start with an OpenAPI schema](https://developer.hashicorp.com/terraform/plugin/code-generation/openapi-generator), as with Speakeasy.
- [Write a provider code specification manually](https://developer.hashicorp.com/terraform/plugin/code-generation/framework-generator), or with some tool the Terraform community may develop in the future, without using OpenAPI at all. Writing a provider code specification is closely coupled to the Terraform system and allows you to precisely create a compatible provider. You can even include custom Go functions to map objects between your API and Terraform. The full list of features with examples is [here](https://developer.hashicorp.com/terraform/plugin/code-generation/specification).
- Create a provider manually by coding it in Go (the traditional way).

As noted in the [HashiCorp code generation design principles](https://developer.hashicorp.com/terraform/plugin/code-generation/design#apis-support-more-than-just-terraform-providers), there is a mismatch between an OpenAPI schema and a Terraform provider. Providers expect resources, transfers, and errors to relate in a way that an API doesn't. For this reason, it's unlikely that there will ever be a general solution to creating a provider from a schema that does not require annotations like Speakeasy extension attributes or a HashiCorp generator configuration.

### The HashiCorp Workflow Example

HashiCorp provides a [full walkthrough for creating a Terraform provider from a schema](https://developer.hashicorp.com/terraform/plugin/code-generation/workflow-example). The Docker container you have been working in has everything you need to follow the Hashicorp walkthrough if you'd like to. Change to the `hashi` folder and continue working.

We don't repeat HashiCorp's tutorial here, but let's take a look at the steps and how the process differs from Speakeasy.

- HashiCorp doesn't create a full Go project for you. You need to create one with `go mod init terraform-provider-petstore`.
- You need to use the framework code generator `scaffold` command to create a template for the Go provider code, and write your own `main.go` file.
- Instead of adding attributes to your schema, you create a `generator_config.yml` file to hold the mapping between the schema and the provider. It looks like this:
  ```yaml
  provider:
  name: petstore
  resources:
    pet:
      create:
        path: /pet
        method: POST
      read:
        path: /pet/{petId}
        method: GET
      schema:
        attributes:
          aliases:
            petId: id
  ```
- The `tfplugingen-openapi generate` command creates a provider code specification in JSON from your schema, and then `tfplugingen-framework generate` creates the provider from the provider code specification, like so:
  ```sh
  tfplugingen-openapi generate \
    --config ./generator_config.yaml \
    --output ./specification.json \
    ./openapi.yaml &&

  tfplugingen-framework generate all \
      --input specification.json \
      --output internal/provider
  ```
- This creates the Go files `internal/provider/provider_petstore/petstore_provider_gen.go` and `internal/provider/resource_pet/pet_resource_gen.go`. The `_gen` in the filename is a hint that a tool created the file, and it should not be edited manually.
- The HashiCorp Terraform provider generator does not create all the code you need. It creates only the Go data types for the pet resource, not the code that manages the data. In the middle of the HashiCorp Terraform provider generation walkthrough, you can see that a long page of code needs to be copied and pasted into `/internal/provider/pet_resource.go`.

## Create a Terraform Provider With Speakeasy

In the Docker container terminal, navigate to the `se` directory using the command `cd /workspace/se` and run the following command to check that your OpenAPI schema is acceptable to Speakeasy:

```sh
speakeasy validate openapi -s openapi.yaml;
```

The Petstore schema has three object types: pet, order, and user. To test Terraform providers, we'll implement the `addPet` and `getPetById` operations. Terraform will use these two operations to create a single pet, and then check if the pet exists when verifying the Terraform state against the API. These operations correspond to Create and Read in CRUD.

### Add Speakeasy Terraform Annotations

We use Speakeasy extension attributes in a schema to have Speakeasy create a Terraform provider. The Speakeasy extensions tell Terraform which OpenAPI schema operations map to which CRUD operations for each resource, and which field is the ID.

Read our [overview of how the Terraform provider creation process works](/docs/create-terraform) or view the [full list of Speakeasy Terraform extensions](/docs/terraform/customize) for more information about creating Terraform providers with Speakeasy.

Insert the following commented lines into `openapi.yaml`:

```yaml
...
    post:
      tags:
        - pet
      summary: Add a new pet to the store
      description: Add a new pet to the store
      x-speakeasy-entity-operation: Pet#create  # <-----
      operationId: addPet
...
  /pet/{petId}:
    get:
      tags:
        - pet
      summary: Find pet by ID
      description: Returns a single pet
      operationId: getPetById
      x-speakeasy-entity-operation: Pet#read  # <-----
      parameters:
        - name: petId
          x-speakeasy-match: id  # <-----
...
components:
  schemas:
    ...
    Pet:
      x-speakeasy-entity: Pet  # <-----
      required:
        - name
        - photoUrls
      type: object
...
```

Validate the schema and create the provider in the Docker container:

```sh
speakeasy validate openapi -s openapi.yaml &&
speakeasy quickstart
```

Speakeasy creates a Terraform provider (Go module) in the `sdk` folder.

#### Speakeasy Security Support

In most cases, Speakeasy should support [security in providers](/docs/create-terraform):
> Every authentication mechanism that relies on static authorization is supported with its values automatically available to be configured in the provider configuration.

But you may encounter instances where this will not be true in practice. If you create a provider for the Petstore schema without removing the security elements, when you run `go run main.go --debug`, you'll get the error:

```sh
internal/provider/pet_data_source.go:135:43: not enough arguments in call to r.client.Pet.GetPetByID
 	have (context.Context, operations.GetPetByIDRequest)
 	want (context.Context, operations.GetPetByIDRequest, operations.GetPetByIDSecurity)
```

In this instance, Speakeasy will not create a provider, as the API method is defined to have differing *resource* security configuration to the global *provider* security configuration in the OpenAPI specification. 

Similarly, for oAuth2 authenticated APIs, the authentication mechanism between a Client and a Token endpoint is ambiguous, even in the (latest) OpenAPI 3.1 Specification. Speakeasy currently natively supports the `client_secret_post` oAuth2 authentication schema as described in Section 9 of [OpenID Connect Core 1.0](https://web.archive.org/web/20250916171023/https://openid.net/specs/openid-connect-discovery-1_0.html#OpenID.Core), under the client credentials flow, but does require that some code is written to help authenticate your provider with your service for your flavour of authentication.

### Install the Terraform Provider

Run the commands below in the Docker terminal to install the Terraform provider as `root/go/bin/terraform-provider-terraform`:

```sh
cd sdk
go install
```

You need to edit the Terraform settings file to redirect provider requests from the online Terraform repository to the custom local provider we created, or Terraform will try to find a provider online for your service and fail. The file is not in the shared `code` folder, so you need to edit it in the Docker terminal. Below we use Vim.

```sh
vim /root/.terraformrc