Distribution Strategies

5 minute read

Distribution Overview

While the Terraform Registry is ideal for public providers, organizations often need alternative distribution methods for private, internal, or air-gapped environments.

Distribution Methods

1. Terraform Registry (Public)

Best for: Open source providers

Advantages:

  • ✅ Global distribution
  • ✅ Automatic updates
  • ✅ Version management
  • ✅ Documentation hosting
  • ✅ Discovery

Usage:

terraform {
  required_providers {
    cachecloud = {
      source  = "example/cachecloud"
      version = "~> 1.0"
    }
  }
}

2. Private Registry

Best for: Enterprise organizations

Advantages:

  • ✅ Internal providers
  • ✅ Access control
  • ✅ Custom hosting
  • ✅ Compliance
  • ✅ Audit trails

3. Direct Downloads

Best for: Small teams, custom deployments

Advantages:

  • ✅ Simple setup
  • ✅ Full control
  • ✅ No external dependencies
  • ✅ Air-gap friendly

4. Provider Mirrors

Best for: Multi-region, air-gapped environments

Advantages:

  • ✅ Local caching
  • ✅ Bandwidth savings
  • ✅ Offline support
  • ✅ Compliance

Private Registry Implementation

Terraform Cloud/Enterprise

Setup:

  1. Create Organization
    # Navigate to Terraform Cloud
    open https://app.terraform.io/
       
    # Create organization: "my-company"
    
  2. Configure Private Registry
    # Settings → Registry → Add Provider
    # Link GitHub repository
    # Configure webhook
    
  3. Publish Provider
    # Create release as usual
    git tag -s v1.0.0 -m "Release v1.0.0"
    git push origin v1.0.0
       
    # Terraform Cloud automatically picks up release
    
  4. Use Private Provider
    terraform {
      required_providers {
        cachecloud = {
          source  = "app.terraform.io/my-company/cachecloud"
          version = "~> 1.0"
        }
      }
    }
    

Self-Hosted Registry

Using Terraform Enterprise:

# terraform.rc or .terraformrc
provider_installation {
  direct {
    # Use self-hosted registry
    registry = "terraform.example.com"
  }
}

Using Artifactory:

  1. Setup Terraform Repository
    # Create Terraform repository in Artifactory
    # Type: Generic
    # Package Type: Terraform
    
  2. Upload Provider
    # Upload to Artifactory
    curl -u user:password \
      -T terraform-provider-cachecloud_1.0.0_linux_amd64.zip \
      "https://artifactory.example.com/terraform/example/cachecloud/1.0.0/linux_amd64/terraform-provider-cachecloud_1.0.0_linux_amd64.zip"
    
  3. Configure Terraform
    # .terraformrc
    provider_installation {
      network_mirror {
        url = "https://artifactory.example.com/terraform/"
      }
    }
    

Direct Download Method

Manual Installation

1. Download Provider

# Download release
PROVIDER_VERSION="1.0.0"
OS="darwin"  # or linux, windows
ARCH="amd64"  # or arm64

curl -LO "https://github.com/example/terraform-provider-cachecloud/releases/download/v${PROVIDER_VERSION}/terraform-provider-cachecloud_${PROVIDER_VERSION}_${OS}_${ARCH}.zip"

# Extract
unzip terraform-provider-cachecloud_${PROVIDER_VERSION}_${OS}_${ARCH}.zip

2. Install to Plugin Directory

# Create plugin directory
mkdir -p ~/.terraform.d/plugins/registry.terraform.io/example/cachecloud/${PROVIDER_VERSION}/${OS}_${ARCH}

# Move binary
mv terraform-provider-cachecloud_v${PROVIDER_VERSION} \
   ~/.terraform.d/plugins/registry.terraform.io/example/cachecloud/${PROVIDER_VERSION}/${OS}_${ARCH}/

# Make executable
chmod +x ~/.terraform.d/plugins/registry.terraform.io/example/cachecloud/${PROVIDER_VERSION}/${OS}_${ARCH}/terraform-provider-cachecloud_v${PROVIDER_VERSION}

3. Configure Terraform

terraform {
  required_providers {
    cachecloud = {
      source  = "example/cachecloud"
      version = "1.0.0"
    }
  }
}

Automated Installation Script

#!/bin/bash
# install-provider.sh

set -e

PROVIDER_NAME="cachecloud"
PROVIDER_NAMESPACE="example"
PROVIDER_VERSION="${1:-1.0.0}"
GITHUB_REPO="example/terraform-provider-cachecloud"

# Detect OS and architecture
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"

case "${OS}" in
  darwin) OS="darwin" ;;
  linux)  OS="linux" ;;
  mingw*) OS="windows" ;;
esac

case "${ARCH}" in
  x86_64)  ARCH="amd64" ;;
  aarch64) ARCH="arm64" ;;
  arm64)   ARCH="arm64" ;;
esac

echo "Installing ${PROVIDER_NAMESPACE}/${PROVIDER_NAME} v${PROVIDER_VERSION} for ${OS}_${ARCH}"

# Download
DOWNLOAD_URL="https://github.com/${GITHUB_REPO}/releases/download/v${PROVIDER_VERSION}/terraform-provider-${PROVIDER_NAME}_${PROVIDER_VERSION}_${OS}_${ARCH}.zip"
echo "Downloading from: ${DOWNLOAD_URL}"

curl -LO "${DOWNLOAD_URL}"

# Extract
unzip -o "terraform-provider-${PROVIDER_NAME}_${PROVIDER_VERSION}_${OS}_${ARCH}.zip"

# Create plugin directory
PLUGIN_DIR="${HOME}/.terraform.d/plugins/registry.terraform.io/${PROVIDER_NAMESPACE}/${PROVIDER_NAME}/${PROVIDER_VERSION}/${OS}_${ARCH}"
mkdir -p "${PLUGIN_DIR}"

# Move binary
mv "terraform-provider-${PROVIDER_NAME}_v${PROVIDER_VERSION}" "${PLUGIN_DIR}/"

# Make executable
chmod +x "${PLUGIN_DIR}/terraform-provider-${PROVIDER_NAME}_v${PROVIDER_VERSION}"

# Cleanup
rm "terraform-provider-${PROVIDER_NAME}_${PROVIDER_VERSION}_${OS}_${ARCH}.zip"

echo "✅ Installation complete!"
echo "Provider installed to: ${PLUGIN_DIR}"

Usage:

# Install specific version
./install-provider.sh 1.0.0

# Install latest
./install-provider.sh

Provider Network Mirror

Setting Up Mirror

Directory Structure:

mirror/
└── registry.terraform.io/
    └── example/
        └── cachecloud/
            ├── index.json
            └── 1.0.0.json

index.json:

{
  "versions": {
    "1.0.0": {},
    "1.1.0": {}
  }
}

1.0.0.json:

{
  "archives": {
    "darwin_amd64": {
      "url": "terraform-provider-cachecloud_1.0.0_darwin_amd64.zip",
      "hashes": [
        "zh:a1b2c3d4e5f6...",
        "h1:e5f6g7h8i9j0..."
      ]
    },
    "linux_amd64": {
      "url": "terraform-provider-cachecloud_1.0.0_linux_amd64.zip",
      "hashes": [
        "zh:b2c3d4e5f6g7...",
        "h1:f6g7h8i9j0k1..."
      ]
    }
  }
}

HTTP Server Setup

Using Nginx:

# /etc/nginx/sites-available/terraform-mirror
server {
    listen 80;
    server_name terraform-mirror.internal.example.com;
    
    root /var/www/terraform-mirror;
    
    location / {
        autoindex on;
        add_header 'Access-Control-Allow-Origin' '*';
    }
    
    location ~ \.json$ {
        add_header Content-Type application/json;
    }
}

Using Python:

cd mirror/
python3 -m http.server 8080

Configure Mirror

# .terraformrc
provider_installation {
  network_mirror {
    url = "https://terraform-mirror.internal.example.com/"
  }
}

Air-Gapped Environments

Complete Offline Bundle

1. Download All Artifacts

#!/bin/bash
# download-provider-bundle.sh

PROVIDER="cachecloud"
VERSION="1.0.0"
PLATFORMS="darwin_amd64 darwin_arm64 linux_amd64 linux_arm64 windows_amd64"

mkdir -p bundle/${PROVIDER}/${VERSION}

for PLATFORM in ${PLATFORMS}; do
  echo "Downloading ${PLATFORM}..."
  curl -LO "https://github.com/example/terraform-provider-${PROVIDER}/releases/download/v${VERSION}/terraform-provider-${PROVIDER}_${VERSION}_${PLATFORM}.zip"
  mv "terraform-provider-${PROVIDER}_${VERSION}_${PLATFORM}.zip" bundle/${PROVIDER}/${VERSION}/
done

# Download checksums
curl -LO "https://github.com/example/terraform-provider-${PROVIDER}/releases/download/v${VERSION}/terraform-provider-${PROVIDER}_${VERSION}_SHA256SUMS"
curl -LO "https://github.com/example/terraform-provider-${PROVIDER}/releases/download/v${VERSION}/terraform-provider-${PROVIDER}_${VERSION}_SHA256SUMS.sig"

mv terraform-provider-${PROVIDER}_${VERSION}_SHA256SUMS* bundle/${PROVIDER}/${VERSION}/

# Create bundle
tar -czf terraform-provider-${PROVIDER}-${VERSION}-bundle.tar.gz bundle/

echo "✅ Bundle created: terraform-provider-${PROVIDER}-${VERSION}-bundle.tar.gz"

2. Transfer to Air-Gapped Environment

# Via USB, secure file transfer, etc.
scp terraform-provider-cachecloud-1.0.0-bundle.tar.gz airgap-host:/tmp/

3. Install in Air-Gapped Environment

#!/bin/bash
# install-offline-provider.sh

BUNDLE="terraform-provider-cachecloud-1.0.0-bundle.tar.gz"

# Extract bundle
tar -xzf "${BUNDLE}"

# Detect platform
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"
case "${ARCH}" in
  x86_64)  ARCH="amd64" ;;
  aarch64|arm64) ARCH="arm64" ;;
esac

PLATFORM="${OS}_${ARCH}"

# Find provider binary
cd bundle/cachecloud/1.0.0
unzip "terraform-provider-cachecloud_1.0.0_${PLATFORM}.zip"

# Install
PLUGIN_DIR="${HOME}/.terraform.d/plugins/registry.terraform.io/example/cachecloud/1.0.0/${PLATFORM}"
mkdir -p "${PLUGIN_DIR}"
mv terraform-provider-cachecloud_v1.0.0 "${PLUGIN_DIR}/"
chmod +x "${PLUGIN_DIR}/terraform-provider-cachecloud_v1.0.0"

echo "✅ Provider installed offline"

Development Overrides

Local Development

# ~/.terraformrc
provider_installation {
  dev_overrides {
    "example/cachecloud" = "/Users/developer/go/bin"
  }
  
  # For all other providers
  direct {}
}

Usage:

# Build provider
cd terraform-provider-cachecloud
go build -o ~/go/bin/terraform-provider-cachecloud

# Terraform uses local binary
cd examples/
terraform init  # Skips provider download
terraform plan  # Uses local binary

Container-Based Distribution

Docker Image

Dockerfile:

FROM hashicorp/terraform:1.6

# Copy provider binary
COPY terraform-provider-cachecloud /usr/local/bin/

# Set plugin directory
ENV TF_PLUGIN_CACHE_DIR=/terraform/plugins
RUN mkdir -p /terraform/plugins

WORKDIR /terraform

ENTRYPOINT ["terraform"]

Build and Use:

# Build image
docker build -t terraform-with-cachecloud:1.0.0 .

# Run
docker run -it \
  -v $(pwd):/terraform \
  -w /terraform \
  terraform-with-cachecloud:1.0.0 \
  init

Distribution Best Practices

1. Version Management

# ✅ Good: Clear version strategy
Public Registry: Latest stable versions
Private Registry: All versions including pre-release
Air-Gap Bundle: Specific tested versions

# ❌ Bad: Inconsistent versions
Different versions in different locations
No version tracking

2. Access Control

# ✅ Good: Role-based access
Public: Everyone
Private: Authenticated organization members
Internal: VPN + authentication
Air-Gap: Physical security

# ❌ Bad: No access control
All providers publicly accessible
No authentication required

3. Update Strategy

# ✅ Good: Planned updates
Weekly: Check for updates
Monthly: Test and deploy
Quarterly: Major version upgrades

# ❌ Bad: No update plan
Manual updates only
No testing
Unpredictable timing

Complete Example

Multi-Environment Setup

# .terraformrc
provider_installation {
  # Production: Use company registry
  direct {
    registry = "terraform.company.com"
  }
  
  # Development: Use local override
  dev_overrides {
    "example/cachecloud" = "/Users/developer/providers"
  }
  
  # Fallback: Public registry
  network_mirror {
    url = "https://registry.terraform.io/"
  }
}

Key Takeaways

  • ✅ Choose distribution method based on requirements
  • ✅ Public registry for open source providers
  • ✅ Private registry for enterprise
  • ✅ Direct downloads for simple setups
  • ✅ Network mirrors for air-gapped environments
  • ✅ Use development overrides for local testing
  • ✅ Plan update strategies
  • ✅ Implement access controls
  • ✅ Maintain version consistency
  • ✅ Document distribution process

What’s Next?

Practice the complete publishing workflow in a hands-on lab.

Next: Publishing Lab →


Previous: ← Registry Publication
Next: Publishing Lab →

Last updated: June 30, 2026

Updated: