ChainLaunch

Terraform Provider for ChainLaunch

ChainLaunch includes a Terraform provider that enables Infrastructure-as-Code (IaC) workflows for managing blockchain networks, nodes, and configurations.…

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