Add or modify a resource #
This page describes how to add a new resource to the google
or google-beta
Terraform provider using MMv1 and/or handwritten code.
For more information about types of resources and the generation process overall, see How Magic Modules works.
Before you begin #
- Complete the Generate the providers quickstart to set up your environment and your Google Cloud project.
- Ensure that your
magic-modules
,terraform-provider-google
, andterraform-provider-google-beta
repositories are up to date.cd ~/magic-modules git checkout main && git clean -f . && git checkout -- . && git pull cd $GOPATH/src/github.com/hashicorp/terraform-provider-google git checkout main && git clean -f . && git checkout -- . && git pull cd $GOPATH/src/github.com/hashicorp/terraform-provider-google-beta git checkout main && git clean -f . && git checkout -- . && git pull
Add a resource #
Using an editor of your choice, in the appropriate product folder, create a file called
RESOURCE_NAME.yaml
. ReplaceRESOURCE_NAME
with the name of the API resource you are adding support for. For example, a configuration file for NatAddress would be calledNatAddress.yaml
.Copy the following template into the new file:
# Copyright 2024 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. --- # API resource name name: 'ResourceName' # Resource description for the provider documentation. description: | RESOURCE_DESCRIPTION references: guides: # Link to quickstart in the API's Guides section. For example: # 'Create and connect to a database': 'https://cloud.google.com/alloydb/docs/quickstart/create-and-connect' 'QUICKSTART_TITLE': 'QUICKSTART_URL' # Link to the REST API reference for the resource. For example, # https://cloud.google.com/alloydb/docs/reference/rest/v1/projects.locations.backups api: 'API_REFERENCE_URL' # Marks the resource as beta-only. Ensure a beta version block is present in # provider.yaml. # min_version: beta # URL for the resource's standard List method. https://google.aip.dev/132 # Terraform field names enclosed in double curly braces are replaced with # the field values from the resource at runtime. base_url: 'projects/{{project}}/locations/{{location}}/resourcenames' # URL for the resource's standard Get method. https://google.aip.dev/131 # Terraform field names enclosed in double curly braces are replaced with # the field values from the resource at runtime. self_link: 'projects/{{project}}/locations/{{location}}/resourcenames/{{name}}' # If true, the resource and all its fields are considered immutable - that is, # only creatable, not updatable. Individual fields can override this if they # have a custom update method in the API. # immutable: true # URL for the resource's standard Create method, including query parameters. # https://google.aip.dev/133 # Terraform field names enclosed in double curly braces are replaced with # the field values from the resource at runtime. create_url: 'projects/{{project}}/locations/{{location}}/resourcenames?resourceId={{name}}' # Overrides the URL for the resource's standard Update method. (If unset, the # self_link URL is used by default.) https://google.aip.dev/134 # Terraform field names enclosed in double curly braces are replaced with # the field values from the resource at runtime. # update_url: 'projects/{{project}}/locations/{{location}}/resourcenames/{{name}}' # The HTTP verb used to update a resource. Allowed values: :POST, :PUT, :PATCH. Default: :PUT. update_verb: 'PATCH' # If true, the resource sets an `updateMask` query parameter listing modified # fields when updating the resource. If false, it does not. update_mask: true # If true, code for handling long-running operations is generated along with # the resource. If false, that code is not generated. autogen_async: true # Sets parameters for handling operations returned by the API. async: # Overrides which API calls return operations. Default: ['create', # 'update', 'delete'] # actions: ['create', 'update', 'delete'] operation: base_url: '{{op_id}}' parameters: - name: 'location' type: String required: true immutable: true url_param_only: true description: | LOCATION_DESCRIPTION - name: 'name' type: String required: true immutable: true url_param_only: true description: | NAME_DESCRIPTION properties: # Fields go here
Modify the template as needed to match the API resource’s documented behavior.
Delete all remaining comments in the resource configuration (including attribute descriptions) that were copied from the above template.
Note: The template includes the most commonly-used fields. For a comprehensive reference, see MMv1 resource reference ↗.
Warning: Handwritten resources are more difficult to develop and maintain. New handwritten resources will only be accepted if implementing the resource in MMv1 would require entirely overriding two or more CRUD methods.
- Add the resource in MMv1.
- Generate the beta provider
- From the beta provider, copy the files generated for the resource to the following locations:
- Resource: Copy to the appropriate service folder inside
magic-modules/mmv1/third_party/terraform/services
- Documentation:
magic-modules/mmv1/third_party/terraform/website/docs/r
- Tests: Copy to the appropriate service folder inside
magic-modules/mmv1/third_party/terraform/services
, and remove_generated
from the filename - Sweepers:
magic-modules/mmv1/third_party/terraform/utils
- Resource: Copy to the appropriate service folder inside
- Modify the Go code as needed.
- Replace all occurrences of
github.com/hashicorp/terraform-provider-google-beta/google-beta
withgithub.com/hashicorp/terraform-provider-google/google
- Remove the
Example
suffix from all test function names. - Remove the comments at the top of the file.
- If beta-only fields are being tested, do the following:
- Change the file suffix to
.go.tmpl
- Wrap each beta-only test in a separate version guard:
{{- if ne $.TargetVersionName "ga" -}}...{{- else }}...{{- end }}
- Change the file suffix to
- Replace all occurrences of
- Register the resource
handwrittenResources
inmagic-modules/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb
- Add a version guard for any beta-only resources.
- Optional: Complete other handwritten tasks that require the MMv1 configuration file.
- Delete the MMv1 configuration file.
Add fields #
In general, Terraform resources should implement all configurable fields and all read-only fields. Even fields that seem like they would not be useful in Terraform (like update time or etag) often end up being requested by users, so it’s usually easier to just add them all at once. However, optional or read-only fields can be omitted when adding a resource if they would require significant additional work to implement.
- For each API field, copy the following template into the resource’s
properties
attribute. Be sure to indent appropriately.
- name: 'API_FIELD_NAME'
type: String
description: |
MULTILINE_FIELD_DESCRIPTION
min_version: beta
immutable: true
required: true
output: true
conflicts:
- field_one
- nested_object.0.nested_field
exactly_one_of:
- field_one
- nested_object.0.nested_field
Replace String
in the field type with one of the following options:
String
Integer
Boolean
Double
KeyValuePairs
(string -> string map)KeyValueLabels
(for standard resource ’labels’ field)KeyValueAnnotations
(for standard resource ‘annotations’ field)
- name: 'API_FIELD_NAME'
type: Enum
description: |
MULTILINE_FIELD_DESCRIPTION
min_version: beta
immutable: true
required: true
output: true
conflicts:
- field_one
- nested_object.0.nested_field
exactly_one_of:
- field_one
- nested_object.0.nested_field
enum_values:
- 'VALUE_ONE'
- 'VALUE_TWO'
- name: 'API_FIELD_NAME'
type: ResourceRef
description: |
MULTILINE_FIELD_DESCRIPTION
min_version: beta
immutable: true
required: true
output: true
conflicts:
- field_one
- nested_object.0.nested_field
exactly_one_of:
- field_one
- nested_object.0.nested_field
resource: 'ResourceName'
imports: 'name'
- name: 'API_FIELD_NAME'
type: Array
description: |
MULTILINE_FIELD_DESCRIPTION
min_version: beta
immutable: true
required: true
output: true
conflicts:
- field_one
- nested_object.0.nested_field
exactly_one_of:
- field_one
- nested_object.0.nested_field
# Array of primitives
item_type:
type: String
# Array of nested objects
item_type:
type: NestedObject
properties:
- name: 'FIELD_NAME'
type: String
description: |
MULTI_LINE_FIELD_DESCRIPTION
- name: 'API_FIELD_NAME'
type: NestedObject
description: |
MULTILINE_FIELD_DESCRIPTION
min_version: beta
immutable: true
required: true
output: true
conflicts:
- field_one
- nested_object.0.nested_field
exactly_one_of:
- field_one
- nested_object.0.nested_field
properties:
- name: 'FIELD_NAME'
type: String
description: |
MULTI_LINE_FIELD_DESCRIPTION
- name: 'API_FIELD_NAME'
type: Map
description: |
MULTILINE_FIELD_DESCRIPTION
key_name: 'KEY_NAME'
key_description: |
MULTILINE_KEY_FIELD_DESCRIPTION
value_type:
name: mapObjectName
type: NestedObject
properties:
- name: 'FIELD_NAME'
type: String
description: |
MULTI_LINE_FIELD_DESCRIPTION
This type is only used for string -> complex type mappings, use “KeyValuePairs” for simple mappings. Complex maps can’t be represented natively in Terraform, and this type is transformed into an associative array (TypeSet) with the key merged into the object alongside other top-level fields.
For key_name
and key_description
, provide a domain-appropriate name and description. For example, a map that references a specific type of resource would generally use the singular resource kind as the key name (such as “topic” for PubSub Topic) and a descriptor of the expected format depending on the context (such as resourceId vs full resource name).
- Modify the field configuration according to the API documentation and behavior.
Note: The templates in this section only include the most commonly-used fields. For a comprehensive reference, see MMv1 field reference. For information about modifying the values sent and received for a field, see Modify the API request or response.
- Add the field to the handwritten resource’s schema.
- The new field(s) should mirror the API’s structure to ease predictability and maintenance. However, if there is an existing related / similar field in the resource that uses a different convention, follow that convention instead.
- Enum fields in the API should be represented as
TypeString
in Terraform for forwards-compatibility. Link to the API documentation of allowed values in the field description. - Terraform field names should always use snake case ↗.
- See Schema Types ↗ and Schema Behaviors ↗ for more information about field schemas.
- Add handling for the new field in the resource’s Create method and Update methods.
- “Expanders” convert Terraform resource data to API request data.
- For top level fields, add an expander. If the field is set or has changed, call the expander and add the resulting value to the API request.
- For other fields, add logic to the parent field’s expander to add the field to the API request. Use a nested expander for complex logic.
- Add handling for the new field in the resource’s Read method.
- “Flatteners” convert API response data to Terraform resource data.
- For top level fields, add a flattener. Call
d.Set()
on the flattened API response value to store it in Terraform state. - For other fields, add logic to the parent field’s flattener to convert the value from the API response to the Terraform state value. Use a nested flattener for complex logic.
- If any of the added Go code (including any imports) is beta-only, change the file suffix to
.go.tmpl
and wrap the beta-only code in a version guard:{{- if ne $.TargetVersionName "ga" -}}...{{- else }}...{{- end }}
.- Add a new guard rather than adding the field to an existing guard; it is easier to read.
Add IAM support #
This section covers how to add IAM resources in Terraform if they are supported by a particular API resource (indicated by setIamPolicy
and getIamPolicy
methods in the API documentation for the resource).
IAM support for MMv1-generated resources is configured within the ResourceName.yaml
file, and will create the google_product_resource_iam_policy
, google_product_resource_iam_binding
, google_product_resource_iam_member
resource, website, and test files for that resource target when an iam_policy
block is present.
- Add the following top-level block to
ResourceName.yaml
directly aboveparameters
.
iam_policy:
# Name of the field on the terraform IAM resources which references
# the parent resource. Update to match the parent resource's name.
parent_resource_attribute: 'resource_name'
# Character preceding setIamPolicy in the full URL for the API method.
# Usually `:`
method_name_separator: ':'
# HTTP method for getIamPolicy. Usually 'POST'.
fetch_iam_policy_verb: 'POST'
# Overrides the HTTP method for setIamPolicy. Default: 'POST'
# set_iam_policy_verb: 'POST'
# Must match the parent resource's `import_format` (or `self_link` if
# `import_format` is unset), but with the `parent_resource_attribute`
# value substituted for the final field.
import_format:
- 'projects/{{project}}/locations/{{location}}/resourcenames/{{resource_name}}'
# If IAM conditions are supported, set this attribute to indicate how the
# conditions should be passed to the API. Allowed values: 'QUERY_PARAM',
# 'REQUEST_BODY', 'QUERY_PARAM_NESTED'. Note: 'QUERY_PARAM_NESTED' should
# only be used if the query param field contains a `.`
# iam_conditions_request_type: 'REQUEST_BODY'
# Marks IAM support as beta-only
# min_version: beta
- Modify the template as needed to match the API resource’s documented behavior. These are the most commonly-used fields. For a comprehensive reference, see MMv1 resource reference:
iam_policy
↗. - Delete all remaining comments in the IAM configuration (including attribute descriptions) that were copied from the above template.
Warning: IAM support for handwritten resources should be implemented using MMv1. New handwritten IAM resources will only be accepted if they cannot be implemented using MMv1.
Add support in MMv1 #
- Follow the MMv1 directions in Add the resource to create a skeleton ResourceName.yaml file for the handwritten resource, but set only the following top-level fields:
name
description
(required but unused)base_url
(set to URL of IAM parent resource)self_link
(set to same value asbase_url
)id_format
(set to same value asbase_url
)import_format
(includingbase_url
value)exclude_resource
(set totrue
)properties
- Follow the MMv1 directions in Add fields to add only the fields used by base_url.
- Follow the MMv1 directions in this section to add IAM support.
Convert to handwritten (not usually necessary) #
- Generate the beta provider
- From the beta provider, copy the files generated for the IAM resources to the following locations:
- Resource: Copy to the appropriate service folder inside
magic-modules/mmv1/third_party/terraform/services
- Documentation:
magic-modules/mmv1/third_party/terraform/website/docs/r
- Tests: In the appropriate service folder inside
magic-modules/mmv1/third_party/terraform/services
- Resource: Copy to the appropriate service folder inside
- Modify the Go code as needed.
- Replace all occurrences of
github.com/hashicorp/terraform-provider-google-beta/google-beta
withgithub.com/hashicorp/terraform-provider-google/google
- Remove the comments at the top of the file.
- If any of the added Go code is beta-only:
- Change the file suffix to
.go.tmpl
- Wrap each beta-only code block (including any imports) in a separate version guard:
{{- if ne $.TargetVersionName "ga" -}}...{{- else }}...{{- end }}
- Change the file suffix to
- Replace all occurrences of
- Register the binding, member, and policy resources
handwrittenIAMResources
inmagic-modules/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.tmpl
- Add a version guard for any beta-only resources.
Add documentation #
Documentation is autogenerated based on the resource and field configurations. To preview the documentation:
- Generate the providers
- Copy and paste the generated documentation into the Hashicorp Registry’s Doc Preview Tool to see how it is rendered.
Add or modify documentation files #
- Open the resource documentation in
magic-modules/third_party/terraform/website/docs/r/
using an editor of your choice.- The name of the file is the name of the resource without a
google_
prefix. For example, forgoogle_compute_instance
, the file is calledcompute_instance.html.markdown
- The name of the file is the name of the resource without a
- Modify the documentation as needed according to Handwritten documentation style guide.
- Generate the providers
- Copy and paste the generated documentation into the Hashicorp Registry’s Doc Preview Tool to see how it is rendered.