client = $client; } private function LoginAuth() { try { // Retrieve SSH/Jumphost credentials from database $infra = InfraManagement::where('type', 'jumphost') ->where('status', true) ->first(); if (!$infra) { Log::error('SSH Login Failed: No active jumphost configuration found in InfraManagement.'); return false; } $sshHost = $infra->host; $sshPort = 22; if (isset($infra->meta_data['port'])) { $sshPort = $infra->meta_data['port']; } $sshUser = $infra->username; $sshPass = SignRSA::DecryptFunc($infra->password); $connection = new SSH2($sshHost, $sshPort); if ($connection->login($sshUser, $sshPass)) { $connection->setTimeout(0); // No timeout return $connection; } return false; } catch (Exception $error) { Log::error('SSH Login Failed: ' . $error->getMessage()); return false; } } public function loginSSH(): bool { $conn = $this->LoginAuth(); if ($conn) { Log::info("✅ Connected to vCenter via SSH successfully"); return true; } Log::error("❌ Failed to connect to vCenter via SSH"); return false; } public function runPsCommand(string $psCommand): string { $auth = $this->LoginAuth(); if (!$auth) { return "Login_Fail"; } // Action wrapper $action = 'powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass ' . '-Command "' . str_replace('"', '\"', $psCommand) . '"'; try { $out = $auth->exec($action); $errOut = $auth->getStdError(); return $out . ($errOut ? "\n[STDERR]\n" . $errOut : ''); } catch (\Throwable $e) { return "Error: " . $e->getMessage(); } } public function executePowerCLI(string $cmd) { return $this->CommandRSP($cmd); } protected function CommandRSP(string $cmd) { $auth = $this->LoginAuth(); if (!$auth) { return "Login_Fail"; } $esc = fn(string $v) => str_replace("'", "''", $v); // Get credentials from Client $server = $esc($this->client->apiUri ?? ''); $user = $esc($this->client->getUsername() ?? ''); $pass = $esc($this->client->getPassword() ?? ''); $psTemplate = <<<'PS' $ProgressPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $ErrorActionPreference = 'Stop' Import-Module VMware.PowerCLI -ErrorAction Stop Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$false | Out-Null Connect-VIServer -Server '' -User '' -Password '' | Out-Null & { } | Out-String PS; $psScript = strtr($psTemplate, [ '' => $server, '' => $user, '' => $pass, '' => $cmd, ]); $encoded = base64_encode(mb_convert_encoding($psScript, 'UTF-16LE', 'UTF-8')); $action = "powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encoded"; try { $out = $auth->exec($action); $errOut = $auth->getStdError(); return $out . ($errOut ? "\n[STDERR]\n" . $errOut : ''); } catch (\Throwable $e) { return "Error: " . $e->getMessage(); } } protected function CommandChangePort(string $cmd, bool $expectJson = false) { $auth = $this->LoginAuth(); if (!$auth) { return "Login_Fail"; } $esc = fn(string $v) => str_replace("'", "''", $v); $server = $esc($this->client->apiUri ?? ''); $user = $esc($this->client->getUsername() ?? ''); $pass = $esc($this->client->getPassword() ?? ''); $psTemplate = <<<'PS' $ProgressPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $ErrorActionPreference = 'Stop' [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 Import-Module VMware.PowerCLI -ErrorAction Stop Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$false | Out-Null Connect-VIServer -Server '' -User '' -Password '' | Out-Null & { } PS; $psScript = strtr($psTemplate, [ '' => $server, '' => $user, '' => $pass, '' => $cmd, ]); $encoded = base64_encode(mb_convert_encoding($psScript, 'UTF-16LE', 'UTF-8')); $action = "powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encoded"; try { $out = $auth->exec($action); $errOut = $auth->getStdError(); if ($expectJson) { $trimmed = trim($out); if (preg_match('~(\{.*\}|\[.*\])\s*$~s', $trimmed, $m)) { return $m[1]; } return $trimmed; } return $out . ($errOut ? "\n[STDERR]\n" . $errOut : ''); } catch (\Throwable $e) { return "Error: " . $e->getMessage(); } } public function resetGuestPasswordViaPs1(string $vmName, string $guestUser, string $guestPassword, string $newPassword, string $targetUser = 'Administrator'): array|string { $psFile = "C:\\CLISave\\AssignIP\\SetGuestPwd.ps1"; $q = fn(string $v) => str_replace("'", "''", $v); $args = " -VMName '" . $q($vmName) . "'"; $args .= " -GuestUser '" . $q($guestUser) . "'"; $args .= " -GuestPassword '" . $q($guestPassword) . "'"; $args .= " -TargetUser '" . $q($targetUser) . "'"; $args .= " -NewPassword '" . $q($newPassword) . "'"; $cmd = "& '$psFile'$args | Out-String"; $raw = $this->CommandRSP($cmd); if ($raw === 'Login_Fail' || $raw === false || $raw === null) { return "Login_Fail"; } $trim = trim($raw); $data = json_decode($trim, true); if (json_last_error() !== JSON_ERROR_NONE) { if (preg_match('/(\{.*\}|\[.*\])/s', $trim, $m)) { $data = json_decode($m[1], true); } } return $data ?: $raw; } public function changeRDPPortWindows(string $vmName, string $guestUser, string $guestPassword, string $newPort): array|string { $psFile = "C:\\CLISave\\AssignIP\\change-rdp-port.ps1"; $q = fn(string $v) => str_replace("'", "''", $v); $args = " -VMName '" . $q($vmName) . "'"; $args .= " -GuestUser '" . $q($guestUser) . "'"; $args .= " -GuestPassword '" . $q($guestPassword) . "'"; $args .= " -NewPort " . (int) $newPort; $cmd = "& '$psFile'$args"; $raw = $this->CommandChangePort($cmd, true); if ($raw === 'Login_Fail' || $raw === false || $raw === null) { return "Login_Fail"; } $raw = trim($raw); if (!preg_match('~(\{(?:[^{}]|(?1))*\}|\[(?:[^\[\]]|(?0))*\])\s*$~s', $raw, $m)) { if (preg_match('~(\{.*\}|\[.*\])\s*$~s', $raw, $m)) { $raw = $m[1]; } } else { $raw = $m[1]; } $data = json_decode($raw, true); if (json_last_error() !== JSON_ERROR_NONE) { return [ 'success' => false, 'error' => 'JSON parsing failed', 'raw_output' => $raw, 'json_error' => json_last_error_msg() ]; } if (isset($data[0])) { $data = $data[0]; } return $data; } public function changeIpVM( string $vmName, string $vmwareId, string $ipAddress, string $gateway, string $osType, string $guestUser = '', string $guestPassword = '', string $dns = '8.8.8.8', string $hostname = '' ): string { $q = fn(string $v) => str_replace("'", "''", $v); $isLinux = str_contains(strtolower($osType), 'linux') || str_contains(strtolower($osType), 'ubuntu') || str_contains(strtolower($osType), 'centos') || str_contains(strtolower($osType), 'rocky'); if ($isLinux) { $psFile = "C:\\CLISave\\changeIPLinux.ps1"; $Command = "& '{$psFile}'" . " -VMName '" . $q($vmName) . "'" . " -IP '" . $q($ipAddress) . "'" . " -Gateway '" . $q($gateway) . "'" . " -DNS '" . $q($dns) . "'" . " -GuestUser '" . $q($guestUser) . "'" . " -GuestPassword '" . $q($guestPassword) . "'"; Log::channel('Custom')->info('[changeIpVM:linux] ' . $Command); return (string) $this->CommandChangePort($Command, true); } $VirtualMachine = "VirtualMachine-$vmwareId"; $hn = strtolower($hostname ?: $vmName); $Command = "\$vm = get-vm -Id '{$VirtualMachine}';" . " \$hostname = '{$q($hn)}';" . " \$ipadd = '{$q($ipAddress)}';" . " \$gw = '{$q($gateway)}';" . " \$password = \"'{$q($guestPassword)}'\"; " . " cd /; C:\\CLISave\\changeWindows1line.ps1;"; Log::channel('Custom')->info('[changeIpVM:windows] ' . $Command); return (string) $this->CommandRSP($Command); } public function getWindowsRdpPort(string $vmwareId, string $guestUser, string $guestPassword): ?int { $ps = "(Get-ItemProperty 'HKLM:\\System\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp' -Name PortNumber).PortNumber"; $cmd = "Invoke-VMScript -VM '$vmwareId' " . "-ScriptText \"$ps\" " . "-ScriptType Powershell " . "-GuestUser '$guestUser' " . "-GuestPassword '$guestPassword'"; $output = $this->CommandRSP($cmd); if (!$output || str_contains($output, 'Login_Fail')) { return null; } if (preg_match('/\b(\d{2,5})\b/', $output, $m)) { return (int) $m[1]; } return null; } }