param( [string]$VCenter, [string]$VCUser, [string]$VCPass, [string]$VMName, [string]$IP, [int]$MaskBits, [string]$Gateway, [string]$DNS, [string]$GuestUser, [string]$GuestPassword ) try { Import-Module VMware.PowerCLI -ErrorAction Stop Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-Null Connect-VIServer -Server $VCenter -User $VCUser -Password $VCPass | Out-Null $vm = Get-VM -Name $VMName -ErrorAction Stop if ($vm.PowerState -ne 'PoweredOn') { throw "VM is powered off" } if ($vm.ExtensionData.Guest.ToolsStatus -notin @('toolsOk','toolsOld')) { throw "VMware Tools not ready" } $sec = ConvertTo-SecureString $GuestPassword -AsPlainText -Force $cred = New-Object pscredential($GuestUser, $sec) # ✅ Detect interface chuẩn (chỉ lấy 1 dòng) $detectScript = "ip route | awk '/default/ {print \$5; exit}'" $ifaceRes = Invoke-VMScript -VM $vm -ScriptText $detectScript -GuestCredential $cred -ScriptType Bash $iface = ($ifaceRes.ScriptOutput.Trim() -split "`n")[0].Trim() if ([string]::IsNullOrWhiteSpace($iface)) { throw "Cannot detect network interface" } # ✅ CHANGE IP (clean) $script = @" ip addr flush dev $iface ip addr add $IP/$MaskBits dev $iface ip route replace default via $Gateway echo "nameserver $DNS" > /etc/resolv.conf "@ Invoke-VMScript -VM $vm -ScriptText $script -GuestCredential $cred -ScriptType Bash | Out-Null Start-Sleep -Seconds 2 # ✅ VERIFY chỉ lấy IP (không dính text rác) $verifyScript = "hostname -I | awk '{print \$1}'" $verifyRes = Invoke-VMScript -VM $vm -ScriptText $verifyScript -GuestCredential $cred -ScriptType Bash $actualIp = ($verifyRes.ScriptOutput.Trim() -split "`n")[0].Trim() if ($actualIp -eq $IP) { $status = "ok" $message = "IP updated successfully" } else { $status = "error" $message = "IP mismatch: $actualIp" } Disconnect-VIServer -Confirm:$false | Out-Null [pscustomobject]@{ status = $status ip = $actualIp iface = $iface message = $message } | ConvertTo-Json -Depth 3 } catch { Disconnect-VIServer -Confirm:$false | Out-Null [pscustomobject]@{ status = "error" message = $_.Exception.Message } | ConvertTo-Json -Depth 3 }