/////////////////////////////////////////////////////////////////////////////// // // ManageVirtualMachinesCmdlet.cs // // Copyright 2018-2020, Josh Moyer . All rights reserved. // using NDDN; using Microsoft.PowerShell; using System; using System.Collections.Generic; using System.IO; using System.Diagnostics; using System.Management.Automation; using static System.Management.Automation.Cmdlet; namespace NDDN.VirtualMachines { [Cmdlet(VerbsCommon.Get, "VMs")] public sealed class Get_VMs : VirtualMachineCmdlet { protected override void ProcessRecord() { int i, n; Process Process = new Process(); VmrunInvocationProfile VMRunInvocationProfile = new VmrunInvocationProfile(VMRunExe, VMXType.Workstation, VMXOperation.List, null, true); VirtualMachine[] VMs; InvokeCmd(Process, VMRunInvocationProfile); n = int.Parse(Process.StandardOutput.ReadLine().Split(':')[1].Trim()); VMs = new VirtualMachine[n]; for (i = 0; i < n; i++) { WriteObject(new VirtualMachine(Process.StandardOutput.ReadLine())); } } } /* [Cmdlet(VerbsData.Backup, "VM", SupportsShouldProcess = true)] public sealed class Backup_VM : VirtualMachineCmdlet { [Parameter( Position = 1, ValueFromPipelineByPropertyName = true)] public Boolean Drive; [Parameter( Position = 1, ValueFromPipelineByPropertyName = true)] public Boolean Path; private Boolean WasVMRunning; // Dangerous assumption: all VMs files will be contained in the same directory. // how to manage how much to back up? protected override void ProcessRecord() { //if (ShouldProcess(Path.GetFileNameWithoutExtension(this.VirtualMachine.Specification))) { //WasVMRunning = InvokeCmd(Process, new VmrunInvocationProfile()) // copy // resume if was running } } } */ [Cmdlet(VerbsData.Update, "VM")] public sealed class Update_VM : VirtualMachineCmdlet { protected override void ProcessRecord() { WriteDebug("Checking if " + Path.GetFileNameWithoutExtension(this.VirtualMachine.Specification) + " is running..."); String OutputLine; Process Process = new Process(); InvokeCmd(Process, new VmrunInvocationProfile(VMRunExe, VMXType.Workstation, VMXOperation.List, this.VirtualMachine.Specification, true)); while (!Process.StandardOutput.EndOfStream) { OutputLine = Process.StandardOutput.ReadLine(); WriteDebug("Found " + OutputLine + "..."); if (OutputLine == this.VirtualMachine.Specification) { this.VirtualMachine.State = VirtualMachineState.Running; WriteDebug(this.VirtualMachine.Specification + ": " + this.VirtualMachine.State); return; } } this.VirtualMachine.State = VirtualMachineState.NotRunning; WriteDebug(this.VirtualMachine.Specification + ": " + this.VirtualMachine.State); } } [Cmdlet("Pause", "VM", SupportsShouldProcess = true)] public sealed class Pause_VM : VirtualMachineCmdlet { protected override void ProcessRecord() { WriteDebug("PauseVM()"); if (ShouldProcess(Path.GetFileNameWithoutExtension(this.VirtualMachine.Specification))) { Process Process = new Process(); VmrunInvocationProfile VMRunInvocationProfile = new VmrunInvocationProfile(); VMRunInvocationProfile.VMRunPath = VMRunExe; VMRunInvocationProfile.VMXType = VMXType.Workstation; VMRunInvocationProfile.VMXOperation = VMXOperation.Pause; VMRunInvocationProfile.VMX = this.VirtualMachine.Specification; VMRunInvocationProfile.Sync = true; WriteDebug("Pausing ..."); InvokeCmd(Process, VMRunInvocationProfile); WriteDebug("Process extied with " + Process.ExitCode.ToString()); } } } [Cmdlet(VerbsLifecycle.Start, "VM", SupportsShouldProcess = true)] public sealed class Start_VM : VirtualMachineCmdlet { [Parameter( Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public Byte Interactive; protected override void ProcessRecord() { WriteDebug("StartVM()"); if (ShouldProcess(Path.GetFileNameWithoutExtension(this.VirtualMachine.Specification))) { Process Process = new Process(); VmrunInvocationProfile VMRunInvocationProfile = new VmrunInvocationProfile(); switch (Interactive) { case 0: VMRunInvocationProfile.VMRunPath = VMRunExe; VMRunInvocationProfile.VMXType = VMXType.Workstation; VMRunInvocationProfile.Sync = true; goto default; case 1: VMRunInvocationProfile.VMXType = VMXType.WorkstationInteractive; goto case 255; case 2: VMRunInvocationProfile.VMXType = VMXType.WorkstationFullscreen; goto case 255; case 255: VMRunInvocationProfile.VMRunPath = VMwareExe; VMRunInvocationProfile.Sync = false; goto default; default: VMRunInvocationProfile.VMXOperation = VMXOperation.Start; break; } VMRunInvocationProfile.VMX = this.VirtualMachine.Specification; InvokeCmd(Process, VMRunInvocationProfile); } } } [Cmdlet(VerbsLifecycle.Suspend, "VM", SupportsShouldProcess = true)] public sealed class Suspend_VM : VirtualMachineCmdlet { protected override void ProcessRecord() { WriteDebug("SuspendVM()"); if (ShouldProcess(Path.GetFileNameWithoutExtension(this.VirtualMachine.Specification))) { Process Process = new Process(); VmrunInvocationProfile VMRunInvocationProfile = new VmrunInvocationProfile(); VMRunInvocationProfile.VMRunPath = VMRunExe; VMRunInvocationProfile.VMXType = VMXType.Workstation; VMRunInvocationProfile.VMXOperation = VMXOperation.Suspend; VMRunInvocationProfile.VMX = this.VirtualMachine.Specification; VMRunInvocationProfile.VMXOperationWeight = VMXOperationWeight.Hard; VMRunInvocationProfile.Sync = true; WriteDebug("Suspending ..."); InvokeCmd(Process, VMRunInvocationProfile); WriteDebug("Process extied with " + Process.ExitCode.ToString()); } } } [Cmdlet("Unpause", "VM", SupportsShouldProcess = true)] public sealed class Unpause_VM : VirtualMachineCmdlet { protected override void ProcessRecord() { WriteDebug("UnpauseVM()"); if (ShouldProcess(Path.GetFileNameWithoutExtension(this.VirtualMachine.Specification))) { Process Process = new Process(); VmrunInvocationProfile VMRunInvocationProfile = new VmrunInvocationProfile(); VMRunInvocationProfile.VMRunPath = VMRunExe; VMRunInvocationProfile.VMXType = VMXType.Workstation; VMRunInvocationProfile.VMXOperation = VMXOperation.Unpause; VMRunInvocationProfile.VMX = this.VirtualMachine.Specification; VMRunInvocationProfile.Sync = true; WriteDebug("Unpausing ..."); InvokeCmd(Process, VMRunInvocationProfile); WriteDebug("Process extied with " + Process.ExitCode.ToString()); } } } public class VirtualMachineCmdlet : Cmdlet { [Parameter( ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0, Mandatory = true)] [ValidateNotNullOrEmpty] public VirtualMachine VirtualMachine { get { return this.virtualMachine; } set { this.virtualMachine = value; } } private VirtualMachine virtualMachine; public VirtualMachineCmdlet() { WriteDebug("VirtualMachineCmdlet()"); this.VMwarePath = Environment.GetEnvironmentVariable("VMwarePath"); WriteDebug("$Env:VMwarePath = " + this.VMwarePath); } internal String VMRunExe; internal String VMwareExe; [Parameter( ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] public String VMwarePath { get { return vmwarePath; } set { WriteDebug("setting VmWarePath to " + value + "..."); vmwarePath = value; if (vmwarePath == null || vmwarePath == "" || vmwarePath == "NDDN.VirtualMachines.VirtualMachine") vmwarePath = @"C:\Program Files (x86)\VMware\VMware Workstation"; WriteDebug("vmwarePath"); if (!Directory.Exists(vmwarePath)) ThrowTerminatingError(new ErrorRecord(new FileNotFoundException("VMwarePath"), "ERROR-VMwarePath", ErrorCategory.ObjectNotFound, this)); WriteDebug("VMRunExe"); VMRunExe = vmwarePath + "\\vmrun.exe"; if (!File.Exists(VMRunExe)) ThrowTerminatingError(new ErrorRecord(new FileNotFoundException("VMRunExe"), "ERROR-VMRunExe", ErrorCategory.ObjectNotFound, this)); WriteDebug("VMwareExe"); VMwareExe = vmwarePath + "\\vmware.exe"; if (!File.Exists(VMwareExe)) ThrowTerminatingError(new ErrorRecord(new FileNotFoundException("VMwareExe"), "ERROR-VMwareExe", ErrorCategory.ObjectNotFound, this)); } } internal String vmwarePath; internal Process InvokeCmd(Process Process, VmrunInvocationProfile VMRunInvocationProfile) { // TODO: Check for admin, if necessary Process.StartInfo.CreateNoWindow = true; Process.StartInfo.FileName = VMRunInvocationProfile.VMRunPath; WriteDebug("Will run " + Process.StartInfo.FileName); switch (VMRunInvocationProfile.VMXType) { case VMXType.Workstation: Process.StartInfo.Arguments += "-t ws "; Process.StartInfo.Arguments += VMRunInvocationProfile.VMXOperation.ToString() + " \"" + VMRunInvocationProfile.VMX + "\" "; if (VMRunInvocationProfile.VMXOperation.ToString().ToLower() == "suspend") { switch (VMRunInvocationProfile.VMXOperationWeight) { case VMXOperationWeight.Hard: Process.StartInfo.Arguments += "hard "; break; case VMXOperationWeight.Soft: Process.StartInfo.Arguments += "soft "; break; } } if (VMRunInvocationProfile.VMXOperation.ToString().ToLower() == "start") { switch (VMRunInvocationProfile.VMXGUI) { case true: Process.StartInfo.Arguments += "gui "; break; case false: Process.StartInfo.Arguments += "nogui "; break; } } break; case VMXType.WorkstationInteractive: Process.StartInfo.Arguments += "-n -x"; Process.StartInfo.Arguments += " \"" + VMRunInvocationProfile.VMX + "\" "; break; case VMXType.WorkstationFullscreen: Process.StartInfo.Arguments += "-X -n"; Process.StartInfo.Arguments += " \"" + VMRunInvocationProfile.VMX + "\" "; break; default: throw new Exception(); } WriteDebug("With args " + Process.StartInfo.Arguments); Process.StartInfo.RedirectStandardOutput = true; Process.StartInfo.UseShellExecute = false; WriteDebug("Starting external process..."); //TODO: catch exceptions Process.Start(); if (VMRunInvocationProfile.Sync) { Process.WaitForExit(); WriteDebug("Process extied with " + Process.ExitCode.ToString()); } return Process; } } internal class VmrunInvocationProfile { public String VMRunPath { get { return vmRunPath; } set { vmRunPath = value; } } public String VMX { get { return vmx; } set { vmx = value; } } public Boolean VMXGUI { get { return vmxgui; } set { vmxgui = value; } } public VMXType VMXType { get { return vmxType; } set { vmxType = value; } } public VMXOperation VMXOperation { get { return vmxOperation; } set { vmxOperation = value; } } public VMXOperationWeight VMXOperationWeight { get { return vmxOperationWeight; } set { vmxOperationWeight = value; } } public Boolean Sync { get { return sync; } set { sync = value; } } private String vmRunPath; private String vmx; private Boolean vmxgui; private VMXOperation vmxOperation; private VMXOperationWeight vmxOperationWeight; private VMXType vmxType; private Boolean sync; public VmrunInvocationProfile(String VMRunPath, VMXType VMXType, VMXOperation VMXOperation, String VMX, VMXOperationWeight VMXOperationWeight, Boolean Sync) { this.vmRunPath = VMRunPath; this.vmxType = VMXType; this.vmxOperation = VMXOperation; this.vmx = VMX; this.vmxOperationWeight = VMXOperationWeight; this.sync = Sync; } public VmrunInvocationProfile(String VMRunPath, VMXType VMXType, VMXOperation VMXOperation, String VMX, Boolean VMXGUI, Boolean Sync) { this.vmRunPath = VMRunPath; this.vmxType = VMXType; this.vmxOperation = VMXOperation; this.vmx = VMX; this.vmxOperationWeight = VMXOperationWeight; this.sync = Sync; } public VmrunInvocationProfile(String VMRunPath, VMXType VMXType, VMXOperation VMXOperation, String VMX, Boolean Sync) { this.vmRunPath = VMRunPath; this.vmxType = VMXType; this.vmxOperation = VMXOperation; this.vmx = VMX; this.vmxOperationWeight = VMXOperationWeight.Default; this.sync = Sync; } public VmrunInvocationProfile() { } } internal enum VMXType { Workstation, WorkstationInteractive, WorkstationFullscreen } internal enum VMXOperation { List, Start, Suspend, Pause, Unpause } internal enum VMXOperationWeight { Default, Hard, Soft } }