Skip to main content

Configuration

Terraform Provider generation works by extracting a set of resources from your OpenAPI spec, the properties that make up that resource, annotations to those properties, and the methods used to Create, Read, Update and Destroy those resources.

This graph is then compiled into a Terraform Provider.

A set of required annotations exist to help guide the terraform provider generation process, and a set of "override" extensions exist for when you want to induce certain behaviours in terraform.

x-speakeasy-entity

The x-speakeasy-entity annotation is used to specify which objects in your OpenAPI spec represent a Terraform entity. This annotation is required for every object that you want to include in your Terraform provider.

This annotation is only applicable to object types.

Every property in the x-speakeasy-entity annotated object will become available at the root of the terraform resource.

Example

The annotation, as applied to an Order object below, will become available like so:

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: object
resource "yourprovider_order" "example" {
name = "Filter Blend"
price = 11.5
}

Inferred Properties

Speakeasy will infer the following annotations from your annotated JSON schema, without additional effort:

  1. If a property is marked as required, it will be marked as Optional: false in the Terraform schema (and vice versa). I.e. if it is not specified, it will be a run-time error when invoking the provider
  2. If a property is marked as readonly, it will be marked as Computed: true in the Terraform schema. I.e. it will be read-only in Terraform, and will not allow a user to specify it. Alternatively, don't define it in a Request Type, but do in a Response Type for the same effect.

Effect of varying the "depth" of the annotation

The x-speakeasy-entity annotation is an "informative" extension. Where it is used varies the way that a terraform provider is generated. For instance, if it is used on a top-level object (e.g. a Create response body), then all properties under it will be available as nested objects. If it is used deeper down, every property defined "above" the extension will be flattened into the object.

For example, in this case 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: string
resource "yourprovider_pet" "example" {
id = 123123
data = {
name = "Filter Blend"
}
}

If we instead apply the x-speakeasy-entity annotation lower down, we inline the object, maintaining any objects "above" the annotation 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: string
resource "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.

I.e.

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: string

Would be usable like:

resource "yourprovider_pet" "example" {
id = 123123
name = "Filter Blend"
}

And the following would unify 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: string
resource "yourprovider_pet" "example" {
name = "Filter Blend"
}

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: i.e. any change to them will force a full Destruction / Recreation 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: string

Would enable the following resource interaction:

resource "yourprovider_pet" "example" {
dry_run = true
name = "Filter Blend"
}

x-speakeasy-entity-operation

The x-speakeasy-entity-operation annotation is used 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#delete

Behaviour when some operations are missing

  • If an Entity:create operation exists, the entity will be made available as a terraform "resource".
  • If an Entity:get operation exists, the Entity:get operation will be used regularly to ensure the resource is consistent with terraform state, and to enhance the terraform state with attributes if the most recent entity state is not returned from a Entity:create or Entity:update invocation.
  • If an Entity:update operation exists, the entity will be made into a terraform "resource" with update support. I.e. if it does not exist, then all attributes are ForceNew.
  • If an Entity:delete operation exists, the entity will be made into a terraform "resource" with delete support. I.e. if it does not exist, then no effect will happen when a user deletes the. This is often a bad practice, but might be suitable for "update only" resources
  • If an Entity:create,update operation 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. There will be a generation error informing you of available root level properties. This will rename the API parameters such that 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#delete

x-speakeasy-param-readonly

If this extension is set on any type, it will be marked as Readonly. I.e. 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: true

x-speakeasy-param-optional.

If this extension is set on any type, it will be marked as Optional. I.e. 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: true

x-speakeasy-param-force-new

If this extension is set on any type, it will be marked as ForceNew. I.e. 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: true

x-speakeasy-param-sensitive

If this extension is set on any type, it will be marked as Sensitive. I.e. 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: true