Documentation Lab
Lab Overview
In this lab, you’ll generate complete documentation for the CacheCloud provider, customize templates, and publish to the Terraform Registry.
Prerequisites
- Completed previous modules
- CacheCloud provider code from earlier labs
- Go 1.21+ installed
- tfplugindocs installed
Lab Setup
Install tfplugindocs
go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@latest
Verify Installation
tfplugindocs --version
# tfplugindocs v0.18.0
Project Structure
cd terraform-provider-cachecloud
# Verify structure
tree -L 2
# .
# ├── docs/
# ├── examples/
# ├── internal/
# ├── templates/
# ├── main.go
# └── go.mod
Step 1: Add Schema Descriptions
Update Resource Schema
// internal/provider/cache_resource.go
package provider
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)
func (r *cacheResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "Manages a CacheCloud cache instance. " +
"Provides a fully managed Redis or Memcached cache with automatic backups, monitoring, and scaling.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The unique identifier of the cache (format: `cache-xxxxxxx`).",
Computed: true,
},
"name": schema.StringAttribute{
MarkdownDescription: "The name of the cache. Must be 3-63 characters, " +
"start with a lowercase letter, and contain only lowercase letters, numbers, and hyphens. " +
"Must be unique within your account.",
Required: true,
},
"engine": schema.StringAttribute{
MarkdownDescription: "The cache engine type. Valid values: `redis`, `memcached`.",
Required: true,
},
"version": schema.StringAttribute{
MarkdownDescription: "The engine version. Defaults to the latest available version. " +
"See [Supported Versions](https://docs.example.com/versions) for available versions.",
Optional: true,
Computed: true,
},
"node_type": schema.StringAttribute{
MarkdownDescription: "The compute and memory capacity of the nodes. " +
"See [Node Types](https://example.com/pricing) for available options. " +
"Defaults to `cache.t3.micro`.",
Optional: true,
Computed: true,
},
"num_nodes": schema.Int64Attribute{
MarkdownDescription: "Number of cache nodes in the cluster. " +
"Valid range: 1-90 for Redis, 1-20 for Memcached. Defaults to `1`.",
Optional: true,
Computed: true,
},
"port": schema.Int64Attribute{
MarkdownDescription: "The port number on which the cache accepts connections. " +
"Valid range: 1024-65535. Redis defaults to `6379`, Memcached to `11211`.",
Optional: true,
Computed: true,
},
"endpoint": schema.StringAttribute{
MarkdownDescription: "The primary endpoint for connecting to the cache.",
Computed: true,
},
"status": schema.StringAttribute{
MarkdownDescription: "The current operational status. " +
"Possible values: `creating`, `available`, `modifying`, `deleting`, `create-failed`.",
Computed: true,
},
"backup_enabled": schema.BoolAttribute{
MarkdownDescription: "Enable automatic backups. Only available for Redis. Defaults to `false`.",
Optional: true,
Computed: true,
},
"backup_retention_days": schema.Int64Attribute{
MarkdownDescription: "Number of days to retain automated backups. " +
"Valid range: 1-35. Required if `backup_enabled` is `true`.",
Optional: true,
},
"tags": schema.MapAttribute{
ElementType: types.StringType,
MarkdownDescription: "Key-value map of resource tags. Maximum 50 tags.",
Optional: true,
},
},
}
}
Update Data Source Schema
// internal/provider/cache_data_source.go
func (d *cacheDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "Use this data source to get information about an existing CacheCloud cache. " +
"Query by name, ID, or tags.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The unique identifier of the cache. Conflicts with `name`.",
Optional: true,
Computed: true,
},
"name": schema.StringAttribute{
MarkdownDescription: "The name of the cache. Conflicts with `id`.",
Optional: true,
Computed: true,
},
"engine": schema.StringAttribute{
MarkdownDescription: "The cache engine type (`redis` or `memcached`).",
Computed: true,
},
"endpoint": schema.StringAttribute{
MarkdownDescription: "The primary endpoint for connecting to the cache.",
Computed: true,
},
"tags": schema.MapAttribute{
ElementType: types.StringType,
MarkdownDescription: "Map of tags assigned to the cache.",
Optional: true,
Computed: true,
},
},
}
}
Step 2: Create Examples
Provider Example
mkdir -p examples/provider
# examples/provider/provider.tf
terraform {
required_providers {
cachecloud = {
source = "example/cachecloud"
version = "~> 1.0"
}
}
}
provider "cachecloud" {
# API key for authentication
# Set via CACHECLOUD_API_KEY environment variable or specify here
api_key = var.cachecloud_api_key
# Optional: Custom API endpoint
endpoint = "https://api.cachecloud.example.com"
# Optional: Default region for resources
region = "us-east-1"
}
Resource Examples
mkdir -p examples/resources/cachecloud_cache
# examples/resources/cachecloud_cache/resource.tf
# Basic Redis cache
resource "cachecloud_cache" "basic" {
name = "my-cache"
engine = "redis"
node_type = "cache.t3.micro"
}
# Production cache with high availability
resource "cachecloud_cache" "production" {
name = "prod-api-cache"
engine = "redis"
version = "7.0"
node_type = "cache.m5.large"
num_nodes = 3
# Backups
backup_enabled = true
backup_retention_days = 30
# Tags
tags = {
Environment = "production"
Application = "api"
ManagedBy = "terraform"
}
}
# Memcached cache
resource "cachecloud_cache" "memcached" {
name = "session-cache"
engine = "memcached"
node_type = "cache.t3.small"
num_nodes = 2
port = 11211
}
Import Example
# examples/resources/cachecloud_cache/import.sh
# Import cache by ID
terraform import cachecloud_cache.example cache-123456
# For multi-region caches, use region/id format
terraform import cachecloud_cache.example us-east-1/cache-123456
Data Source Example
mkdir -p examples/data-sources/cachecloud_cache
# examples/data-sources/cachecloud_cache/data-source.tf
# Look up cache by name
data "cachecloud_cache" "example" {
name = "my-cache"
}
output "cache_endpoint" {
description = "Cache endpoint for application configuration"
value = data.cachecloud_cache.example.endpoint
}
# Look up cache by tags
data "cachecloud_cache" "prod_api" {
tags = {
Environment = "production"
Application = "api"
}
}
output "prod_cache_id" {
value = data.cachecloud_cache.prod_api.id
}
Step 3: Customize Templates
Create Templates Directory
mkdir -p templates
Provider Template
<!-- templates/index.md.tmpl -->
---
page_title: "Provider: {{ .ProviderShortName }}"
description: |-
{{ .Description }}
---
# {{ .ProviderShortName }} Provider
The CacheCloud provider allows Terraform to manage CacheCloud resources including cache instances, clusters, and parameter groups.
## Features
- **Multi-Engine Support**: Manage both Redis and Memcached caches
- **High Availability**: Configure multi-AZ deployments with automatic failover
- **Automatic Backups**: Scheduled backups with configurable retention
- **Security**: Encryption at rest and in transit
- **Monitoring**: Built-in metrics and alerting
## Quick Start
{{tffile "examples/provider/provider.tf"}}
{{ .SchemaMarkdown }}
## Environment Variables
- `CACHECLOUD_API_KEY` - API key for authentication
- `CACHECLOUD_ENDPOINT` - Custom API endpoint (optional)
- `CACHECLOUD_REGION` - Default region (optional)
## Support
- **Documentation**: https://docs.cachecloud.example.com
- **API Reference**: https://api-docs.cachecloud.example.com
- **GitHub**: https://github.com/example/terraform-provider-cachecloud
- **Issues**: https://github.com/example/terraform-provider-cachecloud/issues
Resource Template
<!-- templates/resources.md.tmpl -->
---
page_title: "{{.Type}} {{.Name}} - {{.ProviderShortName}}"
subcategory: ""
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# {{.Type}}: {{.Name}}
{{ .Description | trimspace }}
{{ if .HasExample -}}
## Example Usage
{{ tffile .ExampleFile }}
{{- end }}
{{ .SchemaMarkdown | trimspace }}
{{ if .HasImport -}}
## Import
Import using the cache ID:
{{ codefile "shell" .ImportFile }}
{{- end }}
Data Source Template
<!-- templates/data-sources.md.tmpl -->
---
page_title: "{{.Type}} {{.Name}} - {{.ProviderShortName}}"
subcategory: ""
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# {{.Type}}: {{.Name}}
{{ .Description | trimspace }}
~> **Note:** This data source returns a single cache. If multiple caches match the query, an error occurs.
{{ if .HasExample -}}
## Example Usage
{{ tffile .ExampleFile }}
{{- end }}
{{ .SchemaMarkdown | trimspace }}
Step 4: Generate Documentation
Generate Docs
# Generate all documentation
tfplugindocs generate
# Check generated files
ls -la docs/
# docs/
# ├── index.md
# ├── data-sources/
# │ └── cache.md
# └── resources/
# └── cache.md
Review Generated Documentation
# View provider documentation
cat docs/index.md
# View resource documentation
cat docs/resources/cache.md
# View data source documentation
cat docs/data-sources/cache.md
Step 5: Validate Documentation
Validate with tfplugindocs
tfplugindocs validate
# Expected output:
# validating provider docs...
# validating resource docs...
# validating data source docs...
# success: all documentation is valid
Check for Common Issues
# Check for broken links
grep -r "](/devops/" docs/ | while read line; do
file=$(echo "$line" | cut -d: -f1)
link=$(echo "$line" | grep -oP '\(/[^)]+\)')
if [ ! -f "docs$link" ]; then
echo "Broken link in $file: $link"
fi
done
# Check for missing examples
for resource in docs/resources/*.md; do
if ! grep -q "## Example Usage" "$resource"; then
echo "Missing example in $resource"
fi
done
Step 6: Create Custom Guides
Getting Started Guide
mkdir -p docs/guides
<!-- docs/guides/getting-started.md -->
---
page_title: "Getting Started with CacheCloud"
description: |-
Create your first cache in 5 minutes.
---
# Getting Started
This guide walks through creating your first Redis cache.
## Prerequisites
- Terraform 1.0+
- CacheCloud account
- API key
## Step 1: Configure Provider
```terraform
terraform {
required_providers {
cachecloud = {
source = "example/cachecloud"
version = "~> 1.0"
}
}
}
provider "cachecloud" {
# Set CACHECLOUD_API_KEY environment variable
}
Step 2: Create Cache
resource "cachecloud_cache" "my_first_cache" {
name = "my-first-cache"
engine = "redis"
node_type = "cache.t3.micro"
}
output "endpoint" {
value = cachecloud_cache.my_first_cache.endpoint
}
Step 3: Apply
terraform init
terraform apply
Next Steps
Step 7: Add to CI/CD
GitHub Actions Workflow
# .github/workflows/documentation.yml
name: Documentation
on:
push:
branches: [main]
paths:
- 'internal/provider/**'
- 'examples/**'
- 'templates/**'
pull_request:
paths:
- 'docs/**'
- 'examples/**'
jobs:
generate:
name: Generate and Validate Docs
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install tfplugindocs
run: go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@latest
- name: Generate documentation
run: tfplugindocs generate
- name: Validate documentation
run: tfplugindocs validate
- name: Check for uncommitted changes
run: |
git diff --exit-code docs/ || (
echo "Documentation is out of date!"
echo "Run 'tfplugindocs generate' and commit the changes."
exit 1
)
test-examples:
name: Test Examples
runs-on: ubuntu-latest
strategy:
matrix:
example:
- resources/cachecloud_cache
- data-sources/cachecloud_cache
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
working-directory: examples/$
- name: Terraform Validate
run: terraform validate
working-directory: examples/$
- name: Terraform Format
run: terraform fmt -check
working-directory: examples/$
Step 8: Publish to Registry
Prepare for Publishing
# Ensure all documentation is committed
git add docs/
git add examples/
git commit -m "Add comprehensive documentation"
# Tag release
git tag v1.0.0
git push origin v1.0.0
Registry Manifest
{
"version": 1,
"metadata": {
"protocol_versions": ["6.0"]
}
}
Verify Registry Requirements
# Check documentation structure
tree docs/
# docs/
# ├── index.md
# ├── data-sources/
# │ └── cache.md
# ├── guides/
# │ └── getting-started.md
# └── resources/
# └── cache.md
# Verify examples
tree examples/
# examples/
# ├── provider/
# │ └── provider.tf
# ├── resources/
# │ └── cachecloud_cache/
# │ ├── resource.tf
# │ └── import.sh
# └── data-sources/
# └── cachecloud_cache/
# └── data-source.tf
Lab Challenges
Challenge 1: Add Timeouts Documentation
Add timeout configuration to resource documentation:
## Timeouts
The `timeouts` block allows custom timeouts:
```terraform
resource "cachecloud_cache" "example" {
name = "my-cache"
timeouts {
create = "30m"
update = "20m"
delete = "30m"
}
}
create- Default:30mupdate- Default:20mdelete- Default:30m```
Challenge 2: Create Architecture Diagram
Add ASCII diagram to getting started guide:
## Architecture
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ │ │ │ │ │ │ Application │─────▶│ CacheCloud │─────▶│ Redis │ │ │ │ │ │ │ └─────────────┘ └──────────────┘ └─────────────┘
Challenge 3: Add Troubleshooting Section
Create troubleshooting guide:
# Troubleshooting
## Connection Timeout
**Problem:**
Error: timeout waiting for cache to become available
**Solutions:**
1. Check security group rules
2. Verify network connectivity
3. Increase timeout:
```terraform
timeouts {
create = "45m"
}
## Verification
### Checklist
- ✅ Schema descriptions added to all resources
- ✅ Examples created for provider, resources, and data sources
- ✅ Import examples provided
- ✅ Templates customized
- ✅ Documentation generated successfully
- ✅ Validation passes
- ✅ No broken links
- ✅ Getting started guide created
- ✅ CI/CD workflow added
- ✅ Ready for registry publication
### Test Documentation Locally
```bash
# Serve documentation locally
python3 -m http.server 8000 --directory docs/
# Open browser
open http://localhost:8000
Key Takeaways
- ✅ Use MarkdownDescription for schema documentation
- ✅ Create comprehensive, tested examples
- ✅ Customize templates for better user experience
- ✅ Validate documentation before committing
- ✅ Automate documentation generation in CI/CD
- ✅ Include guides for common workflows
- ✅ Test all examples regularly
- ✅ Follow Terraform Registry requirements
- ✅ Keep documentation in sync with code
- ✅ Provide clear import instructions
What’s Next?
Module 12 is complete! Continue to Module 13 to learn about publishing and distributing your provider.
Next Module: Publishing & Distribution →
Previous: ← Documentation Best Practices
Next Module: Publishing & Distribution →
Last updated: June 30, 2026