Files
smom-dbis-138/terraform/phases/phase1/phase1-main.tf
defiQUG 1fb7266469 Add Oracle Aggregator and CCIP Integration
- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control.
- Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities.
- Created .gitmodules to include OpenZeppelin contracts as a submodule.
- Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment.
- Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks.
- Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring.
- Created scripts for resource import and usage validation across non-US regions.
- Added tests for CCIP error handling and integration to ensure robust functionality.
- Included various new files and directories for the orchestration portal and deployment scripts.
2025-12-12 14:57:48 -08:00

485 lines
16 KiB
HCL

# Phase 1: Initial Deployment - 5 US Commercial Azure Regions
# DeFi Oracle Meta Mainnet (ChainID 138)
#
# Architecture:
# - West Europe: Admin/control-plane only (no workload)
# - 5 US Commercial Azure regions: VMs with Standard_D8plsv6
# - Ubuntu 22.04 Gen 2, Docker Engine, NVM, Node 22 LTS, JDK 17
# - Nginx Proxy Server to connect Cloudflare to the 5 regions
# - SSH access for management
#
# NOTE: Phase 3 (36-region global AKS deployment) is archived in phases/phase3/
terraform {
required_version = ">= 1.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
# Backend configuration is in backend.tf (separate file)
}
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = var.environment == "prod" ? true : false
}
key_vault {
purge_soft_delete_on_destroy = var.environment == "prod" ? false : true
recover_soft_deleted_key_vaults = true
}
}
}
# Local values for naming and configuration
locals {
cloud_provider = "az"
env_codes = {
prod = "p"
dev = "d"
test = "t"
staging = "s"
}
env_code = local.env_codes[var.environment]
# 5 US Commercial Azure regions for Phase 1
us_regions = [
"eastus",
"westus",
"centralus",
"eastus2",
"westus2"
]
# Region code mapping for US regions
us_region_codes = {
eastus = "eus"
westus = "wus"
centralus = "cus"
eastus2 = "eus2"
westus2 = "wus2"
}
# Region-specific address spaces (to avoid conflicts if VPN/ExpressRoute connects regions)
region_address_spaces = {
eastus = "10.1.0.0/16"
westus = "10.2.0.0/16"
centralus = "10.3.0.0/16"
eastus2 = "10.4.0.0/16"
westus2 = "10.5.0.0/16"
westeurope = "10.10.0.0/16"
}
# Region-specific subnet prefixes
region_subnet_prefixes = {
eastus = "10.1.1.0/24"
westus = "10.2.1.0/24"
centralus = "10.3.1.0/24"
eastus2 = "10.4.1.0/24"
westus2 = "10.5.1.0/24"
westeurope = "10.10.1.0/24"
}
# Phase 1 configuration for each US region
# NOTE: Standard_D8pls_v6 is ARM64-only, but we're using x64 Ubuntu image
# Using Standard_D8s_v6 (x64) for compatibility with x64 Ubuntu 22.04 Gen 2 image
# East US has capacity restrictions for D8s_v6, using D8s_v5 as fallback
phase1_config = {
for region in local.us_regions : region => {
location = region
region_code = lookup(local.us_region_codes, region, substr(region, 0, 3))
# Use Standard_D8s_v4 for eastus (quota restrictions), Standard_D8s_v6 for others
vm_size = region == "eastus" ? "Standard_D8s_v4" : "Standard_D8s_v6"
vm_count = 1 # 1 VM per region for Phase 1
}
}
# Common tags
common_tags = merge(var.tags, {
Environment = var.environment
CostCenter = "Blockchain"
Owner = "DevOps Team"
NamingConvention = "az-env-region-resource-instance"
DeploymentPhase = "phase1"
Project = "DeFi Oracle Meta Mainnet"
ChainID = "138"
})
# Naming for admin resources (West Europe)
name_prefix = "${local.cloud_provider}-${local.env_code}-wst"
kv_secrets = "${local.name_prefix}-kv-secrets-001"
resource_group_name = var.resource_group_name != "" ? var.resource_group_name : "${local.cloud_provider}-${local.env_code}-wst-rg-comp-001"
}
# Resource Groups for each US region
resource "azurerm_resource_group" "phase1_us_regions" {
for_each = local.phase1_config
name = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-rg-comp-001"
location = each.value.location
tags = merge(local.common_tags, {
Region = each.value.location
Deployment = "phase1-us-regions"
})
}
# Storage accounts for boot diagnostics (one per region)
# Improved naming to reduce collision risk: add region index for additional uniqueness
resource "azurerm_storage_account" "boot_diagnostics" {
for_each = local.phase1_config
# Improved naming: add region index to reduce collision risk
name = substr("${local.cloud_provider}${local.env_code}${each.value.region_code}diag${substr(md5("${each.value.location}-boot-${each.key}"), 0, 6)}", 0, 24)
resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
location = each.value.location
account_tier = "Standard"
account_replication_type = "LRS"
min_tls_version = "TLS1_2"
tags = merge(local.common_tags, {
Region = each.value.location
Purpose = "boot-diagnostics"
Deployment = "phase1-us-regions"
})
}
# Storage accounts for backups (one per region)
module "storage_phase1" {
for_each = local.phase1_config
source = "../../modules/storage"
resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
location = each.value.location
cluster_name = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
environment = var.environment
tags = merge(local.common_tags, {
Region = each.value.location
Deployment = "phase1-us-regions"
})
}
# Log Analytics Workspace (one per region for monitoring)
module "monitoring_phase1" {
for_each = local.phase1_config
source = "../../modules/monitoring"
resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
location = each.value.location
cluster_name = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
environment = var.environment
tags = merge(local.common_tags, {
Region = each.value.location
Deployment = "phase1-us-regions"
})
}
# Recovery Services Vault (one per region for backups)
module "backup_phase1" {
for_each = local.phase1_config
source = "../../modules/backup"
resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
location = each.value.location
cluster_name = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
environment = var.environment
tags = merge(local.common_tags, {
Region = each.value.location
Deployment = "phase1-us-regions"
})
}
# Networking for each US region
module "networking_phase1" {
for_each = local.phase1_config
source = "../../modules/networking-vm"
resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
location = each.value.location
cluster_name = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
environment = var.environment
vnet_address_space = lookup(local.region_address_spaces, each.value.location, "10.0.0.0/16")
subnet_address_prefix = lookup(local.region_subnet_prefixes, each.value.location, "10.0.1.0/24")
allowed_ssh_ips = var.allowed_ssh_ips
allowed_rpc_ips = var.allowed_rpc_ips
allowed_p2p_ips = var.allowed_p2p_ips
allowed_metrics_ips = var.allowed_metrics_ips
tags = merge(local.common_tags, {
Region = each.value.location
Deployment = "phase1-us-regions"
})
}
# VMs for each US region
module "vm_phase1" {
for_each = local.phase1_config
source = "../../modules/vm-deployment"
resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
location = each.value.location
cluster_name = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
node_type = "besu-node"
node_count = each.value.vm_count
vm_size = each.value.vm_size
subnet_id = module.networking_phase1[each.key].vm_subnet_id
network_security_group_id = module.networking_phase1[each.key].vm_nsg_id
admin_username = var.vm_admin_username
ssh_public_key = var.ssh_public_key
storage_account_name = azurerm_storage_account.boot_diagnostics[each.key].name # Boot diagnostics storage
key_vault_id = module.keyvault.key_vault_id
genesis_file_path = "" # Will be configured later
use_phase1_cloud_init = true # Use Phase 1 cloud-init with NVM, Node 22, JDK 17
tags = merge(local.common_tags, {
Region = each.value.location
Deployment = "phase1-us-regions"
Environment = var.environment
})
}
# Networking for Nginx Proxy (West Europe admin region)
module "networking_admin" {
source = "../../modules/networking-vm"
resource_group_name = azurerm_resource_group.main[0].name
location = var.location # West Europe
cluster_name = "${local.cloud_provider}-${local.env_code}-wst-proxy"
environment = var.environment
vnet_address_space = lookup(local.region_address_spaces, var.location, "10.10.0.0/16")
subnet_address_prefix = lookup(local.region_subnet_prefixes, var.location, "10.10.1.0/24")
allowed_ssh_ips = var.allowed_ssh_ips
allowed_rpc_ips = var.allowed_rpc_ips
allowed_p2p_ips = var.allowed_p2p_ips
allowed_metrics_ips = var.allowed_metrics_ips
subnet_nsg_enabled = false # Nginx proxy uses NIC-level NSG, not subnet-level
enable_besu_rules = false # Nginx proxy doesn't need Besu-specific rules
tags = merge(local.common_tags, {
Region = var.location
Deployment = "admin-control-plane"
})
}
# Nginx Proxy Server (deployed in West Europe admin region)
module "nginx_proxy" {
source = "../../modules/nginx-proxy"
resource_group_name = azurerm_resource_group.main[0].name
location = var.location # West Europe
cluster_name = "${local.cloud_provider}-${local.env_code}-wst-proxy"
subnet_id = module.networking_admin.vm_subnet_id
backend_vms = {
for k, v in module.vm_phase1 : k => {
region = v.location
private_ips = v.private_ip_addresses # Backend VMs use private IPs only
public_ips = [] # No public IPs for backend VMs (Cloudflare Tunnel handles connectivity)
}
}
admin_username = var.vm_admin_username
ssh_public_key = var.ssh_public_key
environment = var.environment
tags = local.common_tags
depends_on = [
module.vm_phase1,
module.networking_phase1,
module.networking_admin
]
}
# West Europe admin resource group (for Key Vault and Nginx Proxy)
resource "azurerm_resource_group" "main" {
count = var.use_well_architected ? 0 : 1
name = local.resource_group_name
location = var.location # West Europe
tags = merge(local.common_tags, {
Region = var.location
Deployment = "admin-control-plane"
})
}
# Key Vault (shared, in West Europe admin region)
module "keyvault" {
source = "../../modules/secrets"
resource_group_name = var.use_well_architected ? var.security_resource_group_name : azurerm_resource_group.main[0].name
location = var.location
key_vault_name = var.key_vault_name != "" ? var.key_vault_name : local.kv_secrets
environment = var.environment
allowed_ips = var.key_vault_allowed_ips
allowed_subnets = var.key_vault_allowed_subnets
tags = local.common_tags
}
# Data source for current client config (for Key Vault access policies)
data "azurerm_client_config" "current" {}
# Key Vault access policies for VM Managed Identities
# NOTE: VMs need access to Key Vault to retrieve secrets using Managed Identity
# Use static keys from phase1_config to avoid for_each dependency issues
resource "azurerm_key_vault_access_policy" "vm_phase1" {
for_each = local.phase1_config
key_vault_id = module.keyvault.key_vault_id
tenant_id = data.azurerm_client_config.current.tenant_id
# Use the principal_id from the VM module output (will be known after VM creation)
object_id = module.vm_phase1[each.key].principal_ids[0]
secret_permissions = [
"Get",
"List"
]
key_permissions = [
"Get",
"List"
]
certificate_permissions = [
"Get",
"List"
]
depends_on = [
module.vm_phase1,
module.keyvault
]
}
# Key Vault access policy for Nginx Proxy Managed Identity
resource "azurerm_key_vault_access_policy" "nginx_proxy" {
key_vault_id = module.keyvault.key_vault_id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = module.nginx_proxy.principal_id
secret_permissions = [
"Get",
"List"
]
key_permissions = [
"Get",
"List"
]
certificate_permissions = [
"Get",
"List"
]
depends_on = [
module.nginx_proxy,
module.keyvault
]
}
# Outputs
output "phase1_us_regions" {
value = {
for k, v in module.vm_phase1 : k => {
region = local.phase1_config[k].location
resource_group = azurerm_resource_group.phase1_us_regions[k].name
vm_names = v.vm_names
public_ips = v.public_ip_addresses
private_ips = v.private_ip_addresses
boot_diagnostics_storage = azurerm_storage_account.boot_diagnostics[k].name
backup_storage = module.storage_phase1[k].backup_storage_account_name
}
}
description = "Phase 1 US region deployment information"
}
output "nginx_proxy" {
value = {
fqdn = module.nginx_proxy.fqdn
public_ip = module.nginx_proxy.public_ip
private_ip = module.nginx_proxy.private_ip
backend_count = length(local.us_regions)
note = "Backend VMs use private IPs only. Deploy VPN/ExpressRoute or Cloudflare Tunnel on backend VMs for connectivity."
}
description = "Nginx Proxy Server information (Cloudflare Tunnel integration point)"
}
output "key_vault_name" {
value = module.keyvault.key_vault_name
description = "Name of the Key Vault (West Europe admin region)"
}
output "monitoring" {
value = {
for k, v in module.monitoring_phase1 : k => {
log_analytics_workspace_id = v.log_analytics_workspace_id
log_analytics_workspace_name = v.log_analytics_workspace_name
}
}
description = "Log Analytics Workspace information for monitoring"
}
output "backups" {
value = {
for k, v in module.backup_phase1 : k => {
recovery_services_vault_id = v.recovery_services_vault_id
recovery_services_vault_name = v.recovery_services_vault_name
backup_policy_id = v.backup_policy_id
}
}
description = "Recovery Services Vault information for backups"
}
# Comprehensive outputs with SSH strings and resource IDs
output "ssh_connection_strings" {
value = {
for k, v in module.vm_phase1 : k => {
for idx, vm_name in v.vm_names : vm_name => "ssh ${var.vm_admin_username}@${length(v.public_ip_addresses) > idx ? v.public_ip_addresses[idx] : v.private_ip_addresses[idx]}"
}
}
description = "SSH connection strings for VMs (uses public IP if available, otherwise private IP)"
}
output "nginx_proxy_ssh" {
value = "ssh ${var.vm_admin_username}@${module.nginx_proxy.public_ip}"
description = "SSH connection string for Nginx Proxy"
}
output "resource_ids" {
value = {
resource_groups = {
for k, v in azurerm_resource_group.phase1_us_regions : k => v.id
}
vms = {
for k, v in module.vm_phase1 : k => {
for idx, vm_id in v.vm_ids : v.vm_names[idx] => vm_id
}
}
key_vault = {
id = module.keyvault.key_vault_id
uri = module.keyvault.key_vault_uri
}
}
description = "Resource IDs for all deployed resources"
}
output "storage_accounts" {
value = {
boot_diagnostics = {
for k, v in azurerm_storage_account.boot_diagnostics : k => {
name = v.name
location = v.location
}
}
backups = {
for k, v in module.storage_phase1 : k => {
backup_storage = v.backup_storage_account_name
shared_storage = v.shared_storage_account_name
}
}
}
description = "Storage account information for boot diagnostics and backups"
}