Configuration
Speakeasy extracts resource information from your OpenAPI spec (including resource properties and annotations, and the methods to create, read, update, and destroy those resources) and compiles it into a Terraform provider.
To guide Speakeasy in generating Terraform providers, you need to include a set of required annotations in your OpenAPI spec. Speakeasy also provides a set of "override" extensions you can use to induce certain behaviors in Terraform.
The x-speakeasy-entity Annotation
Use this required annotation for every object in your OpenAPI spec that represents a Terraform entity to specify that it should be included in the Terraform provider. The x-speakeasy-entity annotation can only be used with object types, and every property in the x-speakeasy-entity annotated object will become available at the root of the Terraform resource.
Example
In the following example, the x-speakeasy-entity annotation is applied to the Order object to make it available in the Terraform provider:
components:
schemas:
Order:
description: An order helps you make coffee
x-speakeasy-entity: Order
properties:
id:
type: integer
description: Numeric identifier of the order.
name:
type: string
description: Product name of the coffee.
price:
type: number
description: Suggested cost of the coffee.
required:
- name
- price
type: objectresource "yourprovider_order" "example" {
name = "Filter Blend"
price = 11.5
}Inferred Terraform Types
Speakeasy will attempt to automatically infer all Terraform type signatures in your annotated JSON schema from the semantics used to reference an object in the CREATE and UPDATE request and response bodies.
You should not need to define any specific Terraform types in your OpenAPI spec. You only need to provide Speakeasy with information about the semantics of your API.
Speakeasy applies the following rules recursively across an arbitrary JSON Schema type:
- If a property is marked as
requiredin the CREATE request body, it will be marked asRequired: truein the Terraform schema. If a property is not markedrequired, it will be marked asOptional: true. This is how the property will be reported to an end user invoking the provider usingterraform plan. - If a property is returned in a response body but is not available in the CREATE request, it will be marked as
Computed: true. - If a property is defined in a CREATE request body but not defined in an UPDATE request body, it is marked as
ForceNew. This means that if the property is changed, the pre-existing resource is destroyed and recreated, rather than updated in place. This behavior is set using aPlanModifier. - If you define an attribute as an enum, a runtime type check
Validatoris configured to ensure that all request properties must match one of those values exactly. - Every parameter that is required to make a READ, UPDATE, or DELETE call must be returned by the CREATE API call response body, or always specified as required in a CREATE API request body.
Effect of Varying the “Depth” of the Annotation
The x-speakeasy-entity annotation is an "informative" extension. Its positioning can change how the Terraform provider is generated. For example, if you use the annotation on a top-level object (such as a CREATE response body), all properties under it will be available as nested objects. If you use it on a more deeply nested object, every property defined further up in the extension will be flattened into the object.
In the following example, x-speakeasy-entity: Pet is applied to the "root" response body. This means that data will be available as a nested object and name will be available as a property under that.
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
x-speakeasy-entity-operation: Pet#create
description: Add a new pet to the store
operationId: addPet
requestBody:
content:
application/json:
schema:
type: object
x-speakeasy-entity: Pet
properties:
id:
type: integer
description: Numeric identifier of the Pet.
data:
type: object
properties:
name:
type: stringresource "yourprovider_pet" "example" {
id = 123123
data = {
name = "Filter Blend"
}
}If we apply the x-speakeasy-entity annotation lower down, we inline the object, and any objects "above" the annotation are flattened into the object:
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
x-speakeasy-entity-operation: Pet#create
description: Add a new pet to the store
operationId: addPet
requestBody:
content:
application/json:
schema:
type: object
properties:
id:
type: integer
description: Numeric identifier of the Pet.
data:
x-speakeasy-entity: Pet
type: object
properties:
name:
type: stringresource "yourprovider_pet" "example" {
id = 123123
name = "Filter Blend"
}Be warned: Any properties "above" the x-speakeasy-entity are always flattened to their primitive type. This can cause conflicts. Always carefully apply the x-speakeasy-entity by understanding exactly how you want your users to interact with your API.
To demonstrate, if you apply x-speakeasy-entity like this:
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
x-speakeasy-entity-operation: Pet#create
description: Add a new pet to the store
operationId: addPet
requestBody:
content:
application/json:
schema:
type: object
properties:
id_label:
type: object
properties:
id:
type: string
data:
x-speakeasy-entity: Pet
type: object
properties:
name:
type: stringThe object would be usable like this:
resource "yourprovider_pet" "example" {
id = 123123
name = "Filter Blend"
}The following unifies the two name attributes, such that a user defines one attribute but it will be set twice in the object request.
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
x-speakeasy-entity-operation: Pet#create
description: Add a new pet to the store
operationId: addPet
requestBody:
content:
application/json:
schema:
type: object
properties:
id_label:
type: object
properties:
name:
type: string
data:
x-speakeasy-entity: Pet
type: object
properties:
name:
type: stringresource "yourprovider_pet" "example" {
name = "Filter Blend"
}This would invoke the create API call with the following:
{
"id_label": {
"name": "Filter Blend"
},
"data": {
"name": "Filter Blend"
}
}Create Request Parameters
Similar to "parent" objects above the x-speakeasy-entity annotation, request parameters are inlined into the Terraform resource, though these are always marked as ForceNew when required: Any change to these request parameters will force a full destroy-and-recreate cycle.
For instance, the following:
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
x-speakeasy-entity-operation: Pet#create
parameters:
- in: query
name: dryRun
schema:
type: boolean
required: true
description: Add a new pet to the store
operationId: addPet
requestBody:
content:
application/json:
schema:
x-speakeasy-entity: Pet
type: object
properties:
name:
type: stringWould enable the following resource interaction:
resource "yourprovider_pet" "example" {
dry_run = true
name = "Filter Blend"
}The x-speakeasy-entity-operation Annotation
Use the x-speakeasy-entity-operation annotation to specify which endpoints in your OpenAPI spec are used to create, read, update, or delete a Terraform entity.
The value of this annotation is a string in the format of Entity#operation,operation,..., where Entity is the name of the entity, and operation is one of create, get, update, or delete, or multiple of these concatenated with commas.
Example
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
x-speakeasy-entity-operation: Pet#create
/pet/{petId}:
get:
tags:
- pet
summary: Info for a specific pet
x-speakeasy-entity-operation: Pet#read
update:
tags:
- pet
summary: Update the pet
x-speakeasy-entity-operation: Pet#update
delete:
tags:
- pet
summary: Delete the pet
x-speakeasy-entity-operation: Pet#deleteBehavior of Operations
- If an
Entity:createoperation exists, the entity will be made available as a Terraform resource. - If an
Entity:getoperation exists, it will be used regularly to ensure the resource is consistent with the Terraform state, and to enhance the Terraform state with attributes if the most recent entity state is not returned from anEntity:createorEntity:updateinvocation. - If an
Entity:updateoperation exists, the entity will be made into a Terraform resource withupdatesupport. IfEntity:updatedoes not exist, all attributes areForceNew. - If an
Entity:deleteoperation exists, the entity will be made into a Terraform resource withdeletesupport. IfEntity:deletedoes not exist, then nothing will happen when a user deletes the resource. This is often a bad practice but might be suitable for update-only resources. - If an
Entity:create,updateoperation exists, the API is assumed to be idempotent, and this API will be called both when an attribute changes and if the object is new.
API Parameters
When an API parameter exactly matches an object property on the root level, no additional changes are required.
However, if an API parameter does not match an object property on the root level, the x-speakeasy-match annotation must be used. A generation error will inform you of available root-level properties. The x-speakeasy-match annotation will rename the API parameters and the API parameter will be set to an object available on the Terraform state.
Example
The following example would rewrite the petId parameter to take it from id on the Terraform state.
paths:
/pet/{petId}:
delete:
tags:
- pet
summary: Delete the pet
parameters:
- in: path
name: petId
schema:
type: integer
required: true
x-speakeasy-match: id
x-speakeasy-entity-operation: Pet#deleteThe x-speakeasy-param-readonly Extension
If this extension is set on any type, it will be marked as read-only. If a user attempts to specify it, a runtime error is raised.
Example
components:
schemas:
Pet:
type: object
properties:
name:
type: string
id:
type: integer
x-speakeasy-param-readonly: trueThe x-speakeasy-param-optional Extension
Set this extension on any type to mark it as optional. This will override required from the JSON Schema specification.
Example
components:
schemas:
Pet:
type: object
properties:
name:
type: string
id:
type: integer
x-speakeasy-param-optional: trueThe x-speakeasy-param-force-new Extension
Set this extension on any type to mark it as ForceNew. Any change will cause a full object recreation.
Example
components:
schemas:
Pet:
type: object
properties:
name:
type: string
id:
type: integer
x-speakeasy-param-force-new: trueThe x-speakeasy-param-sensitive Extension
Set this extension on any type to mark it as Sensitive. It will be masked in the Terraform state and when printed on the console.
Example
components:
schemas:
Pet:
type: object
properties:
name:
type: string
secret:
type: string
x-speakeasy-param-sensitive: trueThe x-speakeasy-terraform-ignore: true Extension
Set this extension to true to remove this attribute and any interaction with it from the Terraform state.
Example
components:
schemas:
Pet:
x-speakeasy-entity: Pet
type: object
properties:
optionalMetadata:
x-speakeasy-terraform-ignore: true
type: string
name:
type: string
required:
- nameresource "petstore_pet" "mypet" {
name = "myPet"
# Attempting to set an ignored parameter results in an error
# optionalMetadata = true
}The x-speakeasy-type-override: "any" Extension
This extension can act as an escape hatch, replacing the attribute with a JSON string that can be provided inline. This is useful to improve the interface to attributes that are not well defined.
components:
schemas:
Pet:
x-speakeasy-entity: Pet
type: object
properties:
deep:
x-speakeasy-type-override: any
type: object
properties:
object:
type: object
additionalProperties: true
properties:
in:
type: object
properties:
here:
type: string
name:
type: string
required:
- nameresource "petstore_pet" "mypet" {
name = "myPet"
deep = jsonencode({
object = {
with = "anything"
defined = true
}
})
}The x-speakeasy-param-suppress-computed-diff: true Extension
Usually, Terraform will appropriately detect changes in attributes and only make changes to attributes that have changed. However, in some cases, you might see spurious plan changes for computed attributes that are (known after apply).
If this is the case, you can add an aggressive plan modifier to those attributes using x-speakeasy-param-suppress-computed-diff: true that will only mark the attribute as changing if:
- A GET request is available, and after making it to the API for the resource, the attribute in the plan differs from that in the API response.
- The initial creation API call is unavailable.
This extension is applied recursively downwards to any attributes within the tagged attribute.
Note: Applying this modifier when x-speakeasy-entity-operation: my_resource#read is not defined may result in drift between the Terraform plan and remote state, should updates to attributes happen outside of Terraform changes. Please only apply this when necessary.
Example
components:
schemas:
Pet:
x-speakeasy-entity: Pet
type: object
properties:
deep:
x-speakeasy-param-suppress-computed-diff: true
type: object
properties:
object:
type: object
properties:
in:
type: object
properties:
here:
type: string
name:
type: string
required:
- name
---
# Is the same as
components:
schemas:
Pet:
x-speakeasy-entity: Pet
type: object
properties:
deep:
x-speakeasy-param-suppress-computed-diff: true
type: object
properties:
object:
x-speakeasy-param-suppress-computed-diff: true
type: object
properties:
in:
x-speakeasy-param-suppress-computed-diff: true
type: object
properties:
here:
x-speakeasy-param-suppress-computed-diff: true
type: string
name:
type: string
required:
- name