Implement create
Note: We recommend using the Terraform Plugin Framework for new provider development because it offers significant advantages compared to the SDKv2. Refer to the Plugin Framework tutorials to learn how to create providers using the framework.
In this tutorial, you will create an order
resource for a Terraform provider that interacts with the API of a fictional coffee-shop application called Hashicups. Then, you will use the schema to create a new order and populate the order. To do this, you will:
- Define
order
resource.
You will add a scaffold that defines an empty schema and functions to create, read, update and delete orders. - Define
order
schema.
The schema contains fields used to create a new order and retrieved by the read functionality. - Add create functionality.
The create function invokes aPOST
request to/orders
to create a new order. - Add read functionality.
The read function invokes aGET
request to/orders/{orderID}
to retrieve the order's data. The orders schema contains a nested object, therefore you will use multiple flattening functions to map the response to theorder
schema. - Add
order
resource to the provider schema.
This allows you to use the resource in your configuration.
Prerequisites
To follow this tutorial, you need:
- a Golang 1.15+ installed and configured.
- the Terraform 0.14+ CLI installed locally.
- Docker and Docker Compose to run an instance of HashiCups locally.
Navigate to your terraform-provider-hashicups
directory. Next, fetch new changes.
Then, checkout the debug-tf-provider
branch. This step is optional but recommended to ensure that your code matches the code from the previous tutorials.
Your directory should have the following structure.
If you're stuck at any point during this tutorial, refer to the implement-create
branch to see the changes implemented in this tutorial.
Define order
resource
Add the following code snippet to a new file named hashicups/resource_order.go
in the hashicups
directory. As a general convention, Terraform providers put each resource in their own file, named after the resource, prefixed with resource_
.
This serves as a scaffold for the order
resource. Notice, the order resource defines create, read, update and delete (CRUD) functionality. In this tutorial, you will only implement create and read to create and read a new order
resource.
Format your code.
Define order
schema
To create a HashiCups order, you would send a POST request to the /orders
endpoint with a list of OrderItems
, containing the coffee object and its respective quantity.
Replace the line Schema: map[string]*schema.Schema{},
in your resourceOrder
function with the following schema. The order
resource schema should resemble the request body.
Format your code.
This order
schema should be similar to the order
data resource defined in the previous Implement Complex Read tutorial with two significant changes.
- The top level
id
attribute is missing in theorder
resource, but present in theorder
data resource. Unlike theorder
data source, you don't know the order ID ahead of time. When HashiCups provider invokes the HashiCups API, the API generates an order ID which is then assigned as the resource's ID by the HashiCups provider. items
is arequired
field, not a computed one. This is because theOrderItems
need to be defined to create an order.
There are multiple ways to nest maps.
- The first (see
coffee
object) is to define the nested object as anschema.TypeList
with 1 item. This is currently the closest way to emulate a nested object. - The second way is to use a
schema.TypeMap
. This method may be preferable if you only require a key value map of primitive types. However, you should use a validation function to enforce required keys.
Notice that the coffee object is represented as a schema.TypeList
of one item. This method was selected because it closely matches the coffee object returned in the response.
Implement create
Now that you have defined the order
resource schema, replace the resourceOrderCreate
function in hashicups/resource_order.go
with the following code snippet. This function will create a new HashiCups order and Terraform resource.
Since this uses strconv
to convert the ID into a string, import the strconv
library. Also, import the HashiCups API client library.
Format your code.
Notice the m
(meta) input parameter contains the HashiCups API Client set by the ConfigureContextFunc
defined above. If an unauthenticated API Client is provided (no username/password), this function will fail and return an error message.
The function retrieves the order attributes using d.Get("items")
— order items are located in items
as defined in the schema — and typecasts it to a []interface{}
. Then, the function transforms the items into a slice of OrderItem
, a struct required by the HashiCups API client to create an order.
Finally, upon successful order creation, SetID
sets the resource ID to the order ID. The resource ID must be a non-blank string that can be used to read the resource again. If no ID is set, Terraform assumes the resource was not created successfully; as a result, no state will be saved for that resource.
Implement read
Now that you have implemented create, you must implement read to retrieve the resource current state. This will be run during the plan
process to determine whether the resource needs to be updated.
Replace the resourceOrderRead
function in hashicups/resource_order.go
with the following code snippet.
Format your code.
This function should be similar to the dataSourceOrderRead
function in hashicups/data_resource_order.go
with two significant change.
- The
order
resource's read function (resourceOrderRead
) usesd.ID()
as the order ID. Theorder
data resource's read function (dataSourceOrderRead
) uses an order ID provider by the user. - The function uses
flattenOrderItems
instead offlattenOrderItemsData
because theorder
resource and theorder
data source uses different schemas. You will define theflattenOrderItems
in the next section.
Add flattening functions
The API Client's GetOrder
function returns an order object that needs to be "flattened" so the provider can accurately map it to the order schema
. An order consists of an order ID and a list of coffee objects and their respective quantities.
As a result, it must go through two flattening functions:
- The first flattening function populates the list of coffee objects and their quantities
- The second flattening function populates the actual coffee object itself.
Add the following function to your hashicups/resource_order.go
file. This is the first flattening function to return a list of order items. The flattenOrderItems
function takes an *[]hc.OrderItem
as orderItems
. If orderItems
is not nil, it will iterate through the slice and map its values into a map[string]interface{}
.
Then, add the second flattening function, flattenCoffee
, to your hashicups/resource_order.go
file. This is called in the first flattening function. It takes a hc.Coffee
and returns a slice with a single object. Notice how this mirrors the coffee schema — a schema.TypeList
with a maximum of one item.
Format your code.
Add read function to create function
Finally, add resourceOrderRead
to the bottom of your resourceOrderCreate
function. This will populate the Terraform state to its current state after the resource creation.
Format your code.
Add order
resource to provider
Now that you've defined the order
resource, you can add it to your provider.
In your hashicups/provider.go
file, add the order
resource to the ResourceMap. Resources and data sources names must follow the <provider>_<resource_name>
convention.
Format your code.
Test the provider
Now that you've created the order
resource with create and read capabilities, verify that it works.
First, confirm that you are in the terraform-provider-hashicups
root directory.
Next, build the binary and move it into your user Terraform plugins directory. This allows you to sideload and test the custom provider. Select the tab for your operating system for specific instructions.
Tip
The Perform CRUD operations with Providers tutorial explains why and how to sideload custom providers. Refer to it to learn more about where to install custom providers and how to reference them in your configuration.
Navigate to the terraform-provider-hashicups/examples
directory. This contains a sample Terraform configuration for the Terraform HashiCups provider.
Add the following Terraform configuration to main.tf
.
Initialize your workspace to refresh your HashiCups provider then apply the configuration. This should create an order and return its values in your output.
Next steps
In this tutorial, you created the order
resource with create and read capabilities.
If you were stuck during this tutorial, checkout the implement-create
branch to see the changes implemented in this tutorial.
- The Terraform Provider Scaffold is a quick-start repository for creating a Terraform provider. Use this GitHub template when you're ready to create a custom provider.
- To learn more about the Terraform Plugin SDK, refer to the Terraform Plugin SDK Documentation.