# Tgc Add New Handwritten Resource Skill

> Add a new handwritten resource to TGC. Use when you need to add a new handwritten resource to TGC Next.

- Skill: `googlecloudplatform/tgc-add-new-handwritten-resource-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add googlecloudplatform/tgc-add-new-handwritten-resource-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/googlecloudplatform/tgc-add-new-handwritten-resource-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: GoogleCloudPlatform (https://skillmd.com/u/googlecloudplatform)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/googlecloudplatform/tgc-add-new-handwritten-resource-skill

---


# 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.

```go
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"
)

// The CAI asset type corresponding to this resource
const <ResourceName>AssetType string = "whatever.googleapis.com/<AssetType>"

// The Terraform resource name
const <ResourceName>SchemaName string = "google_<product>_<resource_name>"

// Return the resource schema (typically copied from the Terraform Google provider)
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.

```go
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) {
	// Construct the CAI asset path matching GCS/CAI requirements
	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{})
	
	// Map values from the Terraform resource data 'd' into the 'obj' map
	if v, ok := d.GetOk("field_name"); ok {
		obj["fieldName"] = v
	}
	// Add custom field expansions here
	
	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.

```go
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{})
	
	// Map fields from asset.Resource.Data (which is a map[string]interface{}) into hclData
	if val, ok := asset.Resource.Data["fieldName"]; ok {
		hclData["field_name"] = val
	}
	// Add custom flattening logic here
	
	ctyVal, err := common.MapToCtyValWithSchema(hclData, c.schema)
	if err != nil {
		return nil, err
	}
	
	// Determine the HCL block name, using options if provided
	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](../tgc-add-field-to-handwritten-resource-skill/SKILL.md).

### 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 ###########`:
  ```go
  "<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 ###########`:
  ```go
  "google_<product>_<resource_name>": <product>.<ResourceName>Tfplan2caiConverter(),
  ```

### 5. Code Generation & Validation
Follow these commands to generate code and compile changes:

```bash
# 1. Generate TGC downstream code
make tgc OUTPUT_PATH="/path/to/your/terraform-google-conversion"

# 2. Compile and run downstream tests
cd /path/to/your/terraform-google-conversion
make mod-clean
make build
```

Verify your new resource mapping by executing the appropriate integration test.

