# 如何透過 PowerShell 快速建立 Hyper-V 伺服器中的虛擬機器

我現在已經很少在公司內部部署測試機的 VM 了，我通常都直接在 Azure 上面部署測試機，用完就砍掉，透過 Azure CLI 基本上兩分鐘就可以搞定一台，非常方便。不過，我們有個專案的測試機所需的負載比較大，開在雲端比較燒錢，所以還是在內部部署比較划算，但設定的步驟都快忘光了。這篇文章我打算記錄一下這個過程，提供日後參考之用。

![Hyper-V Manager](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/7780a1bc00a5348f8f03-178480793-d69115ac-3d01-447d-ba79-d4a3984eb887.png)

### 管理 VM 常用的 Cmdlets

1.  建立 VM
    
    ```powershell
    $VMName = 'Test1'
    $SWName = 'HV-MNA-LAN #01'
    $VMPath = 'D:\Hyper-V'
    $GiB = 1024*1024*1024
    
    # 儲存建立的 VM 物件，可以方便後續進行設定
    $VM = New-VM -Name $VMName -SwitchName $SWName -Path $VMPath `
      -NewVHDPath "${VMPath}\${VMName}\Virtual Hard Disks\${VMName}.vhdx" `
      -MemoryStartupBytes ([int64]8*$GiB) -Generation 1 -NewVHDSizeBytes ([int64]127*$GiB)
    ```
    
    取得 VM 物件
    
    ```powershell
    $VM = Get-VM $VMName
    ```
    
    > 注意: `Get-VM` 支援 [Wildcards](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_wildcards) (萬用支援)，所以針對一些特殊字元比較敏感，使用時要特別注意，必要時要用到 `` ` `` 跳脫字元。
    
    取得所有虛擬機名稱包含「專案測試機」字樣的 VM
    
    ```powershell
    $VM = Get-VM '*專案測試機*'
    ```
    
    變更 VM 名稱
    
    ```powershell
    $VM | Set-VM -NewVMName '[專案測試機] Test1'
    ```
    
2.  設定 CPU 核心數
    
    ```powershell
    $VM | Set-VMProcessor -Count 2
    ```
    
3.  設定 CPU 支援巢狀虛擬化 (Nested Virtualization)
    
    ```powershell
    $VM | Set-VMProcessor -ExposeVirtualizationExtensions $true
    ```
    
4.  設定動態記憶體配置 (Dynamic Memory Allocation)
    
    ```powershell
    $VM | Set-VMMemory -DynamicMemoryEnabled $true
    ```
    
5.  設定 DVD 光碟機掛載檔
    
    ```powershell
    $VM | Get-VMDvdDrive | Set-VMDvdDrive -Path "E:\ISOs\en_windows_server_2016_updated_feb_2018_x64_dvd_11636692.iso"
    ```
    
6.  啟動 VM
    
    ```powershell
    $VM | Start-VM
    ```
    
7.  關閉 VM
    
    ```powershell
    $VM | Stop-VM -Force
    ```
    
8.  刪除 VM
    
    ```powershell
    $VM | Remove-VM -Force
    ```
    

### 快速開啟 VMC 連線 (`vmconnect.exe`)

我從 [Connecting to Hyper-V virtual machines with PowerShell](https://powershellmagazine.com/2012/10/11/connecting-to-hyper-v-virtual-machines-with-powershell/) 發現有人寫了一個相當方便的 Function 可以快速開啟 VMC 連接 VM，我就直接沿用了！

```powershell
function Connect-VM {
    [CmdletBinding(DefaultParameterSetName = 'name')]

    param(
        [Parameter(ParameterSetName = 'name')]
        [Alias('cn')]
        [System.String[]]$ComputerName = $env:COMPUTERNAME,
        [Parameter(Position = 0,
            Mandatory, ValueFromPipelineByPropertyName,
            ValueFromPipeline, ParameterSetName = 'name')]
        [Alias('VMName')]
        [System.String]$Name,

        [Parameter(Position = 0,
            Mandatory, ValueFromPipelineByPropertyName,
            ValueFromPipeline, ParameterSetName = 'id')]
        [Alias('VMId', 'Guid')]
        [System.Guid]$Id,

        [Parameter(Position = 0, Mandatory,
            ValueFromPipeline, ParameterSetName = 'inputObject')]
        [Microsoft.HyperV.PowerShell.VirtualMachine]$InputObject,

        [switch]$StartVM
    )

    begin {
        Write-Verbose "Initializing InstanceCount, InstanceCount = 0"
        $InstanceCount = 0
    }

    process {
        try {
            foreach ($computer in $ComputerName) {
                Write-Verbose "ParameterSetName is '$($PSCmdlet.ParameterSetName)'"
                if ($PSCmdlet.ParameterSetName -eq 'name') {
                    # Get the VM by Id if Name can convert to a guid
                    if ($Name -as [guid]) {
                        Write-Verbose "Incoming value can cast to guid"
                        $vm = Get-VM -Id $Name -ErrorAction SilentlyContinue
                    }
                    else {
                        $vm = Get-VM -Name $Name -ErrorAction SilentlyContinue
                    }
                }
                elseif ($PSCmdlet.ParameterSetName -eq 'id') {
                    $vm = Get-VM -Id $Id -ErrorAction SilentlyContinue
                }
                else {
                    $vm = $InputObject
                }

                if ($vm) {
                    Write-Verbose "Executing 'vmconnect.exe $computer $($vm.Name) -G $($vm.Id) -C $InstanceCount'"
                    vmconnect.exe $computer $vm.Name -G $vm.Id -C $InstanceCount
                }
                else {
                    Write-Verbose "Cannot find vm: '$Name'"
                }

                if ($StartVM -and $vm) {
                    if ($vm.State -eq 'off') {
                        Write-Verbose "StartVM was specified and VM state is 'off'. Starting VM '$($vm.Name)'"
                        Start-VM -VM $vm
                    }
                    else {
                        Write-Verbose "Starting VM '$($vm.Name)'. Skipping, VM is not not in 'off' state."
                    }
                }

                $InstanceCount += 1
                Write-Verbose "InstanceCount = $InstanceCount"
            }
        }
        catch {
            Write-Error $_
        }
    }
}
```

基本的使用方式大概有以下兩種：

1.  連接 VM
    
    ```powershell
    $VM | Connect-VM
    ```
    
2.  連接 VM 並自動啟動
    
    ```powershell
    $VM | Connect-VM -Start
    ```
    

### 常用的 VM 組合技

1.  快速建立 Windows Server 2022 虛擬機器
    
    ```powershell
    $VMName = 'Test1'
    $SWName = 'HV-MNA-LAN #01'
    $VMPath = 'D:\Hyper-V'
    $ISOPath = 'E:\ISOs\en-us_windows_server_2022_updated_june_2022_x64_dvd_ac918027.iso'
    
    $GiB = 1024*1024*1024
    
    # 儲存建立的 VM 物件，可以方便後續進行設定
    $VM = New-VM -Name $VMName -SwitchName $SWName -Path $VMPath `
      -NewVHDPath "${VMPath}\${VMName}\Virtual Hard Disks\${VMName}.vhdx" `
      -MemoryStartupBytes ([int64]8*$GiB) -Generation 1 -NewVHDSizeBytes ([int64]127*$GiB)
    $VM | Set-VMProcessor -Count 2
    $VM | Set-VMProcessor -ExposeVirtualizationExtensions $true
    $VM | Set-VMMemory -DynamicMemoryEnabled $true
    $VM | Get-VMDvdDrive | Set-VMDvdDrive -Path $ISOPath
    $VM | Connect-VM -Start
    ```
    
2.  快速建立 Ubuntu Server 22.04 LTS 虛擬機器
    
    ```powershell
    $VMName = 'Test1'
    $SWName = 'HV-MNA-LAN #01'
    $VMPath = 'D:\Hyper-V'
    $ISOPath = 'E:\ISOs\ubuntu-22.04-live-server-amd64.iso'
    
    $GiB = 1024*1024*1024
    
    # 儲存建立的 VM 物件，可以方便後續進行設定
    $VM = New-VM -Name $VMName -SwitchName $SWName -Path $VMPath `
      -NewVHDPath "${VMPath}\${VMName}\Virtual Hard Disks\${VMName}.vhdx" `
      -MemoryStartupBytes ([int64]8*$GiB) -Generation 1 -NewVHDSizeBytes ([int64]127*$GiB)
    $VM | Set-VMProcessor -Count 2
    $VM | Set-VMProcessor -ExposeVirtualizationExtensions $true
    $VM | Set-VMMemory -DynamicMemoryEnabled $true
    $VM | Get-VMDvdDrive | Set-VMDvdDrive -Path $ISOPath
    $VM | Start-VM
    $VM | Connect-VM -Start
    ```
    

### 相關連結

-   [在巢狀虛擬化的虛擬機器中執行 Hyper-V](https://learn.microsoft.com/zh-tw/virtualization/hyper-v-on-windows/user-guide/nested-virtualization)
-   [Connecting to Hyper-V virtual machines with PowerShell](https://powershellmagazine.com/2012/10/11/connecting-to-hyper-v-virtual-machines-with-powershell/)
-   [PowerShell and Hyper-V: Using ISOs and Arrays](https://www.altaro.com/hyper-v/powershell-hyper-v-using-isos-and-arrays/)
