param( [switch]$InstallOllama, [switch]$StartOllama, [switch]$OpenFirewall, [string]$Model = "qwen3.5:4b" ) $ErrorActionPreference = "Stop" function Section([string]$Text) { Write-Host "" Write-Host "==> $Text" -ForegroundColor Cyan } function Get-LanIPv4 { $candidates = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -notlike "127.*" -and $_.IPAddress -notlike "169.254.*" -and $_.InterfaceAlias -notmatch "vEthernet|VirtualBox|VMware|Loopback|Tailscale" } | Sort-Object InterfaceMetric return ($candidates | Select-Object -First 1 -ExpandProperty IPAddress) } function Get-VMwareNatInfo { $addr = Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias "*VMware*VMnet8*" -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $addr) { return $null } $octets = $addr.IPAddress.Split('.') if ($octets.Count -ne 4) { return $null } $subnet = "$($octets[0]).$($octets[1]).$($octets[2]).0/$($addr.PrefixLength)" return [pscustomobject]@{ IP = $addr.IPAddress PrefixLength = $addr.PrefixLength Subnet = $subnet } } Section "AzerothAI temporary Windows host check" $lanIp = Get-LanIPv4 $vmnet8 = Get-VMwareNatInfo if (-not $lanIp) { Write-Warning "Could not auto-detect a LAN IPv4 address." } else { Write-Host "Windows LAN IPv4: $lanIp" } if ($vmnet8) { Write-Host "VMware NAT host IPv4: $($vmnet8.IP)" Write-Host "VMware NAT subnet: $($vmnet8.Subnet)" } $free = Get-PSDrive -Name C $freeGB = [math]::Round($free.Free / 1GB, 1) Write-Host "Free space on C:: $freeGB GB" if ($freeGB -lt 70) { Write-Warning "Less than 70 GB is free. The temporary VM may be tight once AzerothCore and client data are built." } $ram = Get-CimInstance Win32_ComputerSystem $totalGB = [math]::Round($ram.TotalPhysicalMemory / 1GB, 1) Write-Host "Installed RAM: $totalGB GB" if ($totalGB -lt 14) { Write-Warning "This PC has less memory than expected for an 8 GB VM plus Windows." } $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 Write-Host "CPU: $($cpu.Name)" Write-Host "Logical processors: $($cpu.NumberOfLogicalProcessors)" $gpu = Get-CimInstance Win32_VideoController | Select-Object -ExpandProperty Name Write-Host "GPU(s): $($gpu -join ', ')" $virt = (Get-CimInstance Win32_Processor | Select-Object -First 1).VirtualizationFirmwareEnabled Write-Host "Firmware virtualization enabled: $virt" if (-not $virt) { Write-Warning "CPU virtualization appears disabled. Enable SVM/AMD-V in BIOS before creating the VM." } $ollamaCommand = Get-Command ollama -ErrorAction SilentlyContinue $ollamaDefault = Join-Path $env:LOCALAPPDATA "Programs\Ollama\ollama.exe" $ollamaExe = if ($ollamaCommand) { $ollamaCommand.Source } elseif (Test-Path $ollamaDefault) { $ollamaDefault } else { $null } if ($InstallOllama -and -not $ollamaExe) { Section "Installing Ollama" if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { throw "winget is not available. Install Ollama manually, then rerun this script." } winget install --id Ollama.Ollama -e --accept-source-agreements --accept-package-agreements if (Test-Path $ollamaDefault) { $ollamaExe = $ollamaDefault } else { $ollamaCommand = Get-Command ollama -ErrorAction SilentlyContinue if ($ollamaCommand) { $ollamaExe = $ollamaCommand.Source } } } if ($ollamaExe) { Section "Ollama" Write-Host "Ollama detected: $ollamaExe" if ($StartOllama) { [Environment]::SetEnvironmentVariable("OLLAMA_HOST", "0.0.0.0:11434", "User") $env:OLLAMA_HOST = "0.0.0.0:11434" Get-Process -Name "ollama" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 1 Start-Process -FilePath $ollamaExe -ArgumentList "serve" -WindowStyle Hidden Start-Sleep -Seconds 3 Write-Host "Pulling $Model ..." & $ollamaExe pull $Model try { Invoke-RestMethod -Uri "http://127.0.0.1:11434/api/tags" -TimeoutSec 5 | Out-Null Write-Host "Ollama API: OK" } catch { Write-Warning "Ollama did not answer on port 11434 yet." } } else { Write-Host "Use -StartOllama when you want this script to expose Ollama to the Ubuntu VM and pull $Model." } } else { Write-Warning "Ollama is not installed. Re-run with -InstallOllama -StartOllama, or install it manually." } if ($OpenFirewall) { Section "Windows firewall" $profiles = Get-NetConnectionProfile -ErrorAction SilentlyContinue $activeProfiles = $profiles | Where-Object { $_.IPv4Connectivity -ne "Disconnected" } foreach ($profile in $activeProfiles) { Write-Host "Network profile: $($profile.InterfaceAlias) -> $($profile.NetworkCategory)" if ($profile.NetworkCategory -eq "Public") { Write-Warning "This active network is Public. The AzerothAI firewall rule is Private-only and will not accept VM traffic until the network is trusted/Private or you deliberately change the rule." } } $ruleName = "AzerothAI Ollama from VM" $existing = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue if ($existing) { Remove-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue } if ($vmnet8) { New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Action Allow -Protocol TCP -LocalPort 11434 -RemoteAddress $vmnet8.Subnet -Profile Any | Out-Null Write-Host "Opened TCP 11434 only to the VMware NAT subnet $($vmnet8.Subnet)." } else { New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Action Allow -Protocol TCP -LocalPort 11434 -Profile Private | Out-Null Write-Host "VMnet8 was not detected, so TCP 11434 was opened on Private networks only." } } Section "Temporary VM recommendation" Write-Host "Ubuntu Server 24.04 LTS" Write-Host "vCPU: 4" Write-Host "RAM: 8 GB" Write-Host "Disk: 90 GB dynamically allocated" Write-Host "Network: NAT recommended for this temporary Wi-Fi lab" Write-Host "" Write-Host "After Ubuntu boots, clone AzerothAI and run:" Write-Host " bash deploy/pc-lab/vm-bootstrap.sh" Write-Host "" if ($vmnet8) { Write-Host "The Ubuntu NAT VM should use this Windows Ollama endpoint:" Write-Host " http://$($vmnet8.IP):11434" } elseif ($lanIp) { Write-Host "Fallback Windows Ollama endpoint:" Write-Host " http://${lanIp}:11434" } Write-Host "" Write-Host "Nothing in this script installs Proxmox or modifies your Windows boot configuration."