| name | tgc-add-new-handwritten-resource-skill |
| description | Add a new handwritten resource to TGC. Use when you need to add a new handwritten resource to TGC Next. |
tgc-add-new-handwritten-resource-skill
When you need to add a new handwritten resource to the TGC Next conversion pipeline, use this skill.
When to Use This Skill
- Use this when adding a new handwritten resource to TGC.
- This is helpful when you need to understand the structure, Go-level files, and registration templates required to implement custom (handwritten) conversion logic for a resource rather than relying on autogenerated converters.
How to Use It
Adding a new handwritten resource to TGC Next involves creating Go-level definitions, implementing the flattener and expander, registering them in code generator templates, and validating.
1. Update the Resource YAML Configuration
Locate the resource's configuration YAML under mmv1/products/<product>/<ResourceName>.yaml.
- Ensure
include_in_tgc_next: true is set at the top level.
- Ensure fields follow the declaration order of the
Resource struct in mmv1/api/resource.go.
- If the resource is fully handwritten in the standard Terraform provider and does not use standard generation,
exclude_resource: true should generally be set to skip standard autogeneration of provider and converter code.
2. Create the Go-level Service Directory and Files
If the product service folder does not exist under mmv1/third_party/tgc_next/pkg/services/<product>/, create it. Then, add three files:
A. resource_<resource_name>.go (Schema Definition)
This file defines the CAI asset type, the Terraform schema name, the resource schema, and handles registry initialization.
package <product>
import (
"github.com/GoogleCloudPlatform/terraform-google-conversion/v7/pkg/cai2hcl/models"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v7/pkg/cai2hcl/registry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const <ResourceName>AssetType string = "whatever.googleapis.com/<AssetType>"
const <ResourceName>SchemaName string = "google_<product>_<resource_name>"
func Resource<ResourceName>() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
Schema: map[string]*schema.Schema{
"field_name": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
func init() {
registry.Schema{
Name: <ResourceName>SchemaName,
ProductName: "<product>",
Type: registry.SchemaTypeResource,
Schema: Resource<ResourceName>(),
}.Register()
}
B. resource_<resource_name>_tfplan2cai.go (Expander / HCL to CAI Converter)
This file converts the planned Terraform resource data into raw CAI asset JSON payloads during roundtrip tests and plan conversions.
package <product>
import (
"github.com/GoogleCloudPlatform/terraform-google-conversion/v7/pkg/tfplan2cai/converters/cai"
)
func <ResourceName>Tfplan2caiConverter() cai.ResourceConverter {
return cai.ResourceConverter{
Convert: Get<ResourceName>CaiObject,
}
}
func Get<ResourceName>CaiObject(d cai.TerraformResourceData, config *cai.Config) ([]cai.Asset, error) {
name, err := cai.AssetName(d, config, "//whatever.googleapis.com/projects/{{project}}/whatevers/{{name}}")
if err != nil {
return []cai.Asset{}, err
}
if obj, err := Get<ResourceName>ApiObject(d, config); err == nil {
return []cai.Asset{{
Name: name,
Type: <ResourceName>AssetType,
Resource: &cai.AssetResource{
Version: "v1",
DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/...",
DiscoveryName: "<ResourceName>",
Data: obj,
},
}}, nil
} else {
return []cai.Asset{}, err
}
}
func Get<ResourceName>ApiObject(d cai.TerraformResourceData, config *cai.Config) (map[string]interface{}, error) {
obj := make(map[string]interface{})
if v, ok := d.GetOk("field_name"); ok {
obj["fieldName"] = v
}
return obj, nil
}
C. resource_<resource_name>_cai2hcl.go (Flattener / CAI to HCL Converter)
This file takes the CAI asset payload and converts it back into HCL resource blocks.
package <product>
import (
"fmt"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v7/pkg/cai2hcl/common"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v7/pkg/cai2hcl/models"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v7/pkg/caiasset"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
type <ResourceName>Cai2hclConverter struct {
name string
schema map[string]*schema.Schema
}
func New<ResourceName>Cai2hclConverter(provider *schema.Provider) models.Cai2hclConverter {
schema := provider.ResourcesMap[<ResourceName>SchemaName].Schema
return &<ResourceName>Cai2hclConverter{
name: <ResourceName>SchemaName,
schema: schema,
}
}
func (c *<ResourceName>Cai2hclConverter) Convert(assets []caiasset.Asset, options *models.ResourceConverterOptions) ([]*models.TerraformResourceBlock, error) {
var blocks []*models.TerraformResourceBlock
for _, asset := range assets {
block, err := c.convertResourceData(asset, options)
if err != nil {
return nil, err
}
if block != nil {
blocks = append(blocks, block)
}
}
return blocks, nil
}
func (c *<ResourceName>Cai2hclConverter) convertResourceData(asset caiasset.Asset, options *models.ResourceConverterOptions) (*models.TerraformResourceBlock, error) {
if asset.Resource == nil || asset.Resource.Data == nil {
return nil, fmt.Errorf("asset resource data is nil")
}
hclData := make(map[string]interface{})
if val, ok := asset.Resource.Data["fieldName"]; ok {
hclData["field_name"] = val
}
ctyVal, err := common.MapToCtyValWithSchema(hclData, c.schema)
if err != nil {
return nil, err
}
var resourceName string
if options != nil && options.ResourceName != "" {
resourceName = options.ResourceName
} else {
var err error
resourceName, err = common.GetResourceName(asset.Name)
if err != nil {
resourceName = "default"
}
}
return &models.TerraformResourceBlock{
Labels: []string{c.name, resourceName},
Value: ctyVal,
}, nil
}
3. Add and Map Resource Fields
To implement the schema definitions, flatteners, and expanders for individual fields of the resource, follow the steps in tgc-add-field-to-handwritten-resource-skill.
4. Register the Converters in Generator Templates
Because conversion mappings are generated dynamically into the downstream binary, you must register your custom converters inside the template files:
- HCL Converter Registration: Open
mmv1/templates/tgc_next/cai2hcl/resource_converters.go.tmpl and add the mapping under // ####### START handwritten resources ###########:
"<cai_asset_type>": {
"Default": <product>.New<ResourceName>Cai2hclConverter(provider),
},
- CAI Converter Registration: Open
mmv1/templates/tgc_next/tfplan2cai/resource_converters.go.tmpl and add the mapping under // ####### START handwritten resources ###########:
"google_<product>_<resource_name>": <product>.<ResourceName>Tfplan2caiConverter(),
5. Code Generation & Validation
Follow these commands to generate code and compile changes:
make tgc OUTPUT_PATH="/path/to/your/terraform-google-conversion"
cd /path/to/your/terraform-google-conversion
make mod-clean
make build
Verify your new resource mapping by executing the appropriate integration test.