The x-speakeasy-entity Annotation
Add the x-speakeasy-entity annotation to objects in your OpenAPI spec to include them as entities 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: object
resource "yourprovider_order" "example" { name = "Filter Blend" price = 11.5}
Where you place the x-speakeasy-entity annotation affects the Terraform provider structure.
- At the top level: Properties are nested objects.
- At a lower Level: Properties above the annotation are flattened.
Top Level
Pet: x-speakeasy-entity: Pet ...
resource "yourprovider_pet" "example" { data = { name = "Filter Blend" }}
Lower Level
Pet: properties: data: x-speakeasy-entity: Pet ...
resource "yourprovider_pet" "example" { name = "Filter Blend"}
Warning
Properties above the x-speakeasy-entity annotation are flattened, which could cause conflicts. Apply the annotation carefully to align the structure of the Terraform provider with your API's intended user interaction.
The x-speakeasy-entity-operation Annotation
The x-speakeasy-entity-operation annotation specifies CRUD (create, read, update, and delete) operations associated with each endpoint in your OpenAPI spec for a Terraform entity.
Use Case
Use the x-speakeasy-entity-operation annotation to precisely map API capabilities to Terraform resource management and ensure each API endpoint is accurately represented in Terraform operations for consistent behavior with the API.
Format
The value is structured as Entity#operation,operation,...#order:
Entityrepresents the name of the entity.operationcan be one or more ofcreate,get,update, anddelete, concatenated with commas.orderis optional and can be used to define a second API call that should be invoked for a given CRUD invocation.
Behavior of Operations
Entity:createmakes the entity a Terraform resource.Entity:readensures consistency with Terraform state and updates attributes.Entity:updateprovides update support for the resource. Without it, attributes areForceNew.Entity:deleteenables deletion of the resource. Without it, no action is taken on deletion.Entity:create,update(Idempotent Operations) indicates the API is idempotent. Combine these operations to allow for the same API call to both create new objects and update existing ones, depending on attribute changes.
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
One API Operation, Multiple Resources
You can optionally annotate one operation with multiple entity annotations. This is useful, for example, when a single API call is used across multiple resources, or annotates or adds additional information to operations.
parameters: - in: query name: id required: false schema: type: stringoperationId: GetMetadataForResourceOrGroupx-speakeasy-entity-operation: - Resource#read#2 - Group#read#2
The x-speakeasy-match Annotation
The x-speakeasy-match annotation adjusts the API parameter name to align with a Terraform state property. If mismatches occur, a generation error will highlight appropriate root-level properties for accurate mapping.
Use Case
Use x-speakeasy-match when an API parameter doesn't match a root-level object property.
paths: /pet/{petId}: delete: parameters: - name: petId x-speakeasy-match: id x-speakeasy-entity-operation: Pet#delete
The x-speakeasy-param-readonly Extension
The x-speakeasy-param-readonly extension marks a property as read-only. Any user attempt to modify it in Terraform will result in a runtime error.
Use Case
Use this extension to prevent unintended changes to critical properties in Terraform configurations.
components: schemas: Pet: type: object properties: name: type: string id: type: integer x-speakeasy-param-readonly: true
The x-speakeasy-param-optional Extension
Apply x-speakeasy-param-optional to any property to designate it as optional. This extension takes precedence over the required attribute in the JSON Schema specification.
Use case
Provides flexibility in Terraform configurations by allowing optional settings for certain properties.
components: schemas: Pet: type: object properties: name: type: string id: type: integer x-speakeasy-param-optional: true
The x-speakeasy-param-force-new Extension
Properties marked with this extension will cause the associated Terraform resource to be destroyed and recreated whenever the property value changes. This setting ensures that any alteration to the property triggers a complete recreation of the object.
Use Case
Use the x-speakeasy-param-force-new extension to mark properties that, when altered, require the creation of a new resource instance for proper application.
components: schemas: Pet: type: object properties: name: type: string id: type: integer x-speakeasy-param-force-new: true
The x-speakeasy-param-sensitive Extension
Properties marked as sensitive will be obscured in the Terraform state and concealed when displayed in the console. This ensures the confidentiality of sensitive data within Terraform operations.
Use Case
This extension is ideal for handling confidential properties like passwords, tokens, or personal data in Terraform by hiding sensitive information from logs and state files.
components: schemas: Pet: type: object properties: name: type: string secret: type: string x-speakeasy-param-sensitive: true
The x-speakeasy-terraform-ignore: true Extension
When set to true, this extension ensures that the specified property and any interactions involving it are omitted from Terraform's state management.
Info
Please note that this extension is used for complete suppression of the property from terraform state. If you are looking to suppress from only the specific operation consider using x-speakeasy-ignore: true.
This omits the operation only the annotated CRUD method. E.g. if a field is both in CREATE response body and READ response body, omitting it from READ response body just turns off diff detection for that field. It's still in CREATE response so it'll still be in terraform state
Use Case
This extension is useful for excluding irrelevant, unnecessary, or temporary properties from Terraform's state to keep it clean and focused.
components: schemas: Pet: x-speakeasy-entity: Pet type: object properties: optionalMetadata: x-speakeasy-terraform-ignore: true type: string name: type: string required: - name
resource "petstore_pet" "mypet" { name = "myPet" # Attempting to set an ignored parameter results in an error # optionalMetadata = true}
The x-speakeasy-type-override: "any" Extension
Set the x-speakeasy-type-override extension to "any" to convert the associated attribute to a JSON string. This allows for inline specification of the attribute's value.
Use Case
Use this extension to accommodate attributes with variable or dynamic structures by allowing the provision of a JSON string inline.
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: - name
resource "petstore_pet" "mypet" { name = "myPet" deep = jsonencode({ object = { with = "anything" defined = true } })}
The x-speakeasy-param-suppress-computed-diff: true Extension
Setting the x-speakeasy-param-suppress-computed-diff to true suppresses unnecessary Terraform plan changes for computed attributes that are not definitively known until after application (marked as "known after apply"). An attribute is marked as changed only if a GET request confirms a difference between the plan and the API response or the initial creation API call is unavailable. Applies to all nested attributes within the tagged attribute.
Use Case
Useful in scenarios where computed attributes frequently cause spurious plan changes, leading to unnecessary updates in Terraform.
components: schemas: Pet: x-speakeasy-entity: Pet type: object properties: name: type: string status: x-speakeasy-param-suppress-computed-diff: true type: string
Warning
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.
The x-speakeasy-conflicts-with Extension
This extension indicates that a property conflicts with another, ensuring that certain combinations of properties are not set together. Accepts relative paths to conflicting attributes.
Use Case
Ideal for situations where certain attributes are mutually exclusive or where setting one attribute invalidates another.
components: schemas: Pet: x-speakeasy-entity: Pet type: object properties: name: type: string name_prefix: type: string x-speakeasy-conflicts-with: name id: type: string generated_name_options: type: object properties: prefix: type: string x-speakeasy-conflicts-with: - ../name_prefix - ../name - ../id
resource "example_pet" "happy_pet" { name = "Mrs Poppy" name_prefix = "Mrs"}
$ terraform plan│ Error: Invalid Attribute Combination│ │ with example_pet.happy_pet,│ on provider.tf line 39, in resource "example_pet" "happy_pet":│ 3: name_prefix = "test"│ │ Attribute "name" cannot be specified when "name_prefix" is specified
The x-speakeasy-plan-validators Extension
Use the x-speakeasy-plan-validators extension to add custom validation logic (opens in a new tab) to Terraform plan operations and ensure configurations meet predefined criteria before execution. Apply this extension to a schema attribute to trigger the automatic generation of a plan validator compatible with the Terraform plugin framework, tailored to the attribute's type. This process enhances the provider's ability to enforce complex validation rules.
Validators created through this extension are organized in a structured directory path beginning with internal/validators/, ensuring a clean and manageable codebase.
Use Case
This extension is essential for scenarios requiring advanced validation logic that JSON Schema cannot accommodate. The extension allows you to implement intricate, context-specific validation strategies, ensuring your Terraform provider operates with enhanced accuracy and reliability.
components: schemas: Pet: type: object x-speakeasy-entity: Pet properties: name: type: string age: type: integer x-speakeasy-plan-validators: AgeValidator
In this scenario, when Speakeasy next generates the Terraform provider, it will bootstrap a custom validator file located at internal/validators/int64validators/age_validator.go, and import the schema configuration wherever x-speakeasy-plan-validators: AgeValidator is referenced. You can modify the validator file to contain your logic.
Implementation Notes
-
A plan validator is a type conformant to the
terraform-plugin-frameworkexpected interface. A unique plan validator will be bootstrapped in the appropriate subfolder for the Terraform type it is applied to:boolvalidators,float64validators,int64validators,listvalidators,mapvalidators,numbervalidators,objectvalidators,setvalidators, orstringvalidators. Speakeasy will always create and use a file assnake_case.gofor a givenx-speakeasy-plan-validatorsvalue. -
A plan validator operates on the raw (untyped) Terraform value types. However, you can convert a Terraform type to a value type Speakeasy manages (
type_mytype.go) by using the included reflection utility. This is useful for applying validators to complex types likelist,map,object, andset. -
While working with a plan validator, you have the ability to perform various tasks, including initiating network requests. However, it's important to ensure that plan validations do not result in any unintended side effects. Please refer to the HashiCorp guidance on plan validator development (opens in a new tab) or reach out in our Slack if you have questions.
-
It is possible to have an array of plan validators, for example,
x-speakeasy-plan-validators: [MinAgeValidator, MaxAgeValidator]. -
A validator can only be applied to a resource attribute. Validators cannot be applied at the same level as the
x-speakeasy-entityannotation because that becomes the "root" of the Terraform resource. However, validators can access or refer to any data in the entire resource (for an example, see thex-speakeasy-conflicts-withvalidator). The annotation will be ignored for data sources. -
Speakeasy regenerations do not delete user-written code. If the validator is no longer in use, it will be ignored (no longer referenced) but the source file will remain. You might want to delete such an orphaned validation file for repository hygiene.
Other Keywords and Annotations
Tip
This section is not an exhaustive list of available keyword and annotation options. If you're unsure whether a keyword or annotation is supported, please reach out to our team at support@speakeasyapi.dev.
The anyOf Keyword
Terraform has limited support for the anyOf keyword due to its less flexible type system than JSON Schema. For instance, managing anyOf with multiple subtypes requires a large set of combined types, leading to practical and implementation challenges.
Consider replacing anyOf in your schema with oneOf or allOf. This adjustment aligns with Terraform's capabilities: oneOf for union types and allOf for intersection types.
For more guidance or to discuss schema adaptations, contact our support team at support@speakeasyapi.dev.
The oneOf Keyword
In Terraform, OneOf is defined as a SingleNestedAttribute where each potential child is represented by a unique key. To ensure compliance with oneOf semantics, an object plan validator is used to confirm that only one of these keys is active at any given time.
The allOf Keyword
For allOf, Speakeasy merges all sub-schemas into a single combined attribute, creating a unified schema component that encapsulates all specified properties.