Integration with Pyvider¶
Package Terraform providers built with Pyvider into self-contained executables.
Optional Integration
FlavorPack works standalone without Pyvider. This integration is optional.
Use FlavorPack with Pyvider when you want to package Terraform providers written in Python into self-contained executables.
Overview¶
Pyvider enables building Terraform providers in Python. FlavorPack packages them into standalone binaries that Terraform can execute without requiring Python installation.
graph LR
PY[Pyvider<br/>Provider Code] --> FP[FlavorPack<br/>Package Builder]
FP --> PKG[terraform-provider-*<br/>.psp executable]
PKG --> TF[Terraform<br/>Uses Provider]
classDef pyvider fill:#f3e5f5,stroke:#4a148c
classDef flavor fill:#e1f5fe,stroke:#01579b
classDef terraform fill:#fff3e0,stroke:#e65100
class PY pyvider
class FP flavor
class PKG,TF terraform
Quick Start¶
1. Create a Pyvider Provider¶
# src/terraform_provider_example/__init__.py
from pyvider import provider, resource, data_source
from pyvider.schema import Attribute
@provider
class ExampleProvider:
"""Example Terraform provider"""
api_key: str = Attribute(
description="API key for authentication",
required=True,
sensitive=True
)
@resource
class Server:
"""Manage server resources"""
name: str = Attribute(
description="Server name",
required=True
)
size: str = Attribute(
description="Server size",
default="small"
)
def create(self, config):
# Create server logic
return {"id": "server-123", **config}
def read(self, id):
# Read server logic
return {"id": id, "name": "my-server", "size": "small"}
def update(self, id, config):
# Update server logic
return {"id": id, **config}
def delete(self, id):
# Delete server logic
pass
2. Configure Provider Manifest¶
# pyproject.toml
[project]
name = "terraform-provider-example"
version = "1.0.0"
dependencies = [
"pyvider>=1.0.0",
"requests>=2.28.0",
]
[tool.flavor]
type = "terraform-provider"
provider_name = "example"
[tool.flavor.execution]
command = "{workenv}/bin/python"
args = ["-m", "pyvider.plugin"]
[tool.flavor.execution.runtime.env]
pass = ["TF_*", "EXAMPLE_*"]
3. Package the Provider¶
# Package for Terraform
flavor pack \
--manifest pyproject.toml \
--output terraform-provider-example.psp
# Rename to Terraform convention
mv terraform-provider-example.psp terraform-provider-example_v1.0.0
Terraform Integration¶
Install Provider¶
# Copy to Terraform plugins directory
mkdir -p ~/.terraform.d/plugins/localhost/providers/example/1.0.0/darwin_arm64/
cp terraform-provider-example_v1.0.0 \
~/.terraform.d/plugins/localhost/providers/example/1.0.0/darwin_arm64/terraform-provider-example_v1.0.0
# Make executable
chmod +x ~/.terraform.d/plugins/localhost/providers/example/1.0.0/darwin_arm64/terraform-provider-example_v1.0.0
Use in Terraform¶
# main.tf
terraform {
required_providers {
example = {
source = "localhost/providers/example"
version = "1.0.0"
}
}
}
provider "example" {
api_key = var.example_api_key
}
resource "example_server" "web" {
name = "web-server"
size = "large"
}
Multi-Platform Providers¶
Package for All Platforms¶
# Build for each platform
for platform in linux_amd64 darwin_arm64 darwin_amd64 windows_amd64; do
flavor pack \
--platform $platform \
--output terraform-provider-example_v1.0.0_$platform
done
Provider Registry Structure¶
~/.terraform.d/plugins/
└── localhost/
└── providers/
└── example/
└── 1.0.0/
├── darwin_arm64/
│ └── terraform-provider-example_v1.0.0
├── darwin_amd64/
│ └── terraform-provider-example_v1.0.0
├── linux_amd64/
│ └── terraform-provider-example_v1.0.0
└── windows_amd64/
└── terraform-provider-example_v1.0.0.exe
Advanced Features¶
Provider with State Management¶
@resource
class Database:
"""Database resource with state"""
name: str = Attribute(required=True)
def create(self, config):
db_id = self.api.create_database(config.name)
# Return full state
return {
"id": db_id,
"name": config.name,
"endpoint": f"{config.name}.db.example.com",
"created_at": datetime.now().isoformat()
}
Custom Packaging Configuration¶
[tool.flavor.terraform]
provider_name = "example"
namespace = "mycompany"
version = "1.0.0"
[[tool.flavor.slots]]
id = 2
path = "./schemas"
extract_to = "schemas"
lifecycle = "cached"
operations = "tar+gzip"
Testing Providers¶
Test Before Packaging¶
# Test provider locally
python -m pyvider.dev serve
# In another terminal
terraform init
terraform plan
terraform apply
Integration Tests¶
# tests/test_provider.py
from pyvider.testing import ProviderTest
def test_server_creation():
test = ProviderTest("example")
server = test.create_resource("example_server", {
"name": "test-server",
"size": "small"
})
assert server.id.startswith("server-")
assert server.name == "test-server"
Distribution¶
Private Registry¶
Terraform Cloud/Enterprise¶
# Package for TFC/TFE
flavor pack \
--manifest pyproject.toml \
--sign \
--output terraform-provider-example_v1.0.0
Benefits¶
- No Python Required: Users don't need Python installed
- Single Binary: Easy distribution and deployment
- Version Lock: Exact provider version in every environment
- Security: Signed, verified providers
- Fast: Native execution with minimal overhead
Troubleshooting¶
Provider Not Found¶
# Check installation
ls ~/.terraform.d/plugins/localhost/providers/example/1.0.0/
# Verify permissions
chmod +x ~/.terraform.d/plugins/localhost/providers/example/1.0.0/*/terraform-provider-example_v1.0.0
Protocol Version Mismatch¶
# Ensure correct pyvider version
[project]
dependencies = [
"pyvider>=1.0.0,<2.0.0", # Lock major version
]