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); SshHostKeyVerifier::verify($connection, $sshHost); 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 runPsCommand(string $psCommand): string { $auth = $this->LoginAuth(); if (!$auth) { return "Login_Fail"; } $action = 'powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ' . PowerShellEscaper::encodedCommand($psCommand); try { $out = $auth->exec($action); $errOut = $auth->getStdError(); $realErr = $errOut && !str_starts_with(trim($errOut), '#< CLIXML'); return $out . ($realErr ? "\n[STDERR]\n" . $errOut : ''); } catch (\Throwable $e) { return "Error: " . $e->getMessage(); } } public function executePowerCLI(string $cmd) { return $this->CommandRSP($cmd); } private function powerCliCertificateLine(): string { return PowerCliCertificatePolicy::configurationLine(); } protected function CommandRSP(string $cmd) { $auth = $this->LoginAuth(); if (!$auth) { return "Login_Fail"; } $server = PowerShellEscaper::singleQuote($this->client->apiUri ?? ''); $user = PowerShellEscaper::singleQuote($this->client->getUsername() ?? ''); $pass = PowerShellEscaper::singleQuote($this->client->getPassword() ?? ''); $psTemplate = <<<'PS' $ProgressPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $ErrorActionPreference = 'Stop' Import-Module VMware.PowerCLI -ErrorAction Stop Connect-VIServer -Server -User -Password | Out-Null & { } | Out-String PS; $psScript = strtr($psTemplate, [ '' => $this->powerCliCertificateLine(), '' => $server, '' => $user, '' => $pass, '' => $cmd, ]); $action = 'powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ' . PowerShellEscaper::encodedCommand($psScript); try { $out = $auth->exec($action); $errOut = $auth->getStdError(); $realErr = $errOut && !str_starts_with(trim($errOut), '#< CLIXML'); return $out . ($realErr ? "\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"; } $server = PowerShellEscaper::singleQuote($this->client->apiUri ?? ''); $user = PowerShellEscaper::singleQuote($this->client->getUsername() ?? ''); $pass = PowerShellEscaper::singleQuote($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 Connect-VIServer -Server -User -Password | Out-Null & { } PS; $psScript = strtr($psTemplate, [ '' => $this->powerCliCertificateLine(), '' => $server, '' => $user, '' => $pass, '' => $cmd, ]); $action = 'powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ' . PowerShellEscaper::encodedCommand($psScript); 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 changeRDPPortWindows(string $vmName, string $guestUser, string $guestPassword, string $newPort): array|string { $psFile = "C:\\CLISave\\AssignIP\\change-rdp-port.ps1"; $args = ' -VMName ' . PowerShellEscaper::singleQuote($vmName); $args .= ' -GuestUser ' . PowerShellEscaper::singleQuote($guestUser); $args .= ' -GuestPassword ' . PowerShellEscaper::singleQuote($guestPassword); $args .= ' -NewPort ' . (int) $newPort; $cmd = '& ' . PowerShellEscaper::singleQuote($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) { \Illuminate\Support\Facades\Log::error('changeRDPPortWindows: failed to parse PowerCLI output', [ 'vm_name' => $vmName, 'raw_output' => $raw, 'json_error' => json_last_error_msg(), ]); return [ 'success' => false, 'error' => 'JSON parsing failed', ]; } 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 { $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 = '& ' . PowerShellEscaper::singleQuote($psFile) . ' -VMName ' . PowerShellEscaper::singleQuote($vmName) . ' -IP ' . PowerShellEscaper::singleQuote($ipAddress) . ' -Gateway ' . PowerShellEscaper::singleQuote($gateway) . ' -DNS ' . PowerShellEscaper::singleQuote($dns) . ' -GuestUser ' . PowerShellEscaper::singleQuote($guestUser) . ' -GuestPassword ' . PowerShellEscaper::singleQuote($guestPassword); Log::channel('Custom')->info('[changeIpVM:linux]', [ 'script' => 'changeIPLinux.ps1', 'vmware_id' => $vmwareId, ]); return (string) $this->CommandChangePort($Command, true); } $VirtualMachine = "VirtualMachine-$vmwareId"; $hn = strtolower($hostname ?: $vmName); $Command = '$vm = get-vm -Id ' . PowerShellEscaper::singleQuote($VirtualMachine) . ';' . ' $hostname = ' . PowerShellEscaper::singleQuote($hn) . ';' . ' $ipadd = ' . PowerShellEscaper::singleQuote($ipAddress) . ';' . ' $gw = ' . PowerShellEscaper::singleQuote($gateway) . ';' . ' $password = ' . PowerShellEscaper::singleQuote($guestPassword) . '; ' . ' cd /; C:\\CLISave\\changeWindows1line.ps1;'; Log::channel('Custom')->info('[changeIpVM:windows]', [ 'script' => 'changeWindows1line.ps1', 'vmware_id' => $vmwareId, ]); return (string) $this->CommandRSP($Command); } }