Public/OSDCloudVM.ps1

function New-OSDCloudVM {
    <#
    .SYNOPSIS
    Creates a Hyper-V VM for use with OSDCloud
 
    .DESCRIPTION
    Creates a Hyper-V VM for use with OSDCloud
 
    .EXAMPLE
    New-OSDCloudVM
 
    .LINK
    https://github.com/OSDeploy/OSD/tree/master/Docs
    #>


    [CmdletBinding()]
    param (
        [System.Boolean]
        $CheckpointVM,

        [ValidateSet('1','2')]
        [System.UInt16]
        $Generation,

        [ValidateRange(2, 64)]
        [System.UInt16]
        $MemoryStartupGB,

        [System.String]
        $NamePrefix,

        [ValidateRange(2, 64)]
        [System.UInt16]
        $ProcessorCount,

        [System.Boolean]
        $StartVM,

        [System.String]
        $SwitchName,

        [ValidateRange(8, 128)]
        [System.UInt16]
        $VHDSizeGB
    )

    # OSDCloudVM
    $Global:NewOSDCloudVM = Get-OSDCloudVMSettings

    # Update Configuration from Parameters
    if ($PSBoundParameters.ContainsKey('CheckpointVM')) {$Global:NewOSDCloudVM.CheckpointVM = $CheckpointVM}
    if ($PSBoundParameters.ContainsKey('Generation')) {$Global:NewOSDCloudVM.Generation = $Generation}
    if ($PSBoundParameters.ContainsKey('MemoryStartupGB')) {$Global:NewOSDCloudVM.MemoryStartupGB = $MemoryStartupGB}
    if ($PSBoundParameters.ContainsKey('NamePrefix')) {$Global:NewOSDCloudVM.NamePrefix = $NamePrefix}
    if ($PSBoundParameters.ContainsKey('ProcessorCount')) {$Global:NewOSDCloudVM.ProcessorCount = $ProcessorCount}
    if ($PSBoundParameters.ContainsKey('StartVM')) {$Global:NewOSDCloudVM.StartVM = $StartVM}
    if ($PSBoundParameters.ContainsKey('SwitchName')) {$Global:NewOSDCloudVM.SwitchName = $SwitchName}
    if ($PSBoundParameters.ContainsKey('VHDSizeGB')) {$Global:NewOSDCloudVM.VHDSizeGB = $VHDSizeGB}

    # Validate SwitchName
    if ($Global:NewOSDCloudVM.SwitchName) {
        $ValidateSwitch = Get-VMSwitch -Name $Global:NewOSDCloudVM.SwitchName -ErrorAction SilentlyContinue

        if (-not ($ValidateSwitch)) {
            Write-Warning "SwitchName value '$($Global:NewOSDCloudVM.SwitchName)' is not valid and will be set to Not connected."
            $Global:NewOSDCloudVM.SwitchName = $null
        }
    }

    # Export the updated configuration
    $WorkspaceConfigurationJson = Join-Path (Get-OSDCloudWorkspace) 'Logs\NewOSDCloudVM.json'
    Write-Host -ForegroundColor DarkGray "Exporting updated configuration to $WorkspaceConfigurationJson"
    $Global:NewOSDCloudVM | ConvertTo-Json -Depth 10 | Out-File -FilePath $WorkspaceConfigurationJson -Force

    if ($Reset) {
        $Global:OSDCloudVM = $null
        $Global:OSDCloudVM = [ordered]@{
            CheckpointVM                = $NewOSDCloudVM.CheckpointVM
            Generation                  = $NewOSDCloudVM.Generation
            MemoryStartupBytes          = ($NewOSDCloudVM.MemoryStartupGB * 1GB)
            MemoryStartupGB             = $NewOSDCloudVM.MemoryStartupGB
            NamePrefix                  = $NewOSDCloudVM.NamePrefix
            ProcessorCount              = $NewOSDCloudVM.ProcessorCount
            StartVM                     = $NewOSDCloudVM.StartVM
            SwitchName                  = $NewOSDCloudVM.SwitchName
            VHDSizeGB                   = [System.Int64]$Global:NewOSDCloudVM.VHDSizeGB
        }

        # Display the current configuration
        Write-Output $Global:OSDCloudVM
    }
    else {
        # Get Hyper-V Defaults
        #$VMManagementService = Get-WmiObject -Namespace root\virtualization\v2 Msvm_VirtualSystemManagementService
        $VMManagementServiceSettingData = Get-WmiObject -Namespace root\virtualization\v2 Msvm_VirtualSystemManagementServiceSettingData

        # Set VM Name
        $VmName = "$($NewOSDCloudVM.NamePrefix)$((Get-Date).ToString('yyMMddHHmmss'))"

        # Build Final Configuration
        $Global:OSDCloudVM = $null
        $Global:OSDCloudVM = [ordered]@{
            CheckpointVM                = $NewOSDCloudVM.CheckpointVM
            DvdDrivePath                = Join-Path $(Get-OSDCloudWorkspace) 'OSDCloud_NoPrompt.iso'
            Generation                  = $NewOSDCloudVM.Generation
            MemoryStartupBytes          = ($NewOSDCloudVM.MemoryStartupGB * 1GB)
            MemoryStartupGB             = $NewOSDCloudVM.MemoryStartupGB
            Name                        = $VmName
            NamePrefix                  = $NewOSDCloudVM.NamePrefix
            ProcessorCount              = $NewOSDCloudVM.ProcessorCount
            StartVM                     = $NewOSDCloudVM.StartVM
            SwitchName                  = $NewOSDCloudVM.SwitchName
            VHDPath                     = [System.String](Join-Path $VMManagementServiceSettingData.DefaultVirtualHardDiskPath "$VmName.vhdx")
            VHDSizeBytes                = ($NewOSDCloudVM.VHDSizeGB * 1GB)
            VHDSizeGB                   = [System.Int64]$Global:NewOSDCloudVM.VHDSizeGB
        }

        # Display the current configuration
        Write-Output $Global:OSDCloudVM

        # Create VM VHD
        if ($OSDCloudVM.SwitchName) {
            $vm = New-VM -Name $OSDCloudVM.Name -Generation $OSDCloudVM.Generation -MemoryStartupBytes $OSDCloudVM.MemoryStartupBytes -NewVHDPath $OSDCloudVM.VHDPath -NewVHDSizeBytes $OSDCloudVM.VHDSizeBytes -SwitchName $OSDCloudVM.SwitchName -Verbose -ErrorAction Stop
        }
        else {
            $vm = New-VM -Name $OSDCloudVM.Name -Generation $OSDCloudVM.Generation -MemoryStartupBytes $OSDCloudVM.MemoryStartupBytes -NewVHDPath $OSDCloudVM.VHDPath -NewVHDSizeBytes $OSDCloudVM.VHDSizeBytes -Verbose -ErrorAction Stop
        }
        
        # Create DVD
        $OSDCloudVM.DvdDrive = $vm | Add-VMDvdDrive -Path $OSDCloudVM.DvdDrivePath -Passthru -Verbose
        $OSDCloudVM.HardDiskDrive = $vm | Get-VMHardDiskDrive
        $OSDCloudVM.NetworkAdapter = $vm | Get-VMNetworkAdapter

        if ($OSDCloudVM.Generation -eq 2) {
            # First Boot Device
            $vm | Set-VMFirmware -FirstBootDevice $OSDCloudVM.DvdDrive

            # Firmware
            #$vm | Set-VMFirmware -BootOrder $OSDCloudVM.DvdDrive, $vmHardDiskDrive, $vmNetworkAdapter -Verbose

            # Security
            $vm | Set-VMFirmware -EnableSecureBoot On -Verbose
            if ((Get-TPM).TpmPresent -eq $true -and (Get-TPM).TpmReady -eq $true) {
                $vm | Set-VMSecurity -VirtualizationBasedSecurityOptOut:$false -Verbose
                $vm | Set-VMKeyProtector -NewLocalKeyProtector -Verbose
                $vm | Enable-VMTPM -Verbose
            }
        }

        # Memory
        $vm | Set-VMMemory -DynamicMemoryEnabled $false -Verbose

        # Processor
        $vm | Set-VMProcessor -Count $OSDCloudVM.ProcessorCount -Verbose

        # Integration Services
        # Thanks Andreas Landry
        $IntegrationService = Get-VMIntegrationService -VMName $vm.Name | Where-Object { $_ -match "Microsoft:[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}\\6C09BB55-D683-4DA0-8931-C9BF705F6480" }
        $vm | Get-VMIntegrationService -Name $IntegrationService.Name | Enable-VMIntegrationService -Verbose

        # Checkpoints Start Stop
        $vm | Set-VM -AutomaticCheckpointsEnabled $false -AutomaticStartAction Nothing -AutomaticStartDelay 3 -AutomaticStopAction Shutdown -Verbose

        #Export Final Configuration
        $Global:OSDCloudVM.VM = $vm
        Write-Verbose "Exporting current configuration to $env:Temp\OSDCloudVM.json"
        $Global:OSDCloudVM | ConvertTo-Json -Depth 2 | Out-File -FilePath "$env:TEMP\OSDCloudVM.json" -Force

        if ($Global:OSDCloudVM.CheckpointVM -eq $true) {
            $vm | Checkpoint-VM -SnapshotName 'New-VM' -Verbose
        }

        if ($Global:OSDCloudVM.StartVM -eq $true) {
            vmconnect.exe $env:ComputerName $OSDCloudVM.Name
            Start-Sleep -Seconds 3
            $vm | Start-VM -Verbose
        }
    }
}
Register-ArgumentCompleter -CommandName New-OSDCloudVM -ParameterName 'SwitchName' -ScriptBlock {Get-VMSwitch | Select-Object -ExpandProperty Name | ForEach-Object {if ($_.Contains(' ')) {"'$_'"} else {$_}}}