Public/Power/Start-VmIfStopped.ps1
|
# --------------------------------------------------------------------------- # Start-VmIfStopped # Idempotently brings a Hyper-V VM to the Running state. Returns a result # object describing the entry state and the action taken so callers can # log, audit, or branch on the transition without re-querying Hyper-V. # # The state machine is intentionally exhaustive: each terminal state has # its own arm, and intermediate states (Paused / Stopping / Starting / # Saving) throw with a named state rather than silently waiting or # silently no-op'ing. A future Hyper-V state we have not considered # surfaces through the explicit "other" arm so a new state never goes # unnoticed. # # Compose with Wait-VmSshReady to bring a VM up and gate post-boot SSH # work on sshd actually accepting connections. # --------------------------------------------------------------------------- function Start-VmIfStopped { [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $VmName ) # Fail fast with an actionable install hint before any Hyper-V cmdlet # is touched - otherwise the first Get-VM call surfaces as the opaque # "term Get-VM is not recognized" message. Assert-HyperVModuleLoaded try { $vm = Get-VM -Name $VmName -ErrorAction Stop } catch { # The native Get-VM error wording does not always include the # requested name in a stable format; re-wrap so the operator # always sees which VM the lookup failed for. throw "Failed to look up Hyper-V VM '$VmName': $($_.Exception.Message)" } # Compare by name string rather than taking a hard # [Microsoft.HyperV.PowerShell.VMState] reference inside the module # body - keeps the module loadable on hosts without the Hyper-V # assemblies (the guard above is the only place that requires them). $stateName = [string] $vm.State switch ($stateName) { 'Running' { Write-Verbose "$VmName`: Running -> AlreadyRunning" return [PSCustomObject]@{ VmName = $VmName EntryState = 'Running' Action = 'AlreadyRunning' } } 'Off' { Start-VM -Name $VmName -ErrorAction Stop Write-Verbose "$VmName`: Off -> Started" return [PSCustomObject]@{ VmName = $VmName EntryState = 'Off' Action = 'Started' } } 'Saved' { Start-VM -Name $VmName -ErrorAction Stop Write-Verbose "$VmName`: Saved -> Resumed" return [PSCustomObject]@{ VmName = $VmName EntryState = 'Saved' Action = 'Resumed' } } { $_ -in 'Paused', 'Stopping', 'Starting', 'Saving' } { throw ("VM '$VmName' is in transient/unsupported state " + "'$stateName'; refusing to call Start-VM.") } default { # Loud failure for any state Hyper-V may add in the future # rather than a silent miss. Same actionable shape as the # transient-state arm so the operator sees the VM and the # observed state without us having to enumerate every value. throw ("VM '$VmName' is in unrecognised state '$stateName'; " + "refusing to call Start-VM.") } } } |