Skip to main content

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 nodes
  • chainlaunch_fabric_orderer - Orderer nodes
  • chainlaunch_fabric_network - Channels
  • chainlaunch_fabric_chaincode - Chaincode packages
  • chainlaunch_fabric_chaincode_install - Install chaincode on peers
  • chainlaunch_fabric_chaincode_approve - Approve chaincode per org
  • chainlaunch_fabric_chaincode_commit - Commit chaincode to channel
  • chainlaunch_fabric_identity - User identities
  • chainlaunch_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 targets
  • chainlaunch_backup_schedule - Automated backup schedules
  • chainlaunch_metrics_prometheus - Prometheus integration
  • chainlaunch_notification_provider - Email notifications
  • chainlaunch_plugin - Custom plugins
  • chainlaunch_plugin_deployment - Plugin deployments

Node Sharing:

  • chainlaunch_node_invitation - Share nodes with other instances
  • chainlaunch_node_accept_invitation - Accept node invitations
  • chainlaunch_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:

  1. API Key (Recommended for automation):
provider "chainlaunch" {
url = "http://localhost:8100"
api_key = var.chainlaunch_api_key
}
  1. 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 URL
  • CHAINLAUNCH_API_KEY - API key
  • CHAINLAUNCH_USERNAME - Username
  • CHAINLAUNCH_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

See Also​