# VM Deployment Module for Besu Network # Deploys Besu nodes on Virtual Machines or VM Scale Sets with Docker Engine # Variables are defined in variables.tf # Network Interface resource "azurerm_network_interface" "besu_node" { count = var.use_scale_set ? 0 : var.node_count name = "${var.cluster_name}-${var.node_type}-nic-${count.index}" location = var.location resource_group_name = var.resource_group_name ip_configuration { name = "internal" subnet_id = var.subnet_id private_ip_address_allocation = "Dynamic" # Only sentry and RPC nodes get public IPs; besu-node uses private IPs only public_ip_address_id = (var.node_type == "sentry" || var.node_type == "rpc") ? azurerm_public_ip.besu_node[count.index].id : null } tags = merge(var.tags, { NodeType = var.node_type NodeIndex = count.index }) } # Associate NSG with NIC resource "azurerm_network_interface_security_group_association" "besu_node" { count = var.use_scale_set ? 0 : var.node_count network_interface_id = azurerm_network_interface.besu_node[count.index].id network_security_group_id = var.network_security_group_id } # Public IP for sentry and RPC nodes only (besu-node uses private IPs only) # Note: Phase 1 backend VMs use private IPs only; Nginx proxy connects via Cloudflare Tunnel resource "azurerm_public_ip" "besu_node" { count = var.use_scale_set ? 0 : (var.node_type == "sentry" || var.node_type == "rpc" ? var.node_count : 0) name = "${var.cluster_name}-${var.node_type}-ip-${count.index}" location = var.location resource_group_name = var.resource_group_name allocation_method = "Static" sku = "Standard" tags = merge(var.tags, { NodeType = var.node_type NodeIndex = count.index }) } # Virtual Machine resource "azurerm_linux_virtual_machine" "besu_node" { count = var.use_scale_set ? 0 : var.node_count name = "${var.cluster_name}-${var.node_type}-${count.index}" location = var.location resource_group_name = var.resource_group_name size = var.vm_size admin_username = var.admin_username network_interface_ids = [azurerm_network_interface.besu_node[count.index].id] admin_ssh_key { username = var.admin_username public_key = var.ssh_public_key } os_disk { name = "${var.cluster_name}-${var.node_type}-disk-${count.index}" caching = "ReadWrite" storage_account_type = var.storage_account_type disk_size_gb = var.disk_size_gb } source_image_reference { publisher = "Canonical" offer = "0001-com-ubuntu-server-jammy" sku = "22_04-lts-gen2" version = "latest" } dynamic "boot_diagnostics" { for_each = var.vm_enable_boot_diagnostics && var.storage_account_name != "" ? [1] : [] content { storage_account_uri = var.storage_account_name != "" ? "https://${var.storage_account_name}.blob.core.windows.net/" : null } } dynamic "identity" { for_each = var.vm_enable_managed_identity ? [1] : [] content { type = "SystemAssigned" } } custom_data = base64encode(templatefile( var.use_phase1_cloud_init ? "${path.module}/cloud-init-phase1.yaml" : "${path.module}/cloud-init.yaml", { node_type = var.node_type node_index = count.index cluster_name = var.cluster_name key_vault_id = var.key_vault_id genesis_file_path = var.genesis_file_path admin_username = var.admin_username } )) tags = merge(var.tags, { NodeType = var.node_type NodeIndex = count.index }) depends_on = [azurerm_network_interface.besu_node] } # VM Scale Set (alternative to individual VMs) resource "azurerm_linux_virtual_machine_scale_set" "besu_node" { count = var.use_scale_set ? 1 : 0 name = "${var.cluster_name}-${var.node_type}-vmss" location = var.location resource_group_name = var.resource_group_name sku = var.vm_size instances = var.node_count admin_username = var.admin_username admin_ssh_key { username = var.admin_username public_key = var.ssh_public_key } source_image_reference { publisher = "Canonical" offer = "0001-com-ubuntu-server-jammy" sku = "22_04-lts-gen2" version = "latest" } os_disk { storage_account_type = var.storage_account_type caching = "ReadWrite" disk_size_gb = var.disk_size_gb } network_interface { name = "${var.cluster_name}-${var.node_type}-nic" primary = true ip_configuration { name = "internal" primary = true subnet_id = var.subnet_id # Only sentry and RPC nodes get public IPs; besu-node uses private IPs only # Match the logic used for individual VMs dynamic "public_ip_address" { for_each = (var.node_type == "sentry" || var.node_type == "rpc") ? [1] : [] content { name = "${var.cluster_name}-${var.node_type}-public-ip" } } } } dynamic "identity" { for_each = var.vm_enable_managed_identity ? [1] : [] content { type = "SystemAssigned" } } custom_data = base64encode(templatefile( var.use_phase1_cloud_init ? "${path.module}/cloud-init-phase1.yaml" : "${path.module}/cloud-init.yaml", { node_type = var.node_type node_index = 0 cluster_name = var.cluster_name key_vault_id = var.key_vault_id genesis_file_path = var.genesis_file_path admin_username = var.admin_username } )) upgrade_mode = "Manual" tags = merge(var.tags, { NodeType = var.node_type }) } # Outputs are defined in outputs.tf