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