Welcome to my first Azure Stack blog.

Recently I have been exploring the Azure Stack POC TP1 which is available for everyone at https://azure.microsoft.com/en-us/overview/azure-stack/

So far I am very impressed, of cause always bear in mind that this is the first technical preview POC.

Today I made a small tool for helping me to have a good overview of my Azure Stack tenant VMs.

In Azure Stack the VMs are named by their GUID in Hyper-V.

This makes perfect sense, since multiple tenants could easily name their VMs the same name.

But when administrating your hyper-v host, It can be a bit hard to recognize which machine is which.

 

Therefore I have create a small GUI tool in PowerShell to help list the machines on a host, and to be able to trigger some basic actions.

 

This is the first beta and the following actions have been added:

  • Connect to Console
  • Stop VMs
  • Start VMs

image

You can download the PowerShell based tool here:

https://gallery.technet.microsoft.com/Azure-Stack-VM-GUI-Beta-1-9e55aa80

Here is the current script content (the gallery item might be updated since this blog post was published):

# //*********************************************************************************************
# // Solution:  
# // Author:	Jakob Gottlieb Svendsen, Coretech A/S. https://blog.ctglobalservices.com / www.runbook.guru
# // Purpose:   List Azure Stack VMs from hyper-v host in a friendly layout.
# //			Trigger actions on a number of VMs
# //            Connects to localhost by default. Please send suggestions to [email protected]
# //
# // Usage:     MAS-VM-Admin.ps1
# //            MAS-VM-Admin.ps1 -Computer <hyper-v host"
# //
# // History:
# // 1.0.0     JGS 02/22/2016  Created initial version.
# //
# //********************************************************************************************
# //----------------------------------------------------------------------------
#//
#//  Global constant and variable declarations
#/
#//----------------------------------------------------------------------------

param($ComputerName = "localhost")

#//----------------------------------------------------------------------------
#//  Procedures
#//----------------------------------------------------------------------------

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

    foreach ($VM in $VMs)
    {
        if(!$vm.Notes.StartsWith("Region:")) { continue } #Skip non MAS VMs

        $VMInfo = New-Object -TypeName PSObject
        $VMInfo | Add-Member -MemberType NoteProperty -Name Name -Value $VM.Name


        #MAS Attributes
        $NotesSplit = $VM.Notes.Split(",")
        foreach ($pair in $NotesSplit)
        {
            $pairsplit = $pair.Split(":")
            $VMInfo | Add-Member -MemberType NoteProperty -Name $pairsplit[0].Trim() -Value $pairsplit[1].Trim()
        }
        $VMInfo | Add-Member -MemberType NoteProperty -Name VM -Value $VM
        $VMInfos += $VMInfo
    }
    
    $ListViewMain.ItemsSource= $VMInfos

}
#//----------------------------------------------------------------------------
#//  Main routines
#//----------------------------------------------------------------------------

#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-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore

[XML]$MainWindow = @'
<Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Runbook.Guru - Azure Stack VM Manager" Height="700" Width="1000">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1000*"></ColumnDefinition>
            <ColumnDefinition Width="150"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <ListView x:Name="ListViewMain" Grid.Column="0" HorizontalAlignment="Stretch"  VerticalAlignment="Stretch" >
            <ListView.View>
                <GridView >
                    <GridViewColumn Header="VMName" DisplayMemberBinding="{Binding VMName}" />
                    <GridViewColumn Header="State" DisplayMemberBinding="{Binding VM.State}" />
                    <GridViewColumn Header="CPU Usage" DisplayMemberBinding="{Binding VM.CPUUsage}" />
                    <GridViewColumn Header="Assigned Memory" DisplayMemberBinding="{Binding VM.MemoryAssigned}" />
                    <GridViewColumn Header="Uptime" DisplayMemberBinding="{Binding VM.Uptime}" />
                    <GridViewColumn Header="ResourceGroup" DisplayMemberBinding="{Binding ResourceGroup}" />
                    <GridViewColumn Header="Subscription" DisplayMemberBinding="{Binding Subscription}" />
                    <GridViewColumn Header="Region" DisplayMemberBinding="{Binding Region}" />
                    <GridViewColumn Header="Hyper-V VM Name" DisplayMemberBinding="{Binding Name}" />
                    <!--<GridViewColumn Header="VM" DisplayMemberBinding="{Binding VM}" />-->
                </GridView>
            </ListView.View>
        </ListView>

        <StackPanel Grid.Column="1">
            <Label Name="LabelAction" Content="Action" />
            <ComboBox Name="ComboBoxAction" />
            <Button Name="ButtonGo" Content="Go" />
            <Button Name="ButtonRefresh" Content="Refresh VMs" />
        </StackPanel>
    </Grid>
</Window>
'@

$Reader = (New-Object System.XML.XMLNodeReader $MainWindow)
$Window = [Windows.Markup.XAMLReader]::Load($Reader)

$ButtonGo = $Window.FindName('ButtonGo')
#$ButtonAdd = $Window.FindName('ButtonAdd')
$ButtonRefresh = $Window.FindName('ButtonRefresh')
$ComboBoxAction = $Window.FindName('ComboBoxAction')
$ListViewMain = $Window.FindName('ListViewMain')

#Set sorting events

UpdateVMList -ComputerName $ComputerName

$Actions = @("Connect to Console","Stop VMs", "Start VMs")
$ComboBoxAction.ItemsSource = $Actions
$ComboBoxAction.SelectedIndex = 0

#Event Handlers
$Window.Add_Loaded({

 })

#Sort event handler
$Window.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)
     
        })

$ButtonGo.Add_Click({
    
    switch ($ComboBoxAction.SelectedValue)
    {
        "Stop VMs" {
            Stop-VMs -VM $ListViewMain.SelectedItems
        }
        "Start VMs" {
            Start-VMs -VM $ListViewMain.SelectedItems
        }
        "Connect to Console" {
             #. "$env:windir\system32\vmconnect.exe" "$computername $($ListViewMain.SelectedItem.Name)"
             Start-Process -FilePath  "$env:windir\system32\vmconnect.exe" -ArgumentList  $computername,$ListViewMain.SelectedItem.Name
        }
       
    }

})

$ButtonRefresh.Add_Click({
    UpdateVMList
})

#Actions
function Stop-VMs
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [Object] $VM
    )

    Process
    {
     write-warning "Shutting down VM: $($VM.VMName) ($($VM.Name))"
     Stop-VM -Name $VM.Name
    }
}

function Start-VMs
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [Object] $VM
    )

    Process
    {
     write-warning "Starting VM: $($VM.VMName) ($($VM.Name))"
     Start-VM -Name $VM.Name
    }
}

$Window.ShowDialog()


#//----------------------------------------------------------------------------
#//  End Script
#//----------------------------------------------------------------------------