Create Your Terraform Provider From OpenAPI / Swagger

Introduction

Terraform is an infrastructure-as-code tool that uses providers to manage cloud infrastructure via API calls. Creating and maintaining these providers, typically written in Go, can be challenging due to the need for specialized skills and continuous updates with API changes.

Speakeasy offers a streamlined solution by generating Terraform providers directly from OpenAPI specifications. This approach minimizes the need for Go expertise, ensures providers are consistently updated, and simplifies the development and maintenance of Terraform providers for complex cloud environments.

What Does Speakeasy Generate?

Using the Terraform Plugin Framework (opens in a new tab), Speakeasy generates a fully functional Terraform provider from an annotated (opens in a new tab) OpenAPI spec.

Provider Components
Provider ComponentsSpeakeasy generation
Resource SchemasGenerated by adding the x-speakeasy-entity-operation: MyEntity#create to any operation in the OpenAPI spec. Hoists and merges JSON schemas associated with all create, read, update, and delete operations appropriately.
Data Source SchemasGenerated by adding the x-speakeasy-entity-operation: MyEntity#read to any operation in the OpenAPI spec. Hoists and merges JSON schemas associated with all read operations into a single data source.
Create MethodsGenerated by adding the x-speakeasy-entity-operation: MyEntity#create to any operation in the OpenAPI spec. Speakeasy generates platform connector logic to invoke all API requests and save responses to Terraform state.
Read MethodsGenerated by adding the x-speakeasy-entity-operation: MyEntity#read to any operation in the OpenAPI spec. Speakeasy generates platform connector logic to invoke all API requests and save responses to Terraform state. These operations are also invoked and connected to state in CREATE and UPDATE methods should some data attributes only be available by making an additional API call. By defining these, drift detection and import are enabled on your Terraform provider for all associated API response attributes.
Update MethodsGenerated by adding the x-speakeasy-entity-operation: MyEntity#update to any operation in the OpenAPI spec. Speakeasy generates platform connector logic to invoke all API requests and save responses to Terraform state. All attributes available in a create request but unavailable in an update request are associated with a plan modifier that forces a resource recreation when changed.
Delete MethodsGenerated by adding the x-speakeasy-entity-operation: MyEntity#delete to any operation with all non-optional API request properties available in the OpenAPI spec. Speakeasy generates platform connector logic to invoke all API requests and save responses to Terraform state.
Plan ValidatorsGenerated when using restricted OpenAPI data types such as JSON fields, date fields, or date-time fields and OpenAPI validations. Also generated for some specific Speakeasy extensions when a customer requests it (for example, x-speakeasy-conflicts-with).
Plan ModifiersGenerated when needed to ensure API and the terraform state and terraform plan commands have the appropriate semantics, for instance, to work around diff-detection issues.
Resource ImportsGenerated by adding the x-speakeasy-entity-operation: MyEntity#read to an operation with a single ID field in the OpenAPI spec.

What Does Speakeasy Support?

Speakeasy follows OpenAPI semantics when generating a provider. In most simple CRUD APIs, very few annotations are required on your OpenAPI specifications, with all of the following inferred from the specification.

Supported OpenAPI Semantics
OpenAPI SemanticsSpeakeasy Support / Comment
Resource Schemas (const)When an attribute in an OpenAPI specification is specified as const, it is removed from a Terraform schema and always sent in the request or assumed in the response.
Resource Schemas (default)When an attribute specifies a default value, this value will be used whenever that attribute is not explicitly set (for example, if it is unset in the Terraform configuration).
Server ConfigurationA server_url variable is available in the provider to enable it to be invoked against any API server, defaulted to the first entry in the servers field of your OpenAPI specification.
Global AuthenticationEvery authentication mechanism that relies on static authorization is supported with its values automatically available to be configured in the provider configuration. For OAuth, a custom hook will need to be written containing your authentication flow logic.
Query Parameter Serialization✅️Full support. All query parameter attributes will be available as resource or data source attributes in a Terraform-native form. Might require remapping through x-speakeasy-match.
Request Headers✅️Full support. All request attributes will be available as resource or data source attributes in a Terraform-native form.
Multiple API Requests in One CRUD StepFull support. For example, create requiring two API calls.
JSON Schema type: stringFull support
JSON Schema type: numberFull support
JSON Schema type: integerFull support
JSON Schema type: booleanFull support
JSON Schema type: objectFull support
JSON Schema type: nullFull support
JSON Schema required: [requiredPropertyA, ...]Full support. Combined into Required or Optional Terraform attribute modifiers.
JSON Schema enum: [...values...]Full support. A plan validator is added to assert that only one of the predefined values is set.
JSON Schema type: arrayFull support.
JSON Schema type: array, minItems: NFull support. A plan validator is added to ensure the Terraform ListAttribute has N items set.
JSON Schema type: array, maxItems: JFull support. A plan validator is added to ensure the Terraform ListAttribute has J items set.
JSON Schema type: array, uniqueItems: trueFull support. A plan validator is added to ensure the Terraform ListAttribute has all items as unique values.
JSON Schema type: number, format: floatFull support.
JSON Schema type: number, format: doubleFull support.
JSON Schema type: integer, format: int32Full support.
JSON Schema type: integer, format: int64Full support.
JSON Schema type: string, format: dateFull support. A plan validator is added to ensure that this String value is set to a date in the YYYY-MM-DD format, for example, "2022-01-30"
JSON Schema type: string, format: date-timeFull support. A plan validator is added to ensure that this String value is RFC 3339-compatible, for example, "2019-10-12T07:20:50.52Z"
JSON Schema format: binaryFull support. Accessible as a String attribute
JSON Schema nullable: trueFull support. Combined into Required or Optional Terraform attribute modifiers.
JSON Schema additionalProperties: trueFull support. Note that a free-form object without additionalProperties: true is treated as an empty object
JSON Schema additionalProperties: ${JSON Schema}Full support.
JSON Schema oneOf: [${JSON Schema}, ...]Full support. Represented as a nested object with one child attribute for each oneOf subschema. A plan validator is added that asserts only one child attribute can be set.
JSON Schema anyOf: [${JSON Schema}, ...]Full support. Considered the same as oneOf.
JSON Schema allOf: [${JSON Schema}, ...]Full support. Constructs an "uber-type" by merging the superset of all subschemas.
JSON Schema "Any" TypeFull support. Requires the x-speakeasy-type-override: any annotation. Used as an escape hatch.
OpenAPI readOnly: trueFull support.
OpenAPI writeOnly: trueFull support.
Example GenerationFull support. Propagates example and examples into generated Terraform resource examples. Uses a type-appropriate value for other cases.
Massaging of Divergent API and Schema Types⚠️Partial support. Speakeasy supports many different mechanisms to configure the reconciliation of divergent Terraform types and the merging of multiple API requests into a single resource schema, even when there's type divergence across APIs. Reach out to us on Slack support for more information or look at these advanced examples (opens in a new tab) that describe some common scenarios.

What Are the Limitations of Speakeasy Terraform Provider Generation?

Speakeasy covers a wide range of OpenAPI features and we're constantly working to support more. The following outlines the current limitations of our Terraform provider generation product:

Limitations
OpenAPI SemanticsSpeakeasy Support / Comment
Operation-Specific Authentication⚠️Speakeasy supports all OpenAPI global authentication mechanisms but currently doesn't support overriding global authentication on certain specific operations without advanced techniques like monkey patching. Ensure that all API operations are authenticated through global security configuration.
label / matrix Path Parameter Serialization⚠️No support for label or matrix path parameter styles. Might require remapping through x-speakeasy-match when path parameter names for Read, Update, or Delete do not match Create attribute names.
XML Request Body Serialization⚠️Full support for all JSON data types, multipart encoding, binary, form data, file uploads, URL-encoded form methods, plain text, and raw byte entries. No support for XML or other methods. All request attributes will be available as resource or data source attributes in a Terraform-native form.
XML Response Body Deserialization⚠️Full support for all JSON data types, plain text, and raw bytes. No support for other media types. All response attributes will be made available in Terraform state.
Circular References⚠️Partial support through x-speakeasy-type-override: any to enable an attribute to be set with jsonencode(...arbitrary data...). However, the Terraform type schema doesn't support circular dependencies.
Lists of Lists of Primitives⚠️Partial support through x-speakeasy-type-override: any to enable an attribute to be set with jsonencode(...arbitrary data...). However, the Terraform ListAttribute can only contain primitive item types. To support lists of lists of attributes, use an escape hatch like x-speakeasy-ignore or x-speakeasy-type-override: any.
Runtime Validations (for example, pattern)⚠️Partial support. Contact support if you need more.

Note: Circular References

The Terraform Type schema must be a directed acyclic graph. OpenAPI's use of JSON Schema allows for circular references to be defined. When Speakeasy detects a circular reference, it will output an error and instructions for a few workarounds.

To prevent errors due to circular references, apply x-speakeasy-type-override: any to the attribute causing the circular reference, and the attribute will be inferred as a string that satisfies JSON. A Terraform user can then use the jsonencode function to pass arbitrary data into the attribute.

Terraform Framework Types From JSON Schema Types

Attribute types are inferred, with one exception (see below).

Exceptions
JSON Schema TypeTerraform Framework TypeExceptions
type: stringschema.StringAttribute
type: numberschema.NumberAttribute
type: integerschema.Int64Attribute
type: booleanschema.BoolAttribute
type: arrayschema.ListAttribute or Schema.ListNestedAttributeA ListAttribute is used when the array has primitive children.
type: objectschema.SingleNestedAttribute
type: nullElement ignored
additionalPropertiesschema.MapNestedAttributeThe item type of the MapNestedAttribute is dependent on the subschema of the additionalProperties value
JSON Schema Subschema TypeHandling
oneOfA schema.SingleNestedAttribute is created with one key for each oneOf child. If a discriminator is defined, the discriminator is used to determine the name of the subkey. A plan validator is defined to validate that only one subattribute is used at a time.
anyOfConsidered the same as oneOf. Speakeasy does not strictly enforce this subschema type, as it has not been observed being used correctly in production environments.
allOfMerges all subschemas. When the subschemas are objects, it creates a composite object incorporating all properties from the child schemas.

1. Prerequisites

To get started with the Speakeasy Terraform-provider creation, you will need:

Spec FormatSupported
OpenAPI 3.0
OpenAPI 3.1
JSON Schema
Postman Collection🔜
Success Icon

TIP

If you are using an unsupported spec format, some tools can help you convert to a supported format:

2. Add Annotations

Annotate objects representing Terraform entities with x-speakeasy-entity. This determines their inclusion in the Terraform provider.


paths:
/pet:
post:
...
x-speakeasy-entity-operation: Pet#create
...
Pet:
x-speakeasy-entity: Pet
...

Terraform Usage:


resource "petstore_pet" "myPet" {
...
}

Speakeasy infers Terraform types from your JSON schema, focusing on the semantics of CREATE and UPDATE requests and responses. You should not need to define any specific Terraform types in your OpenAPI spec.

  1. Required vs. Optional: A property is marked as Required: true if it's required in the CREATE request body; 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 Terraform will compute the properties' values.
  3. The ForceNew Property: If a property exists in the CREATE request but is not present in the UPDATE request, it's labeled ForceNew.
  4. 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.
  5. 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.
Success Icon

TIP

Use additional x-speakeasy annotations to customize your provider as necessary.

4. Enhance Generated Documentation

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

  1. Include Descriptions: Ensure your OpenAPI spec contains detailed descriptions of resources, attributes, and operations. Clear and concise descriptions help users understand the purpose and use of each component.
  2. Provide Examples: Use examples in your OpenAPI spec to illustrate how resources and attributes should be configured. Speakeasy leverages these examples to generate usage snippets that users can refer to when starting with your provider.

The Swagger Pet Store would generate a usage snippet for the pet resource like the following:

"petstore_pet"

id = 10
name = "doggie"
photo_urls = [
"...",
]
}.

5. Generate Terraform

  • Run the Speakeasy generate command:

speakeasy generate sdk --lang terraform -o . -s [openAPI spec]

Frequently Asked Questions

Do generated Terraform providers support the ability to import resources?

Yes, generated Terraform providers do support the ability to import resources. However, there are specific prerequisites and considerations to keep in mind:

Prerequisites

  1. API Specification: It is essential to have an annotated and type-complete API operation defined for reading each resource in the OpenAPI specification. Tag the operation with x-speakeasy-entity-operation: MyEntity#read.

  2. Single Resource ID: Speakeasy can currently only generate import logic for resources that can be read using a single ID-like attribute. An ID-like attribute is any required attribute in the READ API request.

  3. Comprehensive READ Operation: If any attribute of a resource is not defined in the READ API, Terraform will set that attribute to null during the import process.

Handling Composite Keys

Speakeasy doesn't natively support direct import for resources with multiple ID fields. To work around this, serialize composite keys into a single ID field and override the implementation by parsing the import-ID request.

Composite Key Format: "<sourceId>,<workspaceID>"