Documentation Lab

9 minute read

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: 30m
  • update - Default: 20m
  • delete - 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

Updated: