PSHyperVTemplate.psm1


# NEW HYPER-V MACHINE
function New-Vmachine {
    param (
        [Parameter(Mandatory = $false)][string]$Template,
        [Parameter(Mandatory = $false)][string]$Generation,
        [Parameter(Mandatory = $false)][switch]$Start,
        [Parameter(Mandatory = $false)][string]$Name,
        [Parameter(Mandatory = $false)][string]$ISO,
        [Parameter(Mandatory = $false)][switch]$RunManager
    )
    $Env = [System.Environment]::OSVersion.Platform
    if ($Env -match "Win32NT") {
        # verify that the VmModule.xml exist
        if (!(Test-Path -Path "C:\Temp\VmTemplates")) { New-Item -Path "C:\Temp\VmTemplates" -ItemType Directory }
        if (!(Test-Path -Path "C:\Temp\VmTemplates\DefaultTemplate.xml")) { Invoke-WebRequest -Uri "https://raw.githubusercontent.com/UsefulScripts01/PsModules/main/DefaultTemplate.xml" -OutFile "C:\Temp\VmTemplates\DefaultTemplate.xml" }

        # templates
        if ($Template) {
            if ($Template -notmatch ".xml") { $Template = "${Template}.xml" } # add .xml
            $TemplatesList = Get-ChildItem -Path "C:\Temp\VmTemplates\"
            if ($TemplatesList.Name -contains $Template) { [XML]$Set = Get-Content -Path "C:\Temp\VmTemplates\$Template" }
            else {
                Get-ChildItem -Path "C:\Temp\VmTemplates\"
                Write-Warning "$Template file not found.."
                break
            }
        }
        else { [XML]$Set = Get-Content -Path "C:\Temp\VmTemplates\DefaultTemplate.xml" }

        # VM name
        if ($Name) { $VMName = $Name }
        elseif (!$Name) {
            $AutoName = $Set.NewVmachine.Name
            $VMLastNumber = ((Get-Vm -Name $AutoName*).Name | Measure-Object -Maximum).Count
            $VMLastNumber ++
            $VMName = "${AutoName}${VMLastNumber}"
        }

        # memory
        $RamSize = 1073741824 * ($Set.NewVmachine.Memory.Size)
        $MinimumRAM = 1073741824 * ($Set.NewVmachine.Memory.Minimum)
        $MaximumRAM = 1073741824 * ($Set.NewVmachine.Memory.Maximum)

        $VhdSize = 1073741824 * ($Set.NewVmachine.HardDrive.Size) # vhd size to bytes
        $VhdPath = Join-Path -Path $Set.NewVmachine.HardDrive.Path -ChildPath "$VMName.vhdx" # path from xml

        # boot ISO
        if ($ISO) { $VMBootISO = $ISO }
        elseif (!$ISO) { $VMBootISO = $Set.NewVmachine.DVD.ISO }

        # generation
        if (!$Generation) { $Generation = $Set.NewVmachine.Generation }
        Switch ($Generation) {
            1 {
                # create VM - generation 1
                # script wil attach the existing VHDX (with the same name as VM) instead of creating a new one
                if (!(Test-Path -Path $VhdPath)) { New-VM -Name $VMName -Generation 1 -MemoryStartupBytes $RamSize -NewVHDPath $VhdPath -NewVHDSizeBytes $VhdSize -BootDevice CD }
                else { New-VM -Name $VMName -Generation 1 -MemoryStartupBytes $RamSize -VHDPath $VhdPath -BootDevice CD }
                Set-VMDvdDrive -VMName $VMName -Path $VMBootISO
            }
            2 {
                # create VM - generation 2
                if (!(Test-Path -Path $VhdPath)) { New-VM -Name $VMName -Generation 2 -MemoryStartupBytes $RamSize -NewVHDPath $VhdPath -NewVHDSizeBytes $VhdSize }
                else { New-VM -Name $VMName -Generation 2 -MemoryStartupBytes $RamSize -VHDPath $VhdPath }
                Add-VMDvdDrive -VMName $VMName -Path $VMBootISO
                $DVD = Get-VMDVDDrive -VMName $VMName
                Set-VMFirmware $VMName -FirstBootDevice $DVD
            }
        }

        # CHANGE SETTINGS ON AN EXISTING MACHINE
        # secure boot
        if ((Get-VM  -Name $VMName).Generation -eq "2") {
            switch ($Set.NewVmachine.SecureBoot) {
                0 { Set-VMFirmware -VMName $VMName -EnableSecureBoot OFF }
                1 { Set-VMFirmware -VMName $VMName -EnableSecureBoot ON }
            }
        }

        # processor
        switch ($Set.NewVmachine.CPU.MigrateToPhysical) {
            0 { Set-VMProcessor -VMName $VMName -Count $Set.NewVmachine.CPU.Count -CompatibilityForMigrationEnabled $false }
            1 { Set-VMProcessor -VMName $VMName -Count $Set.NewVmachine.CPU.Count -CompatibilityForMigrationEnabled $true }
        }

        # min/max ram size
        Set-VMMemory $VMName -DynamicMemoryEnabled $true -MinimumBytes $MinimumRAM -StartupBytes $RamSize -MaximumBytes $MaximumRAM -Priority 80 -Buffer 20

        # automatic checkpoints
        switch ($Set.NewVmachine.AutoCheckpoints) {
            0 { Set-VM -VMName $VMName -AutomaticCheckpointsEnabled $false }
            1 { Set-VM -VMName $VMName -AutomaticCheckpointsEnabled $true }
        }

        # network
        $NetAdapter = (Get-VMNetworkAdapter -VMName $VMName).Name
        Connect-VMNetworkAdapter -VMName $VMName -Name $NetAdapter -SwitchName $Set.NewVmachine.Network.VirtualSwitch

        # "start" switch
        if ($Start) { Start-VM -Name $VMName }
        elseif (!$Start) {
            switch ($Set.NewVmachine.AutoStart) {
                0 { }
                1 { Start-VM -Name $VMName }
            }
        }

        # automatic run hyper-v manager
        if ($RunManager) { Start-Process -FilePath "virtmgmt.msc" }
        elseif (!$RunManager) {
            switch ($Set.NewVmachine.AutoRunManager) {
                0 { }
                1 { Start-Process -FilePath "virtmgmt.msc" }
            }
        }

        # Collected error log will be used in FinishProcess.ps1 (Get-ErrorLog)
        if (!$Error.Count.Equals(0)) {
            $DateTime = Get-Date -Format "dd.MM.yyyy HH:mm"
            foreach ($Entry in $Error) {
                Add-Content -Value "$DateTime - $VMName - $Entry" -Path "~\Desktop\VM-ErrorLog.log" -Force
            }
        }

    }
    else { Write-Warning "New-Vmachine is available only in Win32NT environment.." }
}
Export-ModuleMember -Function New-Vmachine

# DELETE HYPER-V MACHINE AND VHDX
function Remove-Vmachine {
    param (
        [Parameter(Mandatory = $true)][string]$Name
    )
    switch ([System.Environment]::OSVersion.Platform) {
        Win32NT {
            if ((Get-VM).Name.Contains($Name)) {
                $Vhdx = (Get-VMHardDiskDrive -VMName $Name).Path
                Remove-VM -Name $Name
                Start-Sleep -Seconds 2
                Remove-Item -Path $Vhdx
            }
            else { Write-Warning "Machine $Name does not exist.." }
        }
        Unix { Write-Warning "Remove-Vmachine is available only in Win32NT environment.." }
    }
}
Export-ModuleMember -Function Remove-Vmachine