Hyper-V GUI in Powershell

written by Tomas
2021 · 03 · 08

A small powershell script that i make to test powershell script with gui. In this gui a can see all my hyper-v guest i have. I can start/stop, connect and delete machines in this gui.

Powershell Script for showing machines in Hyper-V

<#
 Solution: Hyper-V 
 Purpose: Hyper-V GUI
 Version: 2.0.1
    Date: 08 Mars 2021 

  Author: Tomas Johansson
 Twitter: @Deploymentdude
     WWW: www.4thcorner.net

 This script is provided "AS IS" with no warranties, confers no rights and 
 is not supported by the author
#>

# Check for elevation
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( `
    [Security.Principal.WindowsBuiltInRole] "Administrator")) {
    Write-Warning "Oupps, you need to run this script from an elevated PowerShell prompt!`nPlease start the PowerShell prompt as an Administrator and re-run the script."
	Write-Warning "Aborting script..."
    Break
}

# Check hyper-v module installed
if(!(get-module -ListAvailable -Name "hyper-v")) {
    throw "Hyper-V module not found. Please run this tool on a server that has the Hyper-V management tools installed"
}

# Add needed Assemblys
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Drawing  
Add-Type -AssemblyName System.Windows.Forms
[void] [System.Windows.Forms.Application]::EnableVisualStyles() 

[xml]$XAML = @'
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Virtual Machines in Hyper-V" Height="450" Width="740">
        <Grid Name="grid" Margin="20,0,25,0.5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Label Name="Information" Content="Hyper-V Machines on:" Grid.Row="1" />
        <Label Name="Machine" Content="Machines" Grid.Row="1" Grid.ColumnSpan="2" Margin="125,0,0,0" />
        <ListView Name="ListViewMain" HorizontalAlignment="Left" Height="280" Width="680" Margin="0" VerticalAlignment="Top"  Grid.Row="2">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Hyper-V VM Name" DisplayMemberBinding="{Binding VMName}" />
                    <!--<GridViewColumn Header="VMName" DisplayMemberBinding ="{Binding VMName}"/>-->
                    <GridViewColumn Header="State" DisplayMemberBinding ="{Binding VMState}"/>
                    <GridViewColumn Header="CPU Usage" DisplayMemberBinding ="{Binding VMCPUUsage}"/>
                    <GridViewColumn Header="Assigned Memory MB" DisplayMemberBinding ="{Binding VMMemoryAssigned}"/>
                    <GridViewColumn Header="Version" DisplayMemberBinding ="{Binding VMVersion}"/>
                    <!--<GridViewColumn Header="VM Path" DisplayMemberBinding ="{Binding VMPath}"/>-->
                    <GridViewColumn Header="VM Uptime" DisplayMemberBinding ="{Binding VMUptime}"/>
                    <!--<GridViewColumn Header="VM" DisplayMemberBinding="{Binding VM}" />-->
                </GridView>
            </ListView.View>
        </ListView>
        <StackPanel Grid.Row="3" HorizontalAlignment="Left" Grid.ColumnSpan="2" Width="300">
            <Label Name="LabelAction" Content="Actions:" />
        </StackPanel>
        <GroupBox Name="groupBox_Hyper_V_Start_Stop" Header="Start/Stop VM" HorizontalAlignment="Left" Margin="0,30,0,0" Grid.Row="3" VerticalAlignment="Top" Height="64">
            <StackPanel VerticalAlignment="Top" HorizontalAlignment="Left" Orientation="Horizontal">
                <Button Name="buttonStart" Content="Start" Width="75" Margin="10,10,0,10" />
                <Button Name="buttonStop" Content="Stop" Width="75" Margin="20,10,10,10" HorizontalAlignment="Left"/>
            </StackPanel>
        </GroupBox>
        <GroupBox Name="groupBoxConnect" Header="Console" HorizontalAlignment="Left" Margin="220,30,0,0" Grid.Row="3" VerticalAlignment="Top">
            <Button Name="buttonConnect" Content="Connect" HorizontalAlignment="Center" Width="75" Margin="10"/>
        </GroupBox>
        <Button Name="buttonRefresh" Content="Refresh" HorizontalAlignment="Left" Margin="500,65,0,0" Grid.Row="3" VerticalAlignment="Top" Width="75"/>
        <Button Name="buttonCancel" Content="Cancel" HorizontalAlignment="Right" Margin="0,65,10,0" Grid.Row="3" VerticalAlignment="Top" Width="74"/>
        <GroupBox x:Name="groupBoxDelete" Header="Delete VM" HorizontalAlignment="Left" Margin="350,30,0,0" Grid.Row="3" VerticalAlignment="Top">
            <Button Name="buttonDeleteVM" Content="Delete" HorizontalAlignment="Left" Width="75" Margin="10"/>
        </GroupBox>
    </Grid>
</Window>
'@

# Read XAML
$Reader = New-Object System.Xml.XmlNodeReader $XAML
$Form = [Windows.Markup.XamlReader]::Load($Reader)
foreach ($Name in ($XAML | Select-Xml '//*/@Name' | ForEach-Object { $_.Node.Value})) {
    New-Variable -Name $Name -Value $Form.FindName($Name) -Force
}

Function LoadListView {
$VMInfos = @()
$VMs = get-vm

    foreach ($VM in $VMs) {
        $VMInfo = New-Object -TypeName PSObject
        $VMInfo | Add-Member -MemberType NoteProperty -Name VMName -Value $VM.Name
        $VMInfo | Add-Member -MemberType NoteProperty -Name Name -Value $VM.Name
        $VMInfo | Add-Member -MemberType NoteProperty -Name VMState -Value $VM.State
        $VMInfo | Add-Member -MemberType NoteProperty -Name VMCPUUsage -Value $VM.CPUUsage
        $VMInfo | Add-Member -MemberType NoteProperty -Name VMMemoryAssigned -Value ($VM.MemoryAssigned/1MB)
        $VMInfo | Add-Member -MemberType NoteProperty -Name VMVersion -Value $VM.Version
        $VMInfo | Add-Member -MemberType NoteProperty -Name VMPath -Value $VM.Path
        $VMInfo | Add-Member -MemberType NoteProperty -Name VMUptime -Value ("{0:D1}" -f $VM.Uptime.Days +  '.' + "{0:D2}" -f $VM.Uptime.Hours +  ':' + "{0:D2}" -f $VM.Uptime.Minutes +  ':' + "{0:D2}" -f $VM.Uptime.Seconds)
        $VMInfo | Add-Member -MemberType NoteProperty -Name VM -Value $VM
        $VMInfos += $VMInfo
    }
    $ListViewMain.ItemsSource = $VMInfos
}

# Event Handlers
$Form.Add_Loaded({

})

# Sort event handler
$Form.Add_SourceInitialized({
    [System.Windows.RoutedEventHandler]$ColumnSortHandler = {

        If ($_.OriginalSource -is [System.Windows.Controls.GridViewColumnHeader]) {
           
            If ($_.OriginalSource -AND $_.OriginalSource.Role -ne 'Padding') {

                $Column = $_.Originalsource.Column.DisplayMemberBinding.Path.Path
                                                
                # And now we actually apply the sort to the View
                $ListViewMain_DefaultView = [System.Windows.Data.CollectionViewSource]::GetDefaultView($ListViewMain.ItemsSource)
                # Change the sort direction each time they sort
                    Switch($ListViewMain_DefaultView.SortDescriptions[0].Direction)
                    {
                        "Decending" { $Direction = "Ascending" }
                        "Ascending" { $Direction = "Descending" }
                            Default { $Direction = "Ascending" }
                    }           
                $ListViewMain_DefaultView.SortDescriptions.Clear()
                $ListViewMain_DefaultView.SortDescriptions.Add((New-Object System.ComponentModel.SortDescription $Column, $Direction))
                $ListViewMain_DefaultView.Refresh()  
            }
        }
    }
    # Attach the Event Handler
    $ListViewMain.AddHandler([System.Windows.Controls.GridViewColumnHeader]::ClickEvent, $ColumnSortHandler)
})

# Stop selected VMs
$ButtonStop.Add_Click({
    $Form.Topmost = $false
    Stop-VMs -VM $ListViewMain.SelectedItems
})

# Start selected VMs
$buttonStart.Add_Click({
    $Form.Topmost = $false
    Start-VMs -VM $ListViewMain.SelectedItems
})

# Connect to Conole on selected VM
$buttonConnect.Add_Click({
    $Form.Topmost = $false

    if ([string]::IsNullOrEmpty($ListViewMain.SelectedItem.Name)) {
        $ButtonType = [System.Windows.MessageBoxButton]::OK
        $MessageboxTitle = "Connect to Console"
        $Messageboxbody = "Please select a Machine to connect to!"
        $MessageIcon = [System.Windows.MessageBoxImage]::Exclamation
        [System.Windows.MessageBox]::Show($Messageboxbody,$MessageboxTitle,$ButtonType,$messageicon)

    }
    Else {
        Start-Process -FilePath  "$env:windirsystem32vmconnect.exe" -ArgumentList $env:COMPUTERNAME,$ListViewMain.SelectedItem.Name
    }
})

$buttonDeleteVM.Add_Click({
    $Form.Topmost = $false

    if ([string]::IsNullOrEmpty($ListViewMain.SelectedItem.Name)) {
        $ButtonType = [System.Windows.MessageBoxButton]::OK
        $MessageboxTitle = "Delete Virtula Macine"
        $Messageboxbody = "Please select a Machine to Delete!"
        $MessageIcon = [System.Windows.MessageBoxImage]::Exclamation
        [System.Windows.MessageBox]::Show($Messageboxbody,$MessageboxTitle,$ButtonType,$messageicon)

    }
    Else {
        Delete-VM -VM $ListViewMain.SelectedItems
    }
})

# Refresh ListView
$ButtonRefresh.Add_Click({
    LoadListView
})

# Close dialog window
$ButtonCancel.add_click({ 
    $Form.Close() 
})

$objForm = New-Object System.Windows.Forms.Form 
$objForm.KeyPreview = $True

$objForm.Add_KeyDown({
    If ($_.KeyCode -eq "Escape") {
        $objForm.Close()
    }
})

# Action Stop Virtual Machines
function Stop-VMs
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [Object] $VM
    )
    Begin {
    }
    Process
    {
     write-warning "Shutting down VM: $($VM.VMName) ($($VM.Name))"
     Stop-VM -Name $VM.Name
    }
    End {
        LoadListView
    }
}

# Action Start Virtual Machines
Function Start-VMs {
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [Object] $VM
    )
    Begin {
    }
    Process
    {
     write-warning "Starting VM: $($VM.VMName) "
     Start-VM -Name $VM.VMName
    }

    End {
        LoadListView
    }
}

# Delete Selected Virtual Machines
Function Delete-VM {
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [Object] $VM
    )
    Begin {
    }
    Process {
        write-warning "Deleting VM: $($VM.VMName) ($($VM.Name))"
        $VM2Delete  = $VM
        ForEach ($VMDelet in $VM2Delete) {
            $ButtonType = [System.Windows.MessageBoxButton]::YesNo
            $MessageboxTitle = “ Delete Virtual Machine”
            $Messageboxbody = “Do you really want to delete this machine:`n`n$($VMDelet.Name)"
            $MessageIcon = [System.Windows.MessageBoxImage]::Question
            $Result = [System.Windows.MessageBox]::Show($Messageboxbody,$MessageboxTitle,$ButtonType,$messageicon)
            If ($Result -eq "YES") {
                DoDeleteOfVM -MachineName $VMDelet.Name
                $ButtonType = [System.Windows.MessageBoxButton]::OK
                $MessageboxTitle = “ Virtual Machine Deleted”
                $Messageboxbody = “Virtual Machine: $($VMDelet.Name) Deleted!"
                $MessageIcon = [System.Windows.MessageBoxImage]::Information
                [System.Windows.MessageBox]::Show($Messageboxbody,$MessageboxTitle,$ButtonType,$messageicon)
            }
        }
    }
    End {
        LoadListView
    }
}

# Doing Deleting ov selected VirtualMachine
Function DoDeleteofVM {
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [String]$MachineName
    )
    Begin {
    }
    Process {
        Try {
        # Get VM information
        $VM = Get-VM -Name $MachineName -ErrorAction stop
        Write-Host  -ForegroundColor Green "Virtual machine: $($MachineName.ToUpper()) Exist on host"

        # Shutdown machine if running
        If ($VM.State -eq "Running")  {
            Write-Host  -ForegroundColor Yellow "Virtual machine: $($MachineName.ToUpper()) Running"
            DO {
                Stop-VM -Name $MachineName -TurnOff
                $VM = Get-VM -Name $MachineName -ErrorAction stop
    
            } While ($VM.State -eq "Running")
            Write-Host  -ForegroundColor Green "Virtual machine: $($MachineName.ToUpper()) is Stopped"
        }

        #Get Path for machine to delete
        $VMPath = $VM.Path

        # Removing machine from hyper-v host
        Remove-VM –vmname $MachineName -Force

        # Remove Directorys and files
        If (Test-Path $VMPath) {
            Get-ChildItem -Path $VMPath -Include * | remove-Item -recurse
            If (Test-Path $VMPath) { 
                Remove-Item -Path $VMPath
            }
        }
            Write-Host -ForegroundColor Green "Virtual machine: $($MachineName.ToUpper()) Deleted from host"
        }
        Catch
        {
            Write-Host -ForegroundColor Yellow "No Virtual machine: $($MachineName.ToUpper()) Exist on host"
        }
    }
}

# Set Lable Machine Content
$Machine.Content = $env:COMPUTERNAME

# Load Listview with data
LoadListView

# Launch the window
$Form.WindowStartupLocation="CenterScreen"
$Form.Add_Loaded( {
    $this.TopMost = $true
})

# Show the window
$Form.ShowDialog() | out-null

Link to repository on github

Author

Tomas

I have worked in IT for more than 25 years, as a consultant, system administrator and technician. As a consultant, i have worked with system integration and development. I have also managed Windows client deployment for Swedish Armed Forces. I have a broad experience of most things in IT and still has a burning interest in learning new technologies and how to best use the technology.

Related Posts

Copy files to and from Hyper-V Guest

Using the PowerShell cmdlet Copy-VMFile To copy files to a VM using the Copy-VMFile cmdlet, we first have to enable Guest Services under Integration Services for a VM. Luckily, there is a PowerShell one-liner for this:Now we are now ready to copy the file from the...

Create NetNat Switch for Hyper-V

 In the latest versions in the Windows 10 client operating system, Microsoft already includes a “Default Virtual Switch”, which allows you to use Hyper-V NAT Networking, without doing any configuration changes. If you want to create an additional VM Switch which uses...

Powershell GUI to encrypt string

I’ve often found myself with tasks eligible for automation. Automating is great with PowerShell until you need to pass credentials into a script. I have seen many administrators put passwords into the body of their script. For testing purposes, this could considered a...

Copy files to and from Hyper-V Guest

Using the PowerShell cmdlet Copy-VMFile To copy files to a VM using the Copy-VMFile cmdlet, we first have to enable Guest Services under Integration Services for a VM. Luckily, there is a PowerShell one-liner for this:Now we are now ready to copy the file from the...

Create NetNat Switch for Hyper-V

 In the latest versions in the Windows 10 client operating system, Microsoft already includes a “Default Virtual Switch”, which allows you to use Hyper-V NAT Networking, without doing any configuration changes. If you want to create an additional VM Switch which uses...

Powershell GUI to encrypt string

I’ve often found myself with tasks eligible for automation. Automating is great with PowerShell until you need to pass credentials into a script. I have seen many administrators put passwords into the body of their script. For testing purposes, this could considered a...

Comments

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

Follow Us