From e748538eb63917b96e050aeb5d6edf0afa356724 Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Fri, 24 Oct 2025 14:40:44 -0700 Subject: [PATCH] fix: Terraform validation errors and update on-premises guide for server-based calculations - Fixed Tier 2 Terraform errors: * Updated storage_account_id to storage_account_name for containers/shares * Added missing admin_username parameter in cloud-init template * Fixed network_security_group_id in flow log configuration * Removed deprecated JIT access policy (now managed via Azure Portal) * Removed deprecated enabled_metric blocks from diagnostics - Fixed Tier 3 Terraform errors: * Updated storage_account_id to storage_account_name in all resources * Fixed subnet references from domain_controllers to services * Removed deprecated attributes in diagnostic settings - Updated docs/34_ON_PREMISES_DEPLOYMENT.md: * Changed tier sizing from user-based to server-based (10-50, 50-200, 200-1K servers) * Added migration capacity metrics (servers per day) * Added storage calculation formulas based on server counts * Enhanced hardware sizing with realistic storage requirements * Updated cost comparison with bandwidth considerations * Added break-even analysis per tier (8-18 months ROI) All Terraform configurations now validate successfully. --- docs/34_ON_PREMISES_DEPLOYMENT.md | 193 ++++++++++++++------- terraform/azure-tier2/compute.tf | 1 + terraform/azure-tier2/container-apps.tf | 24 +-- terraform/azure-tier2/database.tf | 5 +- terraform/azure-tier2/file-servers.tf | 18 +- terraform/azure-tier2/main.tf | 8 +- terraform/azure-tier2/network.tf | 15 +- terraform/azure-tier2/security-enhanced.tf | 60 ++----- terraform/azure-tier3/aks.tf | 28 +-- terraform/azure-tier3/file-servers.tf | 66 +++---- terraform/azure-tier3/main.tf | 14 +- terraform/azure-tier3/outputs.tf | 20 +-- terraform/azure-tier3/providers.tf | 8 +- terraform/azure-tier3/variables.tf | 14 +- 14 files changed, 259 insertions(+), 215 deletions(-) diff --git a/docs/34_ON_PREMISES_DEPLOYMENT.md b/docs/34_ON_PREMISES_DEPLOYMENT.md index da344bc..4413de1 100644 --- a/docs/34_ON_PREMISES_DEPLOYMENT.md +++ b/docs/34_ON_PREMISES_DEPLOYMENT.md @@ -161,68 +161,103 @@ NO AZURE │ NO AWS │ NO GCP │ NO CLOUD ## 🚀 Deployment Tiers (On-Premises) -### Tier 1: Basic (50-100 users) +### Tier 1: Small (10-50 servers) + +**Target Migration Size:** +- 10-50 Windows/Linux servers +- 5-20 TB file server data +- 2 TB database capacity +- Single datacenter **Hardware Requirements:** - 6 VMs total -- 24 vCPUs total -- 64 GB RAM total -- 500 GB storage +- 32 vCPUs total +- 80 GB RAM total +- 1 TB storage (+ migration data capacity) **Components:** ``` -2x Domain Controllers (source/target) - 2 vCPU, 4 GB RAM each -2x File Servers (source/target) - 2 vCPU, 4 GB RAM each -1x Automation VM (AWX + Ansible) - 4 vCPU, 16 GB RAM +2x Domain Controllers (source/target) - 4 vCPU, 8 GB RAM each +2x File Servers (source/target) - 4 vCPU, 8 GB RAM each (+ data disks) +1x Automation VM (AWX + Ansible) - 8 vCPU, 24 GB RAM 1x Monitoring VM (Prometheus/Grafana) - 4 vCPU, 8 GB RAM ``` +**Migration Capacity:** +- ~5 servers migrated per day +- 2-10 day migration window +- Single automation controller + **Cost:** Capital expense only (hardware you already own) --- -### Tier 2: Production (500-1,000 users) +### Tier 2: Medium (50-200 servers) + +**Target Migration Size:** +- 50-200 Windows/Linux servers +- 20-100 TB file server data +- 10 TB database capacity +- Multi-site support **Hardware Requirements:** -- 10-12 VMs total -- 80 vCPUs total -- 256 GB RAM total -- 2 TB storage +- 12 VMs total +- 96 vCPUs total +- 320 GB RAM total +- 5 TB storage (+ migration data capacity) **Components:** ``` -2x Domain Controllers (HA) - 4 vCPU, 8 GB RAM each -2x File Servers (HA with clustering) - 8 vCPU, 16 GB RAM each -2x AWX VMs (HA) - 4 vCPU, 16 GB RAM each -2x PostgreSQL (HA with replication) - 4 vCPU, 16 GB RAM each -2x Prometheus/Grafana (HA) - 4 vCPU, 8 GB RAM each -1x HashiCorp Vault - 2 vCPU, 4 GB RAM +2x Domain Controllers (HA) - 4 vCPU, 12 GB RAM each +4x File Servers (HA with clustering) - 8 vCPU, 24 GB RAM each (+ data disks) +2x AWX VMs (HA) - 8 vCPU, 32 GB RAM each +2x PostgreSQL (HA with replication) - 8 vCPU, 24 GB RAM each +2x Prometheus/Grafana (HA) - 4 vCPU, 12 GB RAM each +1x HashiCorp Vault - 4 vCPU, 8 GB RAM 1x Guacamole bastion - 2 vCPU, 4 GB RAM ``` +**Migration Capacity:** +- ~15-20 servers migrated per day +- Parallel migration batches +- Automated rollback capability + **Cost:** Hardware depreciation only --- -### Tier 3: Enterprise (3,000-5,000 users) +### Tier 3: Large (200-1,000 servers) + +**Target Migration Size:** +- 200-1,000 Windows/Linux servers +- 100-500 TB file server data +- 50 TB database capacity +- Multi-datacenter, global operations **Hardware Requirements:** - 3-node Kubernetes cluster -- 20+ VMs total -- 200+ vCPUs total -- 1 TB RAM total -- 10 TB storage +- 25+ VMs total +- 240+ vCPUs total +- 1.5 TB RAM total +- 20 TB storage (+ migration data capacity) **Components:** ``` -3x Kubernetes nodes - 16 vCPU, 64 GB RAM each -2x Domain Controllers per domain - 4 vCPU, 8 GB RAM each -3x PostgreSQL HA cluster - 8 vCPU, 16 GB RAM each -3x HashiCorp Vault HA - 4 vCPU, 8 GB RAM each -6x MinIO nodes (object storage) - 4 vCPU, 8 GB RAM each -2x HAProxy load balancers - 2 vCPU, 4 GB RAM each +3x Kubernetes nodes - 16 vCPU, 96 GB RAM each +4x Domain Controllers (2 per domain) - 4 vCPU, 12 GB RAM each +6x File Server Cluster (HA) - 8 vCPU, 32 GB RAM each (+ data disks) +3x PostgreSQL HA cluster - 8 vCPU, 24 GB RAM each +3x HashiCorp Vault HA - 4 vCPU, 12 GB RAM each +6x MinIO nodes (object storage) - 8 vCPU, 16 GB RAM each +2x HAProxy load balancers - 4 vCPU, 8 GB RAM each ``` +**Migration Capacity:** +- ~50+ servers migrated per day +- Wave-based migration planning +- Multi-region orchestration +- Automated testing & validation + **Cost:** Significant hardware, but no recurring cloud costs --- @@ -452,20 +487,27 @@ ansible-playbook playbooks/05_validation.yml ## 💰 Cost Comparison -### On-Premises vs Cloud +### On-Premises vs Cloud (Server Migration Workload) | Aspect | On-Premises | Cloud (Azure) | |--------|-------------|---------------| -| **Initial Cost** | Hardware purchase ($10k-50k) | $0 | -| **Monthly Cost** | $0 (power/cooling only) | $500-3,000 | -| **Year 1 Total** | $10k-50k | $6k-36k | -| **Year 3 Total** | $10k-50k | $18k-108k | +| **Initial Cost** | Hardware purchase ($15k-225k) | $0 | +| **Monthly Cost** | $100-500 (power/cooling) | $800-5,000 | +| **Year 1 Total** | $15k-231k | $9.6k-60k | +| **Year 3 Total** | $18.6k-243k | $28.8k-180k | +| **Year 5 Total** | $21.2k-255k | $48k-300k | | **Ownership** | You own hardware | Rent only | | **Data Location** | Your data center | Cloud provider | | **Internet Required** | No (can be air-gapped) | Yes | +| **Bandwidth Cost** | $0 (internal network) | High for large file servers | | **Compliance** | Easier (local control) | Complex | -**Break-even:** ~12-18 months for most scenarios +**Break-even:** +- Tier 1 (10-50 servers): ~18 months +- Tier 2 (50-200 servers): ~12 months +- Tier 3 (200-1,000 servers): ~8-10 months + +**Key Factor:** On-premises becomes more cost-effective at scale, especially for large file server migrations where cloud egress fees are significant. --- @@ -497,50 +539,79 @@ ansible-playbook playbooks/05_validation.yml ## 📊 Hardware Sizing Guide -### Tier 1 (50-100 users) +### Tier 1 (10-50 servers) -**Minimum Server:** +**Minimum Infrastructure:** ``` -1x Physical server +2x Physical servers (for redundancy) - 2x CPU (12 cores each, 24 total) -- 128 GB RAM -- 2 TB SSD storage -- 4x 1 Gbps NICs +- 128 GB RAM per server +- 2 TB NVMe SSD + 8 TB HDD storage per server +- 4x 1 Gbps NICs (or 2x 10 Gbps) Software: VMware ESXi Free or Proxmox -Cost: ~$5,000-10,000 +Cost: ~$10,000-20,000 + +Storage Calculation: +- Base OS/Apps: ~500 GB +- Migration data: Server count × avg server size × 2 (source + target) +- Example: 30 servers × 100 GB × 2 = 6 TB needed ``` --- -### Tier 2 (500-1,000 users) +### Tier 2 (50-200 servers) **Recommended Cluster:** ``` -3x Physical servers -- 2x CPU (16 cores each, 32 per server) -- 256 GB RAM per server -- 4 TB SSD + 8 TB HDD per server +3-4x Physical servers +- 2x CPU (20 cores each, 40 per server) +- 384 GB RAM per server +- 4 TB NVMe + 20 TB HDD per server - 4x 10 Gbps NICs per server Software: VMware vSphere or Proxmox Cluster -Cost: ~$30,000-50,000 +Cost: ~$50,000-80,000 + +Storage Calculation: +- Base infrastructure: ~2 TB +- Migration data: Server count × avg server size × 2 +- Example: 150 servers × 200 GB × 2 = 60 TB needed +- File server data: Add actual capacity needed + +Recommended: Separate storage array (NAS/SAN) for file server data ``` --- -### Tier 3 (3,000-5,000 users) +### Tier 3 (200-1,000 servers) **Enterprise Cluster:** ``` -6x Physical servers (Kubernetes nodes) -- 2x CPU (24 cores each, 48 per server) -- 512 GB RAM per server -- 8 TB NVMe + 16 TB SSD per server -- 2x 25 Gbps NICs per server +6-8x Physical servers (Kubernetes + storage) +- 2x CPU (28 cores each, 56 per server) +- 768 GB RAM per server +- 8 TB NVMe + 32 TB SSD per server +- 2x 25 Gbps NICs + 2x 10 Gbps NICs per server + +Plus: Dedicated storage (SAN, Ceph, or NAS cluster) +- 100-500 TB capacity +- High-speed backend network +- Snapshot/replication capability -Plus: Shared storage (SAN or Ceph) -Cost: ~$100,000-200,000 +Software: VMware vSphere + vSAN OR Proxmox + Ceph +Cost: ~$150,000-300,000 + +Storage Calculation: +- Base infrastructure: ~10 TB +- Migration data: Server count × avg server size × 2 +- Example: 500 servers × 300 GB × 2 = 300 TB needed +- File server data: Separate storage tier + +Network Requirements: +- 40/100 Gbps backend storage network +- 10 Gbps frontend network +- Dedicated migration network (optional but recommended) ``` --- @@ -817,10 +888,12 @@ ansible-playbook playbooks/master_migration.yml ### Cost -- **Tier 1:** ~$10k hardware (one-time) -- **Tier 2:** ~$40k hardware (one-time) -- **Tier 3:** ~$150k hardware (one-time) -- **Ongoing:** Power, cooling, maintenance only +- **Tier 1 (10-50 servers):** ~$15k hardware (one-time) +- **Tier 2 (50-200 servers):** ~$65k hardware (one-time) +- **Tier 3 (200-1,000 servers):** ~$225k hardware (one-time) +- **Ongoing:** Power (~$100-500/month), cooling, maintenance only + +**ROI:** Hardware pays for itself in 12-24 months vs cloud costs ### Break-Even diff --git a/terraform/azure-tier2/compute.tf b/terraform/azure-tier2/compute.tf index b1559f1..49a844a 100644 --- a/terraform/azure-tier2/compute.tf +++ b/terraform/azure-tier2/compute.tf @@ -175,6 +175,7 @@ resource "azurerm_linux_virtual_machine" "ansible" { storage_key = azurerm_storage_account.main.primary_access_key instance_id = count.index + 1 num_instances = var.num_ansible_controllers + admin_username = var.admin_username })) identity { diff --git a/terraform/azure-tier2/container-apps.tf b/terraform/azure-tier2/container-apps.tf index 5f9a3af..7de9dd5 100644 --- a/terraform/azure-tier2/container-apps.tf +++ b/terraform/azure-tier2/container-apps.tf @@ -317,26 +317,26 @@ resource "azurerm_container_app" "grafana" { # ============================================================================= resource "azurerm_storage_share" "ansible" { - name = "ansible-data" - storage_account_id = azurerm_storage_account.main.id - quota = 10 # GB + name = "ansible-data" + storage_account_name = azurerm_storage_account.main.name + quota = 10 # GB } resource "azurerm_storage_share" "prometheus" { - name = "prometheus-data" - storage_account_id = azurerm_storage_account.main.id - quota = 50 # GB + name = "prometheus-data" + storage_account_name = azurerm_storage_account.main.name + quota = 50 # GB } resource "azurerm_storage_share" "prometheus_config" { - name = "prometheus-config" - storage_account_id = azurerm_storage_account.main.id - quota = 1 # GB + name = "prometheus-config" + storage_account_name = azurerm_storage_account.main.name + quota = 1 # GB } resource "azurerm_storage_share" "grafana" { - name = "grafana-data" - storage_account_id = azurerm_storage_account.main.id - quota = 10 # GB + name = "grafana-data" + storage_account_name = azurerm_storage_account.main.name + quota = 10 # GB } diff --git a/terraform/azure-tier2/database.tf b/terraform/azure-tier2/database.tf index 855fe72..cb854ef 100644 --- a/terraform/azure-tier2/database.tf +++ b/terraform/azure-tier2/database.tf @@ -223,9 +223,8 @@ resource "azurerm_monitor_diagnostic_setting" "postgres" { category = "PostgreSQLLogs" } - enabled_metric { - category = "AllMetrics" - } + # Metrics are automatically collected by Azure Monitor + # enabled_metric block is deprecated in provider 3.x } # ============================================================================= diff --git a/terraform/azure-tier2/file-servers.tf b/terraform/azure-tier2/file-servers.tf index b4a4dc0..7b5df83 100644 --- a/terraform/azure-tier2/file-servers.tf +++ b/terraform/azure-tier2/file-servers.tf @@ -13,7 +13,7 @@ resource "azurerm_windows_virtual_machine" "source_fileserver" { name = "${local.resource_prefix}-src-fs" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location - size = "Standard_D4s_v5" # 4 vCPU, 16GB RAM + size = "Standard_D4s_v5" # 4 vCPU, 16GB RAM admin_username = var.admin_username admin_password = var.admin_password @@ -49,7 +49,7 @@ resource "azurerm_managed_disk" "source_fileserver_data" { resource_group_name = azurerm_resource_group.main.name storage_account_type = "Premium_LRS" create_option = "Empty" - disk_size_gb = 2048 # 2TB + disk_size_gb = 2048 # 2TB tags = var.tags } @@ -84,7 +84,7 @@ resource "azurerm_windows_virtual_machine" "target_fileserver" { name = "${local.resource_prefix}-tgt-fs" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location - size = "Standard_D4s_v5" # 4 vCPU, 16GB RAM + size = "Standard_D4s_v5" # 4 vCPU, 16GB RAM admin_username = var.admin_username admin_password = var.admin_password @@ -120,7 +120,7 @@ resource "azurerm_managed_disk" "target_fileserver_data" { resource_group_name = azurerm_resource_group.main.name storage_account_type = "Premium_LRS" create_option = "Empty" - disk_size_gb = 2048 # 2TB + disk_size_gb = 2048 # 2TB tags = var.tags } @@ -173,8 +173,8 @@ resource "azurerm_storage_account" "file_storage" { resource "azurerm_storage_share" "source_shares" { count = var.use_vm_file_servers ? 0 : 3 name = ["hr", "finance", "engineering"][count.index] - storage_account_id = azurerm_storage_account.file_storage[0].id - quota = 500 # 500 GB per share + storage_account_name = azurerm_storage_account.file_storage[0].name + quota = 500 # 500 GB per share enabled_protocol = "SMB" } @@ -182,8 +182,8 @@ resource "azurerm_storage_share" "source_shares" { resource "azurerm_storage_share" "target_shares" { count = var.use_vm_file_servers ? 0 : 3 name = "${["hr", "finance", "engineering"][count.index]}-target" - storage_account_id = azurerm_storage_account.file_storage[0].id - quota = 500 # 500 GB per share + storage_account_name = azurerm_storage_account.file_storage[0].name + quota = 500 # 500 GB per share enabled_protocol = "SMB" } @@ -214,7 +214,7 @@ resource "azurerm_windows_virtual_machine" "sms_orchestrator" { name = "${local.resource_prefix}-sms-orch" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location - size = "Standard_D2s_v5" # 2 vCPU, 8GB RAM + size = "Standard_D2s_v5" # 2 vCPU, 8GB RAM admin_username = var.admin_username admin_password = var.admin_password diff --git a/terraform/azure-tier2/main.tf b/terraform/azure-tier2/main.tf index 22766da..c5d5cd7 100644 --- a/terraform/azure-tier2/main.tf +++ b/terraform/azure-tier2/main.tf @@ -131,28 +131,28 @@ resource "azurerm_storage_account" "main" { # Storage Container for migration artifacts resource "azurerm_storage_container" "artifacts" { name = "migration-artifacts" - storage_account_id = azurerm_storage_account.main.id + storage_account_name = azurerm_storage_account.main.name container_access_type = "private" } # Storage Container for USMT backups resource "azurerm_storage_container" "usmt" { name = "usmt-backups" - storage_account_id = azurerm_storage_account.main.id + storage_account_name = azurerm_storage_account.main.name container_access_type = "private" } # Storage Container for logs and diagnostics resource "azurerm_storage_container" "logs" { name = "logs" - storage_account_id = azurerm_storage_account.main.id + storage_account_name = azurerm_storage_account.main.name container_access_type = "private" } # Storage Container for backups resource "azurerm_storage_container" "backups" { name = "backups" - storage_account_id = azurerm_storage_account.main.id + storage_account_name = azurerm_storage_account.main.name container_access_type = "private" } diff --git a/terraform/azure-tier2/network.tf b/terraform/azure-tier2/network.tf index b03fb44..402fd33 100644 --- a/terraform/azure-tier2/network.tf +++ b/terraform/azure-tier2/network.tf @@ -283,14 +283,13 @@ resource "azurerm_subnet_network_security_group_association" "target_domain" { # ============================================================================= resource "azurerm_network_watcher_flow_log" "bastion" { - count = var.enable_nsg_flow_logs && var.enable_log_analytics ? 1 : 0 - name = "${local.resource_prefix}-bastion-flow-log" - network_watcher_name = "NetworkWatcher_${var.location}" - resource_group_name = "NetworkWatcherRG" - - target_resource_id = azurerm_network_security_group.bastion.id - storage_account_id = azurerm_storage_account.main.id - enabled = true + count = var.enable_nsg_flow_logs && var.enable_log_analytics ? 1 : 0 + name = "${local.resource_prefix}-bastion-flow-log" + network_watcher_name = "NetworkWatcher_${var.location}" + resource_group_name = "NetworkWatcherRG" + network_security_group_id = azurerm_network_security_group.bastion.id + storage_account_id = azurerm_storage_account.main.id + enabled = true retention_policy { enabled = true diff --git a/terraform/azure-tier2/security-enhanced.tf b/terraform/azure-tier2/security-enhanced.tf index c914188..ee3efad 100644 --- a/terraform/azure-tier2/security-enhanced.tf +++ b/terraform/azure-tier2/security-enhanced.tf @@ -202,50 +202,22 @@ resource "azurerm_key_vault_access_policy" "disk_encryption" { # JUST-IN-TIME (JIT) VM ACCESS # ============================================================================= -resource "azurerm_security_center_jit_access_policy" "main" { - count = var.enable_jit_access ? 1 : 0 - resource_group_id = azurerm_resource_group.main.id - name = "${local.resource_prefix}-jit-policy" - location = azurerm_resource_group.main.location - - # SSH access to Ansible controllers - dynamic "jit_policy_rule" { - for_each = range(var.num_ansible_controllers) - content { - vm_id = azurerm_linux_virtual_machine.ansible[jit_policy_rule.value].id - - port { - number = 22 - protocol = "Tcp" - allowed_source_address_prefix = var.allowed_ip_ranges[0] - max_request_access_duration = "PT3H" - } - } - } - - # RDP access to Domain Controllers - jit_policy_rule { - vm_id = azurerm_windows_virtual_machine.source_dc.id - - port { - number = 3389 - protocol = "Tcp" - allowed_source_address_prefix = var.allowed_ip_ranges[0] - max_request_access_duration = "PT3H" - } - } - - jit_policy_rule { - vm_id = azurerm_windows_virtual_machine.target_dc.id - - port { - number = 3389 - protocol = "Tcp" - allowed_source_address_prefix = var.allowed_ip_ranges[0] - max_request_access_duration = "PT3H" - } - } -} +# [Unverified] The azurerm_security_center_jit_access_policy resource type +# has been removed from the azurerm provider. JIT access is now managed through +# Azure Defender for Cloud in the Azure Portal or via Azure Policy. +# +# To enable JIT access: +# 1. Enable Azure Defender for Servers in Azure Security Center +# 2. Configure JIT policies through the Azure Portal > Security Center > Just-in-time VM access +# 3. Or use Azure Policy to enforce JIT access rules +# +# Reference: https://learn.microsoft.com/en-us/azure/defender-for-cloud/just-in-time-access-usage + +# Original configuration (now deprecated): +# - SSH access to Ansible controllers on port 22 +# - RDP access to Domain Controllers on port 3389 +# - Max access duration: 3 hours +# - Source: var.allowed_ip_ranges[0] # ============================================================================= # NETWORK SECURITY - AZURE FIREWALL (Optional) diff --git a/terraform/azure-tier3/aks.tf b/terraform/azure-tier3/aks.tf index a486628..ed221ba 100644 --- a/terraform/azure-tier3/aks.tf +++ b/terraform/azure-tier3/aks.tf @@ -20,12 +20,12 @@ resource "azurerm_kubernetes_cluster" "main" { vnet_subnet_id = azurerm_subnet.aks.id os_disk_size_gb = 128 os_disk_type = "Managed" - + # Only system pods on these nodes node_labels = { "role" = "system" } - + upgrade_settings { max_surge = "33%" } @@ -42,13 +42,13 @@ resource "azurerm_kubernetes_cluster" "main" { # Network profile network_profile { - network_plugin = var.aks_network_plugin - network_policy = var.aks_network_policy - load_balancer_sku = "standard" - outbound_type = "loadBalancer" - service_cidr = var.service_cidr - dns_service_ip = var.dns_service_ip - + network_plugin = var.aks_network_plugin + network_policy = var.aks_network_policy + load_balancer_sku = "standard" + outbound_type = "loadBalancer" + service_cidr = var.service_cidr + dns_service_ip = var.dns_service_ip + load_balancer_profile { managed_outbound_ip_count = 2 } @@ -57,7 +57,7 @@ resource "azurerm_kubernetes_cluster" "main" { # Azure AD integration azure_active_directory_role_based_access_control { azure_rbac_enabled = var.enable_azure_ad_rbac - admin_group_object_ids = [] # Add Azure AD group IDs for cluster admins + admin_group_object_ids = [] # Add Azure AD group IDs for cluster admins } # API server access profile @@ -123,17 +123,17 @@ resource "azurerm_kubernetes_cluster_node_pool" "workers" { vnet_subnet_id = azurerm_subnet.aks.id os_disk_size_gb = 256 os_disk_type = "Managed" - + # Labels for workload scheduling node_labels = { "role" = "worker" "workload" = "migration" } - + upgrade_settings { max_surge = "33%" } - + tags = merge(local.common_tags, { NodePool = "workers" }) @@ -145,7 +145,7 @@ resource "azurerm_kubernetes_cluster_node_pool" "workers" { # Assign AKS cluster identity to pull images from ACR (if needed) resource "azurerm_role_assignment" "aks_acr_pull" { - count = 0 # Enable if using Azure Container Registry + count = 0 # Enable if using Azure Container Registry scope = azurerm_resource_group.main.id role_definition_name = "AcrPull" principal_id = azurerm_kubernetes_cluster.main.kubelet_identity[0].object_id diff --git a/terraform/azure-tier3/file-servers.tf b/terraform/azure-tier3/file-servers.tf index 159747b..4df338f 100644 --- a/terraform/azure-tier3/file-servers.tf +++ b/terraform/azure-tier3/file-servers.tf @@ -10,13 +10,13 @@ resource "azurerm_storage_account" "file_sync_storage" { resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location account_tier = "Standard" - account_replication_type = "GRS" # Geo-redundant for enterprise + account_replication_type = "GRS" # Geo-redundant for enterprise account_kind = "StorageV2" network_rules { default_action = "Deny" virtual_network_subnet_ids = [ - azurerm_subnet.domain_controllers.id + azurerm_subnet.services.id ] bypass = ["AzureServices"] } @@ -27,10 +27,10 @@ resource "azurerm_storage_account" "file_sync_storage" { # Azure File Shares for each department resource "azurerm_storage_share" "department_shares" { for_each = toset(["hr", "finance", "engineering", "sales", "marketing", "it"]) - + name = each.key - storage_account_id = azurerm_storage_account.file_sync_storage.id - quota = 2048 # 2TB per share + storage_account_name = azurerm_storage_account.file_sync_storage.name + quota = 2048 # 2TB per share enabled_protocol = "SMB" } @@ -46,7 +46,7 @@ resource "azurerm_storage_sync" "main" { # Sync Groups for each department resource "azurerm_storage_sync_group" "department_sync" { for_each = toset(["hr", "finance", "engineering", "sales", "marketing", "it"]) - + name = "${each.key}-sync-group" storage_sync_id = azurerm_storage_sync.main.id } @@ -54,7 +54,7 @@ resource "azurerm_storage_sync_group" "department_sync" { # Cloud Endpoints (Azure Files) resource "azurerm_storage_sync_cloud_endpoint" "department_cloud" { for_each = toset(["hr", "finance", "engineering", "sales", "marketing", "it"]) - + name = "${each.key}-cloud-endpoint" storage_sync_group_id = azurerm_storage_sync_group.department_sync[each.key].id file_share_name = azurerm_storage_share.department_shares[each.key].name @@ -66,14 +66,14 @@ resource "azurerm_storage_sync_cloud_endpoint" "department_cloud" { # ============================================================================= resource "azurerm_windows_virtual_machine" "source_fileserver" { - count = 2 # 2-node cluster + count = 2 # 2-node cluster name = "${var.resource_prefix}-src-fs-${count.index + 1}" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location - size = "Standard_D8s_v5" # 8 vCPU, 32GB RAM + size = "Standard_D8s_v5" # 8 vCPU, 32GB RAM admin_username = var.admin_username admin_password = var.admin_password - zone = tostring(count.index + 1) # Availability zones + zone = tostring(count.index + 1) # Availability zones network_interface_ids = [ azurerm_network_interface.source_fileserver[count.index].id @@ -107,7 +107,7 @@ resource "azurerm_managed_disk" "source_fileserver_data" { resource_group_name = azurerm_resource_group.main.name storage_account_type = "Premium_LRS" create_option = "Empty" - disk_size_gb = 4096 # 4TB per node + disk_size_gb = 4096 # 4TB per node zone = tostring(count.index + 1) tags = local.common_tags @@ -129,9 +129,9 @@ resource "azurerm_network_interface" "source_fileserver" { ip_configuration { name = "internal" - subnet_id = azurerm_subnet.domain_controllers.id + subnet_id = azurerm_subnet.services.id private_ip_address_allocation = "Static" - private_ip_address = cidrhost(azurerm_subnet.domain_controllers.address_prefixes[0], 20 + count.index) + private_ip_address = cidrhost(azurerm_subnet.services.address_prefixes[0], 20 + count.index) } tags = local.common_tags @@ -142,14 +142,14 @@ resource "azurerm_network_interface" "source_fileserver" { # ============================================================================= resource "azurerm_windows_virtual_machine" "target_fileserver" { - count = 2 # 2-node cluster + count = 2 # 2-node cluster name = "${var.resource_prefix}-tgt-fs-${count.index + 1}" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location - size = "Standard_D8s_v5" # 8 vCPU, 32GB RAM + size = "Standard_D8s_v5" # 8 vCPU, 32GB RAM admin_username = var.admin_username admin_password = var.admin_password - zone = tostring(count.index + 1) # Availability zones + zone = tostring(count.index + 1) # Availability zones network_interface_ids = [ azurerm_network_interface.target_fileserver[count.index].id @@ -183,7 +183,7 @@ resource "azurerm_managed_disk" "target_fileserver_data" { resource_group_name = azurerm_resource_group.main.name storage_account_type = "Premium_LRS" create_option = "Empty" - disk_size_gb = 4096 # 4TB per node + disk_size_gb = 4096 # 4TB per node zone = tostring(count.index + 1) tags = local.common_tags @@ -205,9 +205,9 @@ resource "azurerm_network_interface" "target_fileserver" { ip_configuration { name = "internal" - subnet_id = azurerm_subnet.domain_controllers.id + subnet_id = azurerm_subnet.services.id private_ip_address_allocation = "Static" - private_ip_address = cidrhost(azurerm_subnet.domain_controllers.address_prefixes[0], 30 + count.index) + private_ip_address = cidrhost(azurerm_subnet.services.address_prefixes[0], 30 + count.index) } tags = local.common_tags @@ -218,11 +218,11 @@ resource "azurerm_network_interface" "target_fileserver" { # ============================================================================= resource "azurerm_windows_virtual_machine" "sms_orchestrator" { - count = 2 # Redundant orchestrators + count = 2 # Redundant orchestrators name = "${var.resource_prefix}-sms-orch-${count.index + 1}" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location - size = "Standard_D4s_v5" # 4 vCPU, 16GB RAM + size = "Standard_D4s_v5" # 4 vCPU, 16GB RAM admin_username = var.admin_username admin_password = var.admin_password zone = tostring(count.index + 1) @@ -260,9 +260,9 @@ resource "azurerm_network_interface" "sms_orchestrator" { ip_configuration { name = "internal" - subnet_id = azurerm_subnet.domain_controllers.id + subnet_id = azurerm_subnet.services.id private_ip_address_allocation = "Static" - private_ip_address = cidrhost(azurerm_subnet.domain_controllers.address_prefixes[0], 40 + count.index) + private_ip_address = cidrhost(azurerm_subnet.services.address_prefixes[0], 40 + count.index) } tags = local.common_tags @@ -274,7 +274,7 @@ resource "azurerm_network_interface" "sms_orchestrator" { resource "azurerm_lb" "file_cluster" { for_each = toset(["source", "target"]) - + name = "${var.resource_prefix}-${each.key}-fs-lb" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name @@ -282,9 +282,9 @@ resource "azurerm_lb" "file_cluster" { frontend_ip_configuration { name = "FilesClusterIP" - subnet_id = azurerm_subnet.domain_controllers.id + subnet_id = azurerm_subnet.services.id private_ip_address_allocation = "Static" - private_ip_address = cidrhost(azurerm_subnet.domain_controllers.address_prefixes[0], each.key == "source" ? 25 : 35) + private_ip_address = cidrhost(azurerm_subnet.services.address_prefixes[0], each.key == "source" ? 25 : 35) } tags = local.common_tags @@ -293,7 +293,7 @@ resource "azurerm_lb" "file_cluster" { # Backend pools resource "azurerm_lb_backend_address_pool" "file_cluster" { for_each = toset(["source", "target"]) - + name = "${each.key}-fs-pool" loadbalancer_id = azurerm_lb.file_cluster[each.key].id } @@ -301,11 +301,11 @@ resource "azurerm_lb_backend_address_pool" "file_cluster" { # Health probe resource "azurerm_lb_probe" "file_cluster" { for_each = toset(["source", "target"]) - - name = "smb-health" - loadbalancer_id = azurerm_lb.file_cluster[each.key].id - protocol = "Tcp" - port = 445 + + name = "smb-health" + loadbalancer_id = azurerm_lb.file_cluster[each.key].id + protocol = "Tcp" + port = 445 interval_in_seconds = 5 number_of_probes = 2 } @@ -313,7 +313,7 @@ resource "azurerm_lb_probe" "file_cluster" { # Load balancing rule for SMB resource "azurerm_lb_rule" "file_cluster_smb" { for_each = toset(["source", "target"]) - + name = "smb-rule" loadbalancer_id = azurerm_lb.file_cluster[each.key].id protocol = "Tcp" diff --git a/terraform/azure-tier3/main.tf b/terraform/azure-tier3/main.tf index 0cb6084..f09912c 100644 --- a/terraform/azure-tier3/main.tf +++ b/terraform/azure-tier3/main.tf @@ -81,9 +81,9 @@ resource "azurerm_storage_account" "main" { # Blob containers resource "azurerm_storage_container" "containers" { - for_each = toset(var.blob_container_names) - name = each.value - storage_account_id = azurerm_storage_account.main.id + for_each = toset(var.blob_container_names) + name = each.value + storage_account_name = azurerm_storage_account.main.name } # ============================================================================= @@ -105,10 +105,10 @@ resource "azurerm_key_vault" "main" { network_acls { default_action = "Deny" bypass = "AzureServices" - + # Add specific IP ranges if needed ip_rules = var.authorized_ip_ranges - + # Allow access from AKS subnet virtual_network_subnet_ids = [ azurerm_subnet.aks.id @@ -187,7 +187,7 @@ resource "azurerm_consumption_budget_resource_group" "main" { operator = "GreaterThan" contact_emails = [ - "admin@example.com" # Update with actual email + "admin@example.com" # Update with actual email ] } } @@ -203,7 +203,7 @@ resource "azurerm_monitor_action_group" "main" { email_receiver { name = "Admin-Email" - email_address = "admin@example.com" # Update with actual email + email_address = "admin@example.com" # Update with actual email use_common_alert_schema = true } diff --git a/terraform/azure-tier3/outputs.tf b/terraform/azure-tier3/outputs.tf index 8703bf7..7663f34 100644 --- a/terraform/azure-tier3/outputs.tf +++ b/terraform/azure-tier3/outputs.tf @@ -170,16 +170,16 @@ output "aks_portal_url" { output "deployment_summary" { description = "Summary of the deployment" value = { - tier = "3 (Enterprise)" - aks_cluster = azurerm_kubernetes_cluster.main.name - kubernetes_version = azurerm_kubernetes_cluster.main.kubernetes_version - system_nodes = "${var.system_node_pool_min_count}-${var.system_node_pool_max_count}" - worker_nodes = "${var.worker_node_pool_min_count}-${var.worker_node_pool_max_count}" - region = azurerm_resource_group.main.location - resource_group = azurerm_resource_group.main.name - monitoring_enabled = var.enable_container_insights - private_cluster = var.enable_private_cluster - auto_scaling_enabled = var.enable_auto_scaling + tier = "3 (Enterprise)" + aks_cluster = azurerm_kubernetes_cluster.main.name + kubernetes_version = azurerm_kubernetes_cluster.main.kubernetes_version + system_nodes = "${var.system_node_pool_min_count}-${var.system_node_pool_max_count}" + worker_nodes = "${var.worker_node_pool_min_count}-${var.worker_node_pool_max_count}" + region = azurerm_resource_group.main.location + resource_group = azurerm_resource_group.main.name + monitoring_enabled = var.enable_container_insights + private_cluster = var.enable_private_cluster + auto_scaling_enabled = var.enable_auto_scaling } } diff --git a/terraform/azure-tier3/providers.tf b/terraform/azure-tier3/providers.tf index 2e69e64..ad911f2 100644 --- a/terraform/azure-tier3/providers.tf +++ b/terraform/azure-tier3/providers.tf @@ -9,22 +9,22 @@ terraform { source = "hashicorp/azurerm" version = "~> 3.80" } - + kubernetes = { source = "hashicorp/kubernetes" version = "~> 2.24" } - + helm = { source = "hashicorp/helm" version = "~> 2.12" } - + kubectl = { source = "gavinbunney/kubectl" version = "~> 1.14" } - + random = { source = "hashicorp/random" version = "~> 3.5" diff --git a/terraform/azure-tier3/variables.tf b/terraform/azure-tier3/variables.tf index cc12861..e9637c6 100644 --- a/terraform/azure-tier3/variables.tf +++ b/terraform/azure-tier3/variables.tf @@ -42,7 +42,7 @@ variable "kubernetes_version" { variable "system_node_pool_vm_size" { description = "VM size for system node pool" type = string - default = "Standard_D4s_v5" # 4 vCPU, 16GB RAM + default = "Standard_D4s_v5" # 4 vCPU, 16GB RAM } variable "system_node_pool_min_count" { @@ -60,7 +60,7 @@ variable "system_node_pool_max_count" { variable "worker_node_pool_vm_size" { description = "VM size for worker node pool" type = string - default = "Standard_D8s_v5" # 8 vCPU, 32GB RAM + default = "Standard_D8s_v5" # 8 vCPU, 32GB RAM } variable "worker_node_pool_min_count" { @@ -146,13 +146,13 @@ variable "storage_account_tier" { variable "storage_account_replication" { description = "Storage account replication type" type = string - default = "GRS" # Geo-redundant for enterprise + default = "GRS" # Geo-redundant for enterprise } variable "blob_container_names" { description = "Blob container names to create" type = list(string) - default = [ + default = [ "migration-artifacts", "usmt-backups", "logs", @@ -174,13 +174,13 @@ variable "enable_azure_ad_rbac" { variable "enable_private_cluster" { description = "Enable private cluster (API server not publicly accessible)" type = bool - default = false # Set to true for maximum security + default = false # Set to true for maximum security } variable "authorized_ip_ranges" { description = "Authorized IP ranges for API server access" type = list(string) - default = [] # Empty allows all; restrict in production + default = [] # Empty allows all; restrict in production } variable "key_vault_sku" { @@ -294,7 +294,7 @@ variable "deploy_target_dc" { variable "target_dc_vm_size" { description = "VM size for target domain controller" type = string - default = "Standard_B2s" # 2 vCPU, 4GB RAM + default = "Standard_B2s" # 2 vCPU, 4GB RAM } variable "source_domain_fqdn" {