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
:
Entity
represents the name of the entity.operation
can be one or more ofcreate
,get
,update
, anddelete
, concatenated with commas.order
is optional and can be used to define a second API call that should be invoked for a given CRUD invocation.
Behavior of Operations
Entity:create
makes the entity a Terraform resource.Entity:read
ensures consistency with Terraform state and updates attributes.Entity:update
provides update support for the resource. Without it, attributes areForceNew
.Entity:delete
enables 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-framework
expected 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.go
for a givenx-speakeasy-plan-validators
value. -
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-entity
annotation 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-with
validator). 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.