Last active
May 17, 2020 13:59
-
-
Save tomudding/18edf380bb3f2c01d3af6742fcbdd58e to your computer and use it in GitHub Desktop.
Modified version of Docker Desktop's MobyLinux.ps1. This modified version allows computers on a network domain to run Docker Desktop (2.2.0.5) using Linux Containers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Manages a MobyLinux VM to run Linux Docker on Hyper-V | |
.DESCRIPTION | |
Creates/Destroys/Starts/Stops A MobyLinux VM to run Docker on Hyper-V | |
.PARAMETER VmName | |
If passed, use this name for the MobyLinux VM, otherwise 'MobyLinuxVM' | |
.PARAMETER IsoFile | |
Path to the MobyLinux ISO image, must be set for Create/ReCreate | |
.PARAMETER SwitchName | |
If passed, use this name for the Hyper-V virtual switch, | |
otherwise the VM will have no Hyper-V NIC. | |
.PARAMETER Create | |
Create a MobyLinux VM | |
.PARAMETER SwitchSubnetMaskSize | |
Switch subnet mask size (default: 24) | |
.PARAMETER SwitchSubnetAddress | |
Switch subnet address (default: 10.0.75.0) | |
.PARAMETER Memory | |
Memory allocated for the VM at start in MB (optional on Create, default: 2048 MB) | |
.PARAMETER CPUs | |
CPUs used in the VM (optional on Create, default: min(2, number of CPUs on the host)) | |
.PARAMETER PreferredIPAddress | |
Preferred IP address for the the MobyLinux virtual switch. If this IP address is not available or not within the allowed subnet range then a different IP address will be selected. | |
.PARAMETER Destroy | |
Remove a MobyLinux VM | |
.PARAMETER KeepVolume | |
if passed, will not delete the vmhd on Destroy | |
.PARAMETER Start | |
Start an existing MobyLinux VM | |
.PARAMETER Stop | |
Stop a running MobyLinux VM | |
.EXAMPLE | |
.\MobyLinux.ps1 -IsoFile .\docker-desktop.iso -Create | |
.\MobyLinux.ps1 -Start | |
#> | |
[CmdletBinding()]Param( | |
[string] $VmName = "DockerDesktopVM", | |
[string] $IsoFile = ".\docker-desktop.iso", | |
[string] $SwitchName = "", | |
[string] $VhdPathOverride = $null, | |
[long] $VhdSize = 64*1000*1000*1000, | |
[string] $confIsoFile = $null, | |
[string] $DockerIsoFile = $null, | |
[string] $KubeImageIsoFile = $null, | |
[Parameter(ParameterSetName='Create',Mandatory=$false)][switch] $Create, | |
[Parameter(ParameterSetName='Create',Mandatory=$false)][int] $CPUs = 2, | |
[Parameter(ParameterSetName='Create',Mandatory=$false)][long] $Memory = 2048, | |
[Parameter(ParameterSetName='Create',Mandatory=$false)][string] $SwitchSubnetAddress = "10.0.75.0", | |
[Parameter(ParameterSetName='Create',Mandatory=$false)][int] $SwitchSubnetMaskSize = 24, | |
[Parameter(ParameterSetName='Create',Mandatory=$false)][string] $PreferredIPAddress, | |
[Parameter(ParameterSetName='Destroy',Mandatory=$false)][switch] $Destroy, | |
[Parameter(ParameterSetName='Destroy',Mandatory=$false)][switch] $KeepVolume, | |
[Parameter(ParameterSetName='Start',Mandatory=$false)][switch] $Start, | |
[Parameter(ParameterSetName='Stop',Mandatory=$false)][switch] $Stop | |
) | |
# This makes sure the system modules can be imported | |
$envPSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath','Machine') | |
$env:PSModulePath = [System.Management.Automation.ModuleIntrinsics]::GetModulePath($envPSModulePath, $envPSModulePath, "") | |
# Make sure we stop at Errors unless otherwise explicitly specified | |
$ErrorActionPreference = "Stop" | |
$ProgressPreference = "SilentlyContinue" | |
$VerbosePreference = "SilentlyContinue" | |
# Explicitly disable Module autoloading and explicitly import the | |
# Modules this script relies on. This is not strictly necessary but | |
# good practice as it prevents arbitrary errors | |
$PSModuleAutoloadingPreference = 'None' | |
Import-Module Microsoft.PowerShell.Utility -Verbose:$false | |
Import-Module Microsoft.PowerShell.Management -Verbose:$false | |
Import-Module Hyper-V -Verbose:$false | |
Import-Module NetAdapter -Verbose:$false | |
Import-Module NetTCPIP -Verbose:$false | |
# Get correct hostname | |
if ((gwmi win32_computersystem).partofdomain -eq $true) { | |
$cname = [System.Net.DNS]::GetHostByName('').HostName | |
} else { | |
$cname = Hostname | |
} | |
$PSDefaultParameterValues["Get-VMHost:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VMNetworkAdapter:ComputerName"] = $cname | |
$PSDefaultParameterValues["Remove-VMSwitch:ComputerName"] = $cname | |
$PSDefaultParameterValues["New-VMSwitch:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VM:ComputerName"] = $cname | |
$PSDefaultParameterValues["New-VM:ComputerName"] = $cname | |
$PSDefaultParameterValues["Set-VM:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VMMemory:ComputerName"] = $cname | |
$PSDefaultParameterValues["Add-VMNetworkAdapter:ComputerName"] = $cname | |
$PSDefaultParameterValues["Remove-VMNetworkAdapter:ComputerName"] = $cname | |
$PSDefaultParameterValues["Connect-VMNetworkAdapter:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VMSwitch:ComputerName"] = $cname | |
$PSDefaultParameterValues["Remove-VMDvdDrive:ComputerName"] = $cname | |
$PSDefaultParameterValues["Add-VMDvdDrive:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VMFirmware:ComputerName"] = $cname | |
$PSDefaultParameterValues["Set-VMFirmware:ComputerName"] = $cname | |
$PSDefaultParameterValues["Set-VMComPort:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VMIntegrationService:ComputerName"] = $cname | |
$PSDefaultParameterValues["Enable-VMIntegrationService:ComputerName"] = $cname | |
$PSDefaultParameterValues["Disable-VMIntegrationService:ComputerName"] = $cname | |
$PSDefaultParameterValues["Disable-VMConsoleSupport:ComputerName"] = $cname | |
$PSDefaultParameterValues["Remove-VM:ComputerName"] = $cname | |
$PSDefaultParameterValues["Remove-VMHardDiskDrive:ComputerName"] = $cname | |
$PSDefaultParameterValues["Add-VMHardDiskDrive:ComputerName"] = $cname | |
$PSDefaultParameterValues["Start-VM:ComputerName"] = $cname | |
$PSDefaultParameterValues["Stop-VM:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VHD:ComputerName"] = $cname | |
$PSDefaultParameterValues["New-VHD:ComputerName"] = $cname | |
$PSDefaultParameterValues["Get-VMScsiController:ComputerName"] = $cname | |
$PSDefaultParameterValues["Add-VMScsiController:ComputerName"] = $cname | |
Write-Information "ComputerName successfully set to $cname" | |
function Enable-MobyLinuxRequiredService([string]$Name) { | |
$service = Get-Service $Name | |
if (-not $service) { | |
throw "$Name service not found. Ensure that the Hyper-V feature is installed." | |
} | |
elseif ($service.StartType -eq [System.ServiceProcess.ServiceStartMode]::Disabled) { | |
throw "$Name service is disabled. The service must be enabled to run Docker Desktop." | |
} | |
elseif ($service.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Stopped) { | |
Start-Service $Name | |
} | |
} | |
function New-MobyLinux { | |
<# | |
.SYNOPSIS | |
Creates a new MobyLinux virtual machine and switch | |
.DESCRIPTION | |
Creates a new MobyLinux virtual machine and switch, configuring the devices as required. | |
.PARAMETER SwitchName | |
Name of the MobyLinux virtual switch | |
.PARAMETER SubnetAddress | |
Subnet address of the MobyLinux virtual switch | |
.PARAMETER SubnetMaskSize | |
Subnet mask size of the MobyLinux virtual switch | |
.PARAMETER PreferredIPAddress | |
Preferred IP address for the the MobyLinux virtual switch. If this IP address is not available or not within the allowed subnet range then a different IP address will be selected. | |
.PARAMETER VmName | |
Name of the MobyLinux virtual machine | |
.PARAMETER Memory | |
Memory allocated for the VM at start in MB | |
.PARAMETER CPUs | |
CPUs used in the VM | |
.PARAMETER SwitchName | |
Name of the VM's virtual switch. If not supplied then no NIC and switch are created. | |
.PARAMETER VhdFile | |
Path of the VHD file | |
.PARAMETER VhdSize | |
Size of the VHD to create if does not already exist | |
.PARAMETER IsoFile | |
Path to the MobyLinux ISO image, must be set for Create/ReCreate | |
.PARAMETER ConfigIsoFile | |
Path to the MobyLinux config ISO image to be attached as a drive to the VM | |
.PARAMETER DockerIsoFile | |
Path to the MobyLinux docker ISO image to be attached as a drive to the VM | |
.PARAMETER KubeImageIsoFile | |
Path to the MobyLinux kubernetes ISO image to be attached as a drive to the VM | |
.OUTPUTS | |
PSObject. Returns the IP address of the MobyLinux virtual switch. | |
#> | |
[CmdletBinding()]param ( | |
[string] $SwitchName = '', | |
[string] $SubnetAddress = '10.0.75.0', | |
[int] $SubnetMaskSize = 24, | |
[string] $PreferredIPAddress, | |
[string] $VmName = 'DockerDesktopVM', | |
[int] $CPUs = 2, | |
[long] $Memory = 2048, | |
[string] $VhdFile, | |
[long] $VhdSize = 64*1000*1000*1000, | |
[string] $IsoFile = '.\docker-desktop.iso', | |
[string] $ConfigIsoFile, | |
[string] $DockerIsoFile, | |
[string] $KubeImageIsoFile | |
) | |
if ($SwitchName -ne "") { | |
$ip = New-MobyLinuxSwitch -Name $SwitchName -SubnetAddress $SubnetAddress -SubnetMaskSize $SubnetMaskSize -PreferredIPAddress $PreferredIPAddress | |
$output = New-Object PSObject | |
$output | Add-Member -NotePropertyName SwitchAddress -NotePropertyValue $ip | |
Write-Output $output | |
} | |
New-MobyLinuxVM -Name $VmName -SwitchName $SwitchName -CPUs $CPUs -Memory $Memory -VhdFile $VhdFile -IsoFile $IsoFile -ConfigIsoFile $ConfigIsoFile -DockerIsoFile $DockerIsoFile -KubeImageIsoFile $KubeImageIsoFile | |
} | |
function New-MobyLinuxSwitch { | |
<# | |
.SYNOPSIS | |
Creates a new MobyLinux virtual switch | |
.DESCRIPTION | |
Creates a new MobyLinux virtual switch with an IPv4 IP Address within the subnet address/mask. If the switch already exists then ensures that the switch has an IP address within the allowed range. | |
.PARAMETER Name | |
Name of the MobyLinux virtual switch | |
.PARAMETER SubnetAddress | |
Subnet address of the MobyLinux virtual switch | |
.PARAMETER SubnetMaskSize | |
Subnet mask size of the MobyLinux virtual switch | |
.PARAMETER PreferredIPAddress | |
Preferred IP address for the the MobyLinux virtual switch. If this IP address is not available or not within the allowed subnet range then a different IP address will be selected. | |
.OUTPUTS | |
System.String. Returns the IP address of the MobyLinux virtual switch. | |
.EXAMPLE | |
New-MobyLinuxSwitch | |
.EXAMPLE | |
New-MobyLinuxSwitch -Name DockerNATSwitch | |
.EXAMPLE | |
New-MobyLinuxSwitch -Name DockerNATSwitch -SubnetAddress 10.75.0.0 -SubnetMaskSize 28 -PreferredIPAddress 10.75.0.2 | |
#> | |
[CmdletBinding()]param ( | |
[string] $Name = "", | |
[string] $SubnetAddress = "10.0.75.0", | |
[int] $SubnetMaskSize = 24, | |
[string] $PreferredIPAddress | |
) | |
$vmSwitch = Hyper-V\Get-VMSwitch $Name -SwitchType Internal -ea SilentlyContinue | |
$vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $Name -ea SilentlyContinue | |
if ($vmSwitch -and $vmNetAdapter) { | |
Write-Verbose "Using existing Switch: $Name" | |
} | |
else { | |
Write-Verbose "Creating Switch: $Name..." | |
Hyper-V\Remove-VMSwitch $Name -Force -ea SilentlyContinue | |
Hyper-V\New-VMSwitch $Name -SwitchType Internal -ea SilentlyContinue | Out-Null | |
$vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $Name | |
Write-Verbose "Switch created." | |
} | |
# Make sure there are no lingering net adapters | |
$netAdapters = Get-NetAdapter | Where-Object { $_.Name.StartsWith("vEthernet ($Name)") } | |
if (($netAdapters).Length -gt 1) { | |
Write-Verbose "Disable and rename invalid NetAdapters" | |
$now = (Get-Date -Format FileDateTimeUniversal) | |
$index = 1 | |
$invalidNetAdapters = $netAdapters | Where-Object { $_.DeviceID -ne $vmNetAdapter.DeviceId } | |
foreach ($netAdapter in $invalidNetAdapters) { | |
$netAdapter ` | |
| Disable-NetAdapter -Confirm:$false -PassThru ` | |
| Rename-NetAdapter -NewName "Broken Docker Adapter ($now) ($index)" ` | |
| Out-Null | |
$index++ | |
} | |
} | |
# Make sure the Switch has the right IP address | |
$networkAdapter = Get-NetAdapter | Where-Object { $_.DeviceID -eq $vmNetAdapter.DeviceId } | |
$switchAddress = Set-SwitchIPAddress -NetAdapter $networkAdapter -SubnetAddress $SubnetAddress -SubnetMaskSize $SubnetMaskSize -PreferredIPAddress $PreferredIPAddress | |
Write-Verbose "Removing any other IP addresses from $Name switch" | |
$networkAdapter | Get-NetIPAddress | Where-Object { $_.IPAddress -ne $switchAddress } | Remove-NetIPAddress -Confirm:$false -ea SilentlyContinue | |
$networkAdapter | Disable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue | |
$networkAdapter | Enable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue | |
Write-Output $switchAddress | |
} | |
function ConvertTo-BinaryIPAddress($IPAddress) { | |
$bytes = ([IPAddress]$IPAddress).GetAddressBytes() | |
if ([BitConverter]::IsLittleEndian) { | |
[Array]::Reverse($bytes) | |
} | |
return [BitConverter]::ToUInt32($bytes, 0) | |
} | |
function Set-IPAddress($NetAdapter, $BinaryIPAddress, $MinIPAddress, $MaxIPAddress) { | |
if (-not $BinaryIPAddress -or $BinaryIPAddress -le $MinIPAddress -or $BinaryIPAddress -gt $MaxIPAddress) { | |
return $null | |
} | |
$ip = [IPAddress]::Parse($BinaryIPAddress).IPAddressToString | |
Write-Verbose "Setting docker switch IP address to $ip" | |
if ($NetAdapter | Get-NetIPAddress -IPAddress $ip -ea SilentlyContinue) { | |
return $ip | |
} | |
elseif ($existingNetIPAddress = Get-NetIPAddress -IPAddress $ip -ea SilentlyContinue) { | |
Write-Verbose "IP address '$ip' is already in use by '$($existingNetIPAddress.InterfaceAlias)'" | |
return $null | |
} | |
$NetAdapter | New-NetIPAddress -AddressFamily IPv4 -IPAddress $ip -PrefixLength $SubnetMaskSize -ea Stop | Out-Null | |
return $ip | |
} | |
function Set-SwitchIPAddress($NetAdapter, $SubnetAddress, $SubnetMaskSize, $PreferredIPAddress) { | |
$mask = [uint32]::MaxValue -shl (32 - $SubnetMaskSize) | |
$minAddress = ConvertTo-BinaryIPAddress -IPAddress $SubnetAddress | |
$maxAddress = $minAddress -bor (-bnot $mask) | |
$ipAddress = $minAddress + 1 | |
# Try using IP address from settings.json | |
if ($PreferredIPAddress) { | |
$ip = Set-IPAddress -NetAdapter $NetAdapter -BinaryIPAddress (ConvertTo-BinaryIPAddress -IPAddress $PreferredIPAddress) -MinIPAddress $minAddress -MaxIPAddress $maxAddress | |
if ($ip) { | |
return $ip | |
} | |
} | |
# Try using existing IP address | |
if ($currentIPAddress = $NetAdapter | | |
Get-NetIPAddress -AddressFamily IPv4 -ea SilentlyContinue | | |
Where-Object { $ip = ConvertTo-BinaryIPAddress -IPAddress $_.IPAddress; $ip -gt $minAddress -and $ip -le $maxAddress } | | |
Select-Object -First 1) { | |
Write-Verbose "Using existing switch IP address $($currentIPAddress.IPAddress)" | |
return $currentIPAddress.IPAddress | |
} | |
# Try all addresses in range | |
$NetAdapter | Set-NetIPInterface -Dhcp Disabled -ea SilentlyContinue | |
for ($ipAddress = $minAddress + 1; $ipAddress -le $maxAddress; $ipAddress++) { | |
$ip = Set-IPAddress -NetAdapter $NetAdapter -BinaryIPAddress $ipAddress -MinIPAddress $minAddress -MaxIPAddress $maxAddress | |
if ($ip) { | |
return $ip | |
} | |
} | |
throw "No free IP address found in range $SubnetAddress/$SubnetMaskSize" | |
} | |
function New-MobyLinuxVM { | |
<# | |
.SYNOPSIS | |
Creates a new MobyLinux virtual machine | |
.DESCRIPTION | |
Creates a new MobyLinux virtual machine, configuring the devices as required. | |
.PARAMETER Name | |
Name of the MobyLinux virtual machine | |
.PARAMETER Memory | |
Memory allocated for the VM at start in MB | |
.PARAMETER CPUs | |
CPUs used in the VM | |
.PARAMETER SwitchName | |
Name of the VM's virtual switch | |
.PARAMETER VhdFile | |
Path of the VHD file | |
.PARAMETER VhdSize | |
Size of the VHD to create if does not already exist | |
.PARAMETER IsoFile | |
Path to the MobyLinux ISO image, must be set for Create/ReCreate | |
.PARAMETER ConfigIsoFile | |
Path to the MobyLinux config ISO image to be attached as a drive to the VM | |
.PARAMETER DockerIsoFile | |
Path to the MobyLinux docker ISO image to be attached as a drive to the VM | |
.PARAMETER KubeImageIsoFile | |
Path to the MobyLinux kubernetes ISO image to be attached as a drive to the VM | |
#> | |
[CmdletBinding()]param( | |
[string] $Name = 'DockerDesktopVM', | |
[int] $CPUs = 2, | |
[long] $Memory = 2048, | |
[string] $SwitchName = "", | |
[string] $VhdFile, | |
[long] $VhdSize = 64*1000*1000*1000, | |
[string] $IsoFile = '.\docker-desktop.iso', | |
[string] $ConfigIsoFile, | |
[string] $DockerIsoFile, | |
[string] $KubeImageIsoFile | |
) | |
if (!(Test-Path $IsoFile)) { | |
throw "ISO file at $IsoFile does not exist" | |
} | |
$CPUs = ((Hyper-V\Get-VMHost).LogicalProcessorCount,$CPUs | Measure-Object -Minimum).Minimum | |
$vm = Hyper-V\Get-VM $Name -ea SilentlyContinue | |
if ($vm) { | |
if ($vm.Length -ne 1) { | |
throw "Multiple VMs exist with the name $Name. Delete invalid ones or reset Docker to factory defaults." | |
} | |
} else { | |
Write-Verbose "Creating VM $Name..." | |
$vm = Hyper-V\New-VM -Name $Name -Generation 2 -NoVHD | |
Hyper-V\Set-VM -Name $Name -AutomaticStartAction Nothing -AutomaticStopAction ShutDown -CheckpointType Disabled | |
} | |
if ($vm.Generation -ne 2) { | |
throw "VM $Name is a Generation $($vm.Generation) VM. It should be a Generation 2." | |
} | |
if ($vm.State -ne "Off") { | |
Write-Verbose "VM $Name is $($vm.State). Cannot change its settings." | |
return | |
} | |
Write-Verbose "Setting CPUs to $CPUs and Memory to $Memory MB" | |
$Memory = ($Memory,(Hyper-V\Get-VMMemory -VMName $Name).MaximumPerNumaNode | Measure-Object -Minimum).Minimum | |
Hyper-V\Set-VM -Name $Name -MemoryStartupBytes ($Memory*1024*1024) -ProcessorCount $CPUs -StaticMemory | |
Set-MobyLinuxVHDPath -VM $vm -VhdFile $VhdFile -VhdSize $VhdSize | |
Hyper-V\Remove-VMNetworkAdapter -VMName $Name | |
if ($SwitchName -ne "") { | |
$vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -VMName $Name | |
if (!$vmNetAdapter) { | |
Write-Verbose "Attach Net Adapter" | |
$vmNetAdapter = Hyper-V\Add-VMNetworkAdapter -VMName $Name -Passthru | |
} | |
Write-Verbose "Connect Internal Switch $SwitchName" | |
$vmNetAdapter | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $(Hyper-V\Get-VMSwitch $SwitchName -SwitchType Internal) | |
} | |
if ($vm.DVDDrives) { | |
Write-Verbose "Remove existing DVDs" | |
Hyper-V\Remove-VMDvdDrive $vm.DVDDrives -ea SilentlyContinue | |
} | |
Write-Verbose "Attach DVD $IsoFile" | |
Hyper-V\Add-VMDvdDrive -VMName $Name -Path $IsoFile | |
$iso = Hyper-V\Get-VMFirmware -VMName $Name | Select-Object -ExpandProperty BootOrder | Where-Object { $_.FirmwarePath.EndsWith("Scsi(0,1)") } | |
Hyper-V\Set-VMFirmware -VMName $Name -EnableSecureBoot Off -FirstBootDevice $iso | |
Hyper-V\Set-VMComPort -VMName $Name -number 1 -Path "\\.\pipe\docker$Name-com1" | |
Hyper-V\Set-VMComPort -VMName $Name -number 2 -Path "\\.\pipe\docker$Name-com2" | |
# Enable only required VM integration services | |
$intSvc = @() | |
$intSvc += "Microsoft:$($vm.Id)\84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47" # Heartbeat | |
$intSvc += "Microsoft:$($vm.Id)\9F8233AC-BE49-4C79-8EE3-E7E1985B2077" # Shutdown | |
$intSvc += "Microsoft:$($vm.Id)\2497F4DE-E9FA-4204-80E4-4B75C46419C0" # TimeSynch | |
Hyper-V\Get-VMIntegrationService -VMName $Name | ForEach-Object { | |
if ($intSvc -contains $_.Id) { | |
Hyper-V\Enable-VMIntegrationService $_ | |
Write-Verbose "Enabled $($_.Name)" | |
} else { | |
Hyper-V\Disable-VMIntegrationService $_ | |
Write-Verbose "Disabled $($_.Name)" | |
} | |
} | |
Hyper-V\Disable-VMConsoleSupport -VMName $Name | |
Write-Verbose "VM created." | |
} | |
function Remove-MobyLinux { | |
<# | |
.SYNOPSIS | |
Removes the MobyLinux virtual machine and switch | |
.DESCRIPTION | |
Removes the MobyLinux virtual machine and switch | |
.PARAMETER VmName | |
Name of the MobyLinux virtual machine | |
.PARAMETER SwitchName | |
Name of the MobyLinux virtual network switch | |
.PARAMETER KeepVolume | |
If set the VHD file is not removed with the virtual machine | |
.PARAMETER VhdFile | |
Path of the VHD file | |
.EXAMPLE | |
Stop-MobyLinuxVM | |
.EXAMPLE | |
Stop-MobyLinuxVM -Name DockerVM | |
#> | |
[CmdletBinding()]param( | |
[string] $VmName = 'DockerDesktopVM', | |
[string] $SwitchName = '', | |
[switch] $KeepVolume, | |
[string] $VhdFile | |
) | |
Stop-MobyLinuxVM -Name $VmName -SwitchName $SwitchName | |
if ($SwitchName -ne "") { | |
Remove-MobyLinuxSwitch -Name $SwitchName | |
} | |
Remove-MobyLinuxVM -Name $VmName -KeepVolume:$KeepVolume -VhdFile $VhdFile | |
} | |
function Remove-MobyLinuxSwitch { | |
<# | |
.SYNOPSIS | |
Removes the MobyLinux virtual network switch | |
.DESCRIPTION | |
Removes the MobyLinux virtual network switch and all of it's IP addresses | |
.PARAMETER Name | |
Name of the MobyLinux virtual network switch | |
.EXAMPLE | |
Remove-MobyLinuxSwitch | |
.EXAMPLE | |
Remove-MobyLinuxSwitch -Name DockerNATSwitch | |
#> | |
[CmdletBinding()] | |
param ( | |
[string] $Name = '' | |
) | |
Write-Verbose "Destroying Switch $Name..." | |
# Let's remove the IP otherwise a nasty bug makes it impossible | |
# to recreate the vswitch | |
$vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $Name -ea SilentlyContinue | |
if ($vmNetAdapter) { | |
$networkAdapter = Get-NetAdapter | Where-Object { $_.DeviceID -eq $vmNetAdapter.DeviceId } | |
$networkAdapter | Remove-NetIPAddress -Confirm:$false -ea SilentlyContinue | |
} | |
Hyper-V\Remove-VMSwitch $Name -Force -ea SilentlyContinue | |
} | |
function Remove-MobyLinuxVM { | |
<# | |
.SYNOPSIS | |
Removes the MobyLinux virtual machine | |
.DESCRIPTION | |
Removes the MobyLinux virtual network switch and VHD file unless specified not to | |
.PARAMETER Name | |
Name of the MobyLinux virtual machine | |
.PARAMETER KeepVolume | |
If set the VHD file is not removed with the virtual machine | |
.PARAMETER VhdFile | |
Path of the VHD file | |
.EXAMPLE | |
Remove-MobyLinuxVM -VhdFile ~/my-vhd.vhd | |
.EXAMPLE | |
Remove-MobyLinuxVM -Name DockerVM -KeepVolume | |
#> | |
[CmdletBinding()]param ( | |
[string] $Name = 'DockerDesktopVM', | |
[switch] $KeepVolume, | |
[string] $VhdFile | |
) | |
Write-Verbose "Removing VM $Name..." | |
Hyper-V\Remove-VM $Name -Force -ea SilentlyContinue | |
if (!$KeepVolume) { | |
Write-Verbose "Delete VHD $VhdFile" | |
Remove-Item $VhdFile -ea SilentlyContinue | |
} | |
} | |
function Set-MobyLinuxVHDPath { | |
[CmdletBinding()]param ( | |
$VM, | |
[string] $VhdFile, | |
[long] $VhdSize = 64*1000*1000*1000 | |
) | |
$vhd = Get-VHD -Path $VhdFile -ea SilentlyContinue | |
if (!$vhd) { | |
Write-Verbose "Creating dynamic VHD: $VhdFile" | |
$vhd = New-VHD -Path $VhdFile -Dynamic -SizeBytes $VhdSize -BlockSizeBytes 1MB | |
} | |
if ($vm.HardDrives.Path -ne $VhdFile) { | |
if ($vm.HardDrives) { | |
Write-Verbose "Remove existing VHDs" | |
Hyper-V\Remove-VMHardDiskDrive $vm.HardDrives -ea SilentlyContinue | |
} | |
Write-Verbose "Attach VHD $VhdFile" | |
Hyper-V\Add-VMHardDiskDrive -VMName $vm.Name -Path $VhdFile | |
} | |
} | |
function Start-MobyLinuxVM { | |
<# | |
.SYNOPSIS | |
Starts the MobyLinux virtual machine | |
.DESCRIPTION | |
Starts the MobyLinux virtual network switch, configuring the devices as required. | |
.PARAMETER Name | |
Name of the MobyLinux virtual machine | |
.PARAMETER VhdFile | |
Path of the VHD file | |
.PARAMETER VhdSize | |
Size of the VHD to create if does not already exist | |
.PARAMETER IsoFile | |
Path to the MobyLinux ISO image, must be set for Create/ReCreate | |
.PARAMETER ConfigIsoFile | |
Path to the MobyLinux config ISO image to be attached as a drive to the VM | |
.PARAMETER DockerIsoFile | |
Path to the MobyLinux docker ISO image to be attached as a drive to the VM | |
.PARAMETER KubeImageIsoFile | |
Path to the MobyLinux kubernetes ISO image to be attached as a drive to the VM | |
#> | |
[CmdletBinding()]param( | |
[string] $Name = 'DockerDesktopVM', | |
[string] $VhdFile, | |
[long] $VhdSize = 64*1000*1000*1000, | |
[string] $IsoFile = '.\docker-desktop.iso', | |
[string] $ConfigIsoFile, | |
[string] $DockerIsoFile, | |
[string] $KubeImageIsoFile | |
) | |
Write-Verbose "Starting VM $Name..." | |
$vm = Hyper-V\Get-VM $Name -ea SilentlyContinue | |
if ($vm.DVDDrives) { | |
Write-Verbose "Remove existing DVDs" | |
Hyper-V\Remove-VMDvdDrive $vm.DVDDrives -ea SilentlyContinue | |
} | |
Write-Verbose "Attach DVD $IsoFile" | |
Hyper-V\Add-VMDvdDrive -VMName $Name -ControllerNumber 0 -ControllerLocation 1 -Path $IsoFile | |
if ((Get-Item $ConfigIsoFile).length -gt 0) { | |
Write-Verbose "Attach Config ISO $ConfigIsoFile" | |
if ((Get-VMScsiController -VMName $Name).length -le 1) { | |
Add-VMScsiController -VMName $Name | |
} | |
Hyper-V\Add-VMDvdDrive -VMName $Name -ControllerNumber 1 -ControllerLocation 1 -Path $ConfigIsoFile | |
} | |
if ((Get-Item $DockerIsoFile).length -gt 0) { | |
Write-Verbose "Attach Docker ISO $DockerIsoFile" | |
if ((Get-VMScsiController -VMName $Name).length -le 2) { | |
Add-VMScsiController -VMName $Name | |
} | |
Hyper-V\Add-VMDvdDrive -VMName $Name -ControllerNumber 2 -ControllerLocation 1 -Path $DockerIsoFile | |
} | |
if (($KubeImageIsoFile) -And (Test-Path $KubeImageIsoFile)) { | |
Write-Verbose "Attach Kube images ISO $KubeImageIsoFile" | |
if ((Get-VMScsiController -VMName $Name).length -le 3) { | |
Add-VMScsiController -VMName $Name | |
} | |
Hyper-V\Add-VMDvdDrive -VMName $Name -ControllerNumber 3 -ControllerLocation 1 -Path $KubeImageIsoFile | |
} | |
Set-MobyLinuxVHDPath -VM $vm -VhdSize $VhdSize -VhdFile $VhdFile | |
$iso = Hyper-V\Get-VMFirmware -VMName $Name | Select-Object -ExpandProperty BootOrder | Where-Object { $_.FirmwarePath.EndsWith("Scsi(0,1)") } | |
Hyper-V\Set-VMFirmware -VMName $Name -EnableSecureBoot Off -BootOrder $iso | |
Hyper-V\Start-VM -VMName $Name | |
} | |
function Stop-MobyLinuxVM { | |
<# | |
.SYNOPSIS | |
Stops the MobyLinux virtual machine | |
.DESCRIPTION | |
Stops the MobyLinux virtual machine | |
.PARAMETER Name | |
Name of the MobyLinux virtual machine | |
.PARAMETER SwitchName | |
Name of the MobyLinux virtual network switch | |
.EXAMPLE | |
Stop-MobyLinuxVM | |
.EXAMPLE | |
Stop-MobyLinuxVM -Name DockerVM | |
#> | |
[CmdletBinding()]param( | |
[string] $Name = 'DockerDesktopVM', | |
[string] $SwitchName = '' | |
) | |
$vms = Hyper-V\Get-VM $Name -ea SilentlyContinue | |
if (!$vms) { | |
Write-Verbose "VM $Name does not exist" | |
return | |
} | |
# Don't use a foreach here as the collection is modified during enumeration in the rare case there is >1 VM | |
for ($i = 0; $i -lt $vms.Count; $i++) { | |
$vm = $vms[$i]; | |
Stop-VMForce -VM $vm -Name $Name -SwitchName $SwitchName | |
} | |
} | |
function Stop-VMForce { | |
[CmdletBinding()]param( | |
$VM, | |
[string] $Name = 'DockerDesktopVM', | |
[string] $SwitchName = '' | |
) | |
if ($VM.State -eq 'Off') { | |
Write-Verbose "VM $Name is stopped" | |
return | |
} | |
$code = { | |
Param($vmId) # Passing the $VM ref is not possible because it will be disposed already | |
$VM = Hyper-V\Get-VM -Id $vmId -ea SilentlyContinue | |
if (!$VM) { | |
Write-Verbose "VM with Id $vmId does not exist" | |
return | |
} | |
$shutdownService = Hyper-V\Get-VMIntegrationService -Name $Name Shutdown -ea SilentlyContinue | |
if ($shutdownService -and $shutdownService.PrimaryOperationalStatus -eq 'Ok') { | |
Write-Verbose "Shutdown VM $Name..." | |
Hyper-V\Stop-VM -Name $Name -Confirm:$false -Force -ea SilentlyContinue | |
if ($VM.State -eq 'Off') { | |
return | |
} | |
} | |
Write-Verbose "Turn Off VM $Name..." | |
Hyper-V\Stop-VM -Name $Name -Confirm:$false -TurnOff -Force -ea SilentlyContinue | |
} | |
Write-Verbose "Stopping VM $Name..." | |
$job = Start-Job -ScriptBlock $code -ArgumentList $VM.VMId.Guid | |
if (Wait-Job $job -Timeout 20) { Receive-Job $job } | |
Remove-Job -Force $job -ea SilentlyContinue | |
if ($VM.State -eq 'Off') { | |
Write-Verbose "VM $Name is stopped" | |
return | |
} | |
# If the VM cannot be stopped properly after the timeout | |
# then we have to kill the process and wait till the state changes to "Off" | |
for ($count = 1; $count -le 10; $count++) { | |
$ProcessID = (Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "Name = '$($VM.Id.Guid)'").ProcessID | |
if (!$ProcessID) { | |
Write-Verbose "VM $Name killed. Waiting for state to change" | |
for ($count = 1; $count -le 20; $count++) { | |
if ($VM.State -eq 'Off') { | |
Write-Verbose "Killed VM $Name is off" | |
Remove-MobyLinuxSwitch -Name $SwitchName | |
Remove-MobyLinuxVM -Name $Name -KeepVolume | |
return | |
} | |
Start-Sleep -Seconds 1 | |
} | |
throw "Killed VM $Name did not stop" | |
} | |
Write-Verbose "Kill VM $Name process..." | |
Stop-Process $ProcessID -Force -Confirm:$false -ea SilentlyContinue | |
Start-Sleep -Seconds 1 | |
} | |
throw "Couldn't stop VM $Name" | |
} | |
# Main entry point | |
Try { | |
Enable-MobyLinuxRequiredService -Name vmcompute | |
Enable-MobyLinuxRequiredService -Name vmms | |
Switch ($PSCmdlet.ParameterSetName) { | |
'Stop' { Stop-MobyLinuxVM -Name $VmName -SwitchName $SwitchName -Verbose } | |
'Destroy' { Remove-MobyLinux -VmName $VmName -SwitchName $SwitchName -KeepVolume:$KeepVolume -VhdFile $VhdPathOverride -Verbose } | |
'Create' { New-MobyLinux -SwitchName $SwitchName -SubnetAddress $SwitchSubnetAddress -SubnetMaskSize $SwitchSubnetMaskSize -VmName $VmName -CPUs $CPUs -Memory $Memory -VhdFile $VhdPathOverride -VhdSize $VhdSize -IsoFile $IsoFile -ConfigIsoFile $confIsoFile -DockerIsoFile $DockerIsoFile -KubeImageIsoFile $KubeImageIsoFile -PreferredIPAddress $PreferredIPAddress -Verbose } | |
'Start' { Start-MobyLinuxVM -Name $VmName -VhdFile $VhdPathOverride -VhdSize $VhdSize -IsoFile $IsoFile -ConfigIsoFile $confIsoFile -DockerIsoFile $DockerIsoFile -KubeImageIsoFile $KubeImageIsoFile -Verbose } | |
} | |
} Catch { | |
throw | |
Exit 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment