Terraform Provider for ChainLaunch
ChainLaunch includes a Terraform provider that enables Infrastructure-as-Code (IaC) workflows for managing blockchain networks, nodes, and configurations. This guide covers how to use the Terraform provider to automate deployment and management of your ChainLaunch infrastructure.
Overview
The ChainLaunch Terraform provider allows you to:
- Define Infrastructure as Code - Declare blockchain networks in HCL
- Version Control - Track infrastructure changes in Git
- Automate Deployments - CI/CD pipelines for blockchain infrastructure
- Manage State - Track resource dependencies and lifecycle
- Collaborate - Share infrastructure configurations across teams
Available Resources
The provider includes 30+ resources covering:
Hyperledger Fabric:
chainlaunch_fabric_organization- Organizations (MSP configuration)chainlaunch_fabric_peer- Peer nodeschainlaunch_fabric_orderer- Orderer nodeschainlaunch_fabric_network- Channelschainlaunch_fabric_chaincode- Chaincode packageschainlaunch_fabric_chaincode_install- Install chaincode on peerschainlaunch_fabric_chaincode_approve- Approve chaincode per orgchainlaunch_fabric_chaincode_commit- Commit chaincode to channelchainlaunch_fabric_identity- User identitieschainlaunch_fabric_anchor_peers- Anchor peer configuration
Hyperledger Besu:
chainlaunch_besu_network- Besu networks (QBFT/IBFT2)chainlaunch_besu_node- Besu validator/RPC nodes
Platform Services:
chainlaunch_key_provider- Key management providers (Database, Vault, AWS KMS)chainlaunch_backup_target- S3-compatible backup targetschainlaunch_backup_schedule- Automated backup scheduleschainlaunch_metrics_prometheus- Prometheus integrationchainlaunch_notification_provider- Email notificationschainlaunch_plugin- Custom pluginschainlaunch_plugin_deployment- Plugin deployments
Node Sharing:
chainlaunch_node_invitation- Share nodes with other instanceschainlaunch_node_accept_invitation- Accept node invitationschainlaunch_external_nodes_sync- Sync external node configurations
For the complete resource reference, see the Terraform Registry Documentation.
Installation
Provider Configuration
Add the ChainLaunch provider to your Terraform configuration:
terraform {
required_providers {
chainlaunch = {
source = "kfsoftware/chainlaunch"
version = "~> 0.0.1-beta3" # Check registry for latest version
}
}
}
provider "chainlaunch" {
url = "http://localhost:8100" # ChainLaunch API URL
api_key = var.chainlaunch_api_key # Or use username/password auth
}
Authentication Options:
The provider supports two authentication methods:
- API Key (Recommended for automation):
provider "chainlaunch" {
url = "http://localhost:8100"
api_key = var.chainlaunch_api_key
}
- Username/Password:
provider "chainlaunch" {
url = "http://localhost:8100"
username = var.chainlaunch_username
password = var.chainlaunch_password
}
All provider configuration can also be set via environment variables:
CHAINLAUNCH_URL- API URLCHAINLAUNCH_API_KEY- API keyCHAINLAUNCH_USERNAME- UsernameCHAINLAUNCH_PASSWORD- Password
Variables Configuration
Create a variables.tf file:
variable "chainlaunch_api_key" {
description = "ChainLaunch API Key"
type = string
sensitive = true
}
Terraform Variables File
Create a terraform.tfvars file (add to .gitignore):
chainlaunch_api_key = "your-api-key-here"
Quick Start Example
Complete Fabric Network
This example creates a complete Hyperledger Fabric network with organizations, peers, orderers, and a channel:
# Create Organization
resource "chainlaunch_fabric_organization" "org1" {
msp_id = "Org1MSP"
description = "Organization 1"
}
# Create Peer Nodes for Org1
resource "chainlaunch_fabric_peer" "peer0_org1" {
name = "peer0-org1"
msp_id = chainlaunch_fabric_organization.org1.msp_id
organization_id = chainlaunch_fabric_organization.org1.id
version = "2.5.9"
mode = "docker"
listen_address = "0.0.0.0:7051"
chaincode_address = "0.0.0.0:7052"
events_address = "0.0.0.0:7053"
operations_listen_address = "0.0.0.0:9443"
external_endpoint = "peer0.org1.example.com:7051"
environment = {
CORE_LEDGER_STATE_STATEDATABASE = "CouchDB"
CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS = "couchdb0:5984"
}
}
# Create Orderer
resource "chainlaunch_fabric_orderer" "orderer0" {
name = "orderer0"
msp_id = "OrdererMSP"
version = "2.5.9"
mode = "docker"
listen_address = "0.0.0.0:7050"
admin_listen_address = "0.0.0.0:7053"
operations_listen_address = "0.0.0.0:9443"
external_endpoint = "orderer0.example.com:7050"
}
# Create Fabric Network (Channel)
resource "chainlaunch_fabric_network" "mychannel" {
name = "mychannel"
description = "Main application channel"
peer_organizations = [
{
id = chainlaunch_fabric_organization.org1.id
node_ids = [chainlaunch_fabric_peer.peer0_org1.id]
}
]
orderer_organizations = [
{
id = chainlaunch_fabric_organization.org1.id
node_ids = [chainlaunch_fabric_orderer.orderer0.id]
}
]
consensus_type = "etcdraft"
}
Resource Types
Organizations
resource "chainlaunch_fabric_organization" "org1" {
msp_id = "Org1MSP" # Required - serves as organization name
description = "Organization 1"
# Optional: Specify key provider
provider_id = chainlaunch_key_provider.vault.id
}
Peer Nodes
resource "chainlaunch_fabric_peer" "peer0" {
name = "peer0-org1"
msp_id = "Org1MSP"
organization_id = chainlaunch_fabric_organization.org1.id
version = "2.5.9" # Fabric version (2.2.0, 2.5.0, 2.5.9)
mode = "docker" # or "service"
# Required network endpoints
listen_address = "0.0.0.0:7051"
chaincode_address = "0.0.0.0:7052"
events_address = "0.0.0.0:7053"
operations_listen_address = "0.0.0.0:9443"
external_endpoint = "peer0.org1.example.com:7051"
# Optional: Custom environment variables
environment = {
CORE_PEER_GOSSIP_USELEADERELECTION = "true"
CORE_PEER_GOSSIP_ORGLEADER = "false"
}
# Optional: Certificate settings
certificate_expiration = 365 # days
auto_renewal_enabled = true
auto_renewal_days = 30
# Optional: Domain names for TLS
domain_names = ["peer0.org1.example.com"]
}
Orderer Nodes
resource "chainlaunch_fabric_orderer" "orderer0" {
name = "orderer0"
msp_id = "OrdererMSP"
organization_id = chainlaunch_fabric_organization.orderer_org.id
version = "2.5.9"
mode = "docker"
# Required network endpoints
listen_address = "0.0.0.0:7050"
admin_listen_address = "0.0.0.0:7053"
operations_listen_address = "0.0.0.0:9443"
external_endpoint = "orderer0.example.com:7050"
# Optional: Certificate settings
certificate_expiration = 365
auto_renewal_enabled = true
# Optional: Domain names
domain_names = ["orderer0.example.com"]
}
Fabric Networks (Channels)
resource "chainlaunch_fabric_network" "mychannel" {
name = "mychannel"
description = "Application Channel"
# Required: Peer organizations and their nodes
peer_organizations = [
{
id = chainlaunch_fabric_organization.org1.id
node_ids = [
chainlaunch_fabric_peer.peer0_org1.id,
chainlaunch_fabric_peer.peer1_org1.id
]
},
{
id = chainlaunch_fabric_organization.org2.id
node_ids = [chainlaunch_fabric_peer.peer0_org2.id]
}
]
# Required: Orderer organizations and their nodes (consenters)
orderer_organizations = [
{
id = chainlaunch_fabric_organization.orderer_org.id
node_ids = [
chainlaunch_fabric_orderer.orderer0.id,
chainlaunch_fabric_orderer.orderer1.id,
chainlaunch_fabric_orderer.orderer2.id
]
}
]
# Consensus configuration
consensus_type = "etcdraft" # or "smartbft"
# Optional: Etcd Raft options
etcdraft_options = {
tick_interval = "500ms"
election_tick = 10
heartbeat_tick = 1
max_inflight_blocks = 5
snapshot_interval_size = 20971520 # 20MB
}
# Optional: Batch configuration
batch_timeout = "2s"
batch_size = {
max_message_count = 500
absolute_max_bytes = 103809024 # 99MB
preferred_max_bytes = 524288 # 512KB
}
# Optional: Capabilities
channel_capabilities = ["V2_0"]
application_capabilities = ["V2_0"]
orderer_capabilities = ["V2_0"]
}
Besu Networks
resource "chainlaunch_besu_network" "private_eth" {
name = "private-ethereum"
description = "Private Ethereum Network"
consensus = "qbft" # or "ibft2"
chain_id = 1337
# Genesis configuration
genesis_block_period_seconds = 2
genesis_epoch_length = 30000
genesis_request_timeout_seconds = 10
}
# Besu Validator Node
resource "chainlaunch_besu_node" "validator0" {
name = "validator0"
network_id = chainlaunch_besu_network.private_eth.id
node_type = "validator" # or "rpc"
version = "24.1.0"
mode = "docker"
# Network endpoints
p2p_port = 30303
rpc_http_port = 8545
rpc_ws_port = 8546
metrics_port = 9545
external_endpoint = "validator0.example.com:30303"
}
Chaincode Deployment
The provider uses a multi-step process for chaincode deployment:
# Step 1: Install chaincode package on peers
resource "chainlaunch_fabric_chaincode_install" "mycc" {
for_each = toset([
chainlaunch_fabric_peer.peer0_org1.id,
chainlaunch_fabric_peer.peer0_org2.id
])
node_id = each.value
package_id = chainlaunch_fabric_chaincode.mycc.package_id
chaincode_id = chainlaunch_fabric_chaincode.mycc.id
}
# Step 2: Define chaincode
resource "chainlaunch_fabric_chaincode" "mycc" {
label = "mycc_1.0"
language = "golang" # or "node", "java"
# Source from Git
source_type = "git"
source_url = "https://github.com/hyperledger/fabric-samples"
source_path = "asset-transfer-basic/chaincode-go"
source_ref = "main"
}
# Step 3: Approve chaincode for each org
resource "chainlaunch_fabric_chaincode_approve" "mycc_org1" {
chaincode_id = chainlaunch_fabric_chaincode.mycc.id
network_id = chainlaunch_fabric_network.mychannel.id
organization_id = chainlaunch_fabric_organization.org1.id
sequence = 1
endorsement_plugin = "escc"
validation_plugin = "vscc"
depends_on = [chainlaunch_fabric_chaincode_install.mycc]
}
# Step 4: Commit chaincode to channel
resource "chainlaunch_fabric_chaincode_commit" "mycc" {
chaincode_id = chainlaunch_fabric_chaincode.mycc.id
network_id = chainlaunch_fabric_network.mychannel.id
sequence = 1
depends_on = [
chainlaunch_fabric_chaincode_approve.mycc_org1,
chainlaunch_fabric_chaincode_approve.mycc_org2
]
}
Advanced Patterns
Multi-Organization Network
# Define multiple organizations
locals {
organizations = {
org1 = { msp_id = "Org1MSP", peer_count = 2, peer_start_port = 7051 }
org2 = { msp_id = "Org2MSP", peer_count = 2, peer_start_port = 8051 }
org3 = { msp_id = "Org3MSP", peer_count = 1, peer_start_port = 9051 }
}
}
# Create organizations
resource "chainlaunch_fabric_organization" "orgs" {
for_each = local.organizations
msp_id = each.value.msp_id
description = "Organization ${each.key}"
}
# Create peers for each organization
resource "chainlaunch_fabric_peer" "peers" {
for_each = {
for pair in flatten([
for org_name, org_config in local.organizations : [
for i in range(org_config.peer_count) : {
key = "${org_name}-peer${i}"
org = org_name
index = i
msp_id = org_config.msp_id
port_offset = org_config.peer_start_port + (i * 100)
}
]
]) : pair.key => pair
}
name = each.key
msp_id = each.value.msp_id
organization_id = chainlaunch_fabric_organization.orgs[each.value.org].id
version = "2.5.9"
mode = "docker"
listen_address = "0.0.0.0:${each.value.port_offset}"
chaincode_address = "0.0.0.0:${each.value.port_offset + 1}"
events_address = "0.0.0.0:${each.value.port_offset + 2}"
operations_listen_address = "0.0.0.0:${each.value.port_offset + 392}" # 9443 offset
external_endpoint = "${each.key}.example.com:${each.value.port_offset}"
}
High Availability Orderer Cluster
# Create orderer organization
resource "chainlaunch_fabric_organization" "orderer_org" {
msp_id = "OrdererMSP"
description = "Orderer Organization"
}
# Create 3 orderers for Raft consensus
resource "chainlaunch_fabric_orderer" "orderers" {
count = 3
name = "orderer${count.index}"
msp_id = "OrdererMSP"
organization_id = chainlaunch_fabric_organization.orderer_org.id
version = "2.5.9"
mode = "docker"
listen_address = "0.0.0.0:${7050 + (count.index * 1000)}"
admin_listen_address = "0.0.0.0:${7053 + (count.index * 1000)}"
operations_listen_address = "0.0.0.0:${9443 + (count.index * 1000)}"
external_endpoint = "orderer${count.index}.example.com:${7050 + (count.index * 1000)}"
domain_names = ["orderer${count.index}.example.com"]
}
Environment-Specific Configurations
# Development Environment
module "dev_network" {
source = "./modules/fabric-network"
environment = "dev"
peer_count = 1
orderer_count = 1
resources = {
cpu = "1"
memory = "2Gi"
storage = "10Gi"
}
}
# Production Environment
module "prod_network" {
source = "./modules/fabric-network"
environment = "prod"
peer_count = 3
orderer_count = 3
resources = {
cpu = "4"
memory = "8Gi"
storage = "100Gi"
}
high_availability = true
}
Data Sources
Query existing resources created outside of Terraform:
# Get existing organization by MSP ID
data "chainlaunch_fabric_organization" "existing_org" {
msp_id = "Org1MSP"
}
# Get existing peer by name
data "chainlaunch_fabric_peer" "existing_peer" {
name = "peer0-org1"
}
# Get existing network (channel)
data "chainlaunch_fabric_network" "existing_channel" {
name = "mychannel"
}
# Get existing Besu network
data "chainlaunch_besu_network" "existing_besu" {
name = "private-ethereum"
}
# List all key providers
data "chainlaunch_key_providers" "all" {}
# Get specific key provider
data "chainlaunch_key_provider" "vault" {
name = "vault-provider"
}
# Use in other resources
resource "chainlaunch_fabric_peer" "new_peer" {
organization_id = data.chainlaunch_fabric_organization.existing_org.id
msp_id = data.chainlaunch_fabric_organization.existing_org.msp_id
# ...
}
State Management
Remote State with S3
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "chainlaunch/production/terraform.tfstate"
region = "us-east-1"
# Enable state locking
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Terraform Cloud
terraform {
cloud {
organization = "my-org"
workspaces {
name = "chainlaunch-production"
}
}
}
CI/CD Integration
GitHub Actions
name: Terraform
on:
push:
branches: [main]
pull_request:
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.5.0
- name: Terraform Init
run: terraform init
env:
TF_VAR_chainlaunch_api_key: ${{ secrets.CHAINLAUNCH_API_KEY }}
- name: Terraform Plan
run: terraform plan
env:
TF_VAR_chainlaunch_api_key: ${{ secrets.CHAINLAUNCH_API_KEY }}
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve
env:
TF_VAR_chainlaunch_api_key: ${{ secrets.CHAINLAUNCH_API_KEY }}
GitLab CI
stages:
- validate
- plan
- apply
variables:
TF_ROOT: ${CI_PROJECT_DIR}
TF_VERSION: 1.5.0
before_script:
- cd ${TF_ROOT}
validate:
stage: validate
script:
- terraform init -backend=false
- terraform validate
plan:
stage: plan
script:
- terraform init
- terraform plan -out=plan.tfplan
artifacts:
paths:
- plan.tfplan
apply:
stage: apply
script:
- terraform init
- terraform apply plan.tfplan
only:
- main
when: manual
Best Practices
1. Use Modules
Organize reusable infrastructure:
terraform/
├── modules/
│ ├── fabric-network/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── besu-network/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ └── prod/
│ ├── main.tf
│ └── terraform.tfvars
└── main.tf
2. Version Pin Dependencies
terraform {
required_version = "~> 1.5.0"
required_providers {
chainlaunch = {
source = "kfsoftware/chainlaunch"
version = "~> 1.0.0"
}
}
}
3. Use Variables for Configuration
variable "network_name" {
description = "Name of the blockchain network"
type = string
}
variable "peer_resources" {
description = "Resource limits for peer nodes"
type = object({
cpu = string
memory = string
storage = string
})
default = {
cpu = "2"
memory = "4Gi"
storage = "20Gi"
}
}
4. Output Important Values
output "network_id" {
description = "ID of the created network"
value = chainlaunch_fabric_network.main.id
}
output "api_endpoints" {
description = "API endpoints for peer nodes"
value = {
for peer in chainlaunch_peer.peers :
peer.name => peer.api_url
}
}
5. Use Workspaces for Environments
# Create workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# Switch between environments
terraform workspace select dev
terraform apply
terraform workspace select prod
terraform apply
Troubleshooting
Common Issues
Issue: Provider authentication failed
Error: Failed to authenticate with ChainLaunch API
Solution:
- Verify API key is correct in
terraform.tfvars - Check API URL is accessible
- Ensure API key has sufficient permissions
Issue: Resource already exists
Error: Resource already exists: organization "org1"
Solution:
# Import existing resource into state
terraform import chainlaunch_organization.org1 org-123456
Issue: State lock timeout
Error: Error acquiring the state lock
Solution:
# Force unlock (use with caution!)
terraform force-unlock LOCK_ID
Migration from Manual Setup
Step 1: Export Existing Configuration
Use the ChainLaunch CLI to export existing resources:
chainlaunch export terraform --output ./existing-infra.tf
Step 2: Import Resources
# Import organizations
terraform import chainlaunch_organization.org1 org-123456
# Import peers
terraform import chainlaunch_peer.peer0 peer-789012
# Import network
terraform import chainlaunch_fabric_network.main network-345678
Step 3: Verify State
terraform plan
# Should show "No changes" if import was successful