Tuesday, March 6, 2012

Applying SCVMM 2012 Logical network assignment with PowerShell

This is a little script that I needed to write in order to quickly set up a test environment of mine.

One thing that I am going to do here is contrast what I did with what the SCVMM view script gave me.

The known issues at hand:

I have a logical network.  I have a large number of hosts that all need to be associated with this logical network.  I have to find the correct NIC (as a logical network is associated with the hardware NIC, not with a virtual network, so it is always an External Virtual Network).

Here is what I have:

$vmHost = Get-SCVMHost

foreach ($e in $vmHost) {
    $vmHostNetworkAdapter = Get-SCVMHostNetworkAdapter -VMHost $e | where {$_.IPAddresses -match "10.233.40"}

    $logicalNetwork = Get-SCLogicalNetwork -Name "Shared"
    $JobGroupID = [System.Guid]::NewGuid().ToString()

    Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $vmHostNetworkAdapter -AddOrSetLogicalNetwork $logicalNetwork -JobGroup $JobGroupID

    Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $vmHostNetworkAdapter -AvailableForPlacement $true  -JobGroup $JobGroupID

    Set-SCVMHost -VMHost $e -JobGroup $JobGroupID -RunAsynchronously

Breaking it down:

  • I get the all the host objects.
  • I query for the physical NIC, selecting on the IP address that it has (I want the logical network in this IP range).
  • I get the logical network object. (previously created manually)
  • Then I assign the Logical network to the NIC.
  • I also enable placement.

I could have selected on different parameters to find the proper NIC of the Hyper-V Server to create the Logical Network on.

A particular SCVMHostNetworkAdapter has the following properties:

Name                           : Intel(R) 82579LM Gigabit Network Connection
IPAddresses                    : {, fe80::b428:524d:463:3ce0}
DHCPEnabled                    : True
IPSubnets                      : {, fe80::/64}
DefaultIPGateways              : {}
MacAddress                     : 18:03:73:D2:53:4C
PhysicalAddress                : 18:03:73:D2:53:4C
NetworkLocation                : Shared
VirtualNetwork                 : Shared
VLanTags                       : {0}
UsableVLanTags                 : {0}
VLanMode                       : Access
DesiredVLanMode                : Access
VLanEnabled                    : False
ConnectionState                : Connected
ConnectionName                 : Local Area Connection 3
Description                    :
PrimaryDNSSuffix               :
MaxBandwidth                   : 1000
VMHost                         : threel5.pvs.brianeh.local
LogicalNetworkMap              : {[Shared, System.Collections.Generic.List`1[Microsoft.SystemCenter.VirtualMachineManager.SubnetVLan]]}
UsableVlanMode                 : Access
ModelAccessibleSubnetVLans     : {[Shared, System.Collections.Generic.List`1[Microsoft.SystemCenter.VirtualMachineManager.SubnetVLan]]}
LogicalNetworks                : {Shared}
SubnetVLans                    : {}
UnassignedVLans                : {}
AvailableForPlacement          : True
UsedForManagement              : True
LogicalNetworkCompliance       : Compliant
LogicalNetworkComplianceErrors : {}
BDFLocationInformation         : PCI bus 0, device 25, function 0
ServerConnection               : Microsoft.SystemCenter.VirtualMachineManager.Remoting.ServerConnection
ID                             : fcb44659-06ca-44e8-9932-c9488897437a
IsViewOnly                     : False
ObjectType                     : VMHostNetworkAdapter
MarkedForDeletion              : False
IsFullyCached                  : True

For me it made sense to select on the following:

IPAddresses : {, fe80::b428:524d:463:3ce0}


IPSubnets : {, fe80::/64}

But you could select on any of the properties of the object.  A bit of background;

In SCVMM the Logical Network is associated with a physical NIC. For the same reason that I selected on the IP. It is a way to tie the VM to the physical topology of your network. So you always end up on the same subnet.

This gets around historic Windows behavior of not always ordering the NICs the same as they are in the chassis. You never can guarantee a relationship between the physical and the ordering in the OS.

I mentioned at the top that I would also look at what the SCVMM View Script button in the wizard gave me.  I have always been critical of how verbose the View Script option is of SCVMM wizards and this is no exception.  The primary reason that I am critical is that it is hard to consume and turn into something universal, especially if you are not familiar with PowerShell.

The thing to pay attention to is where you are setting parameters of an object that already has these settings applied.  In other words, wasting cycles re-setting the exact same value.

Here is what SCVMM gave me with ‘View Script’:

$vmHost = Get-SCVMHost -ID "dcbcaa95-7627-45a1-afbe-69ff970e40ea" -ComputerName "threeb1"

$vmHostNetworkAdapter = Get-SCVMHostNetworkAdapter -Name "Intel(R) 82579LM Gigabit Network Connection" -VMHost $vmHost

# Shared
$logicalNetwork = Get-SCLogicalNetwork -ID "cee53020-cece-48f4-afea-496030292466"
Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $vmHostNetworkAdapter -AddOrSetLogicalNetwork $logicalNetwork -JobGroup "b5856f43-6539-4d68-9a0f-5ebe1241499a"

Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $vmHostNetworkAdapter -Description "" -JobGroup "b5856f43-6539-4d68-9a0f-5ebe1241499a" -VLanMode "Access" -AvailableForPlacement $true -UsedForManagement $true

Set-SCVMHost -VMHost $vmHost -JobGroup "b5856f43-6539-4d68-9a0f-5ebe1241499a" -RunAsynchronously

Breaking it down:

  • The host (hypervisor) is selected using its ID - this is the only way to guarantee you get the right host, and only one host. And is commonly used in this precise way.
  • The host network adapter is selected by name - if you spell out the entire adapter name this works, if you rename your adapters this works. Considering that there is no correlation between the NIC and the patch panel I am going with the IP range or subnet.
  • The Logical network is queried.
  • The Host network adapter is modified to apply the setting of the logical network, then it is again set to apply a description, set the vlan mode and enable for placement.
  • In my case I had already set the VLAN mode and the placement flag was on, why set them again..
  • Then the entire change job is applied to the host.
  • You will notice a JobGroup in here. A JobGroup in SCVMM is an array of commands that are executed in order from 0 to the end. Some cmdlets support it, some do not, no you need to check your cmdlets.

Monday, February 27, 2012

Remote Desktop session shortcut keys

I thought I knew where this was.  I had a link to it.

Guess what?  The link moved and the article changed.

To prevent this in the future I am simply copying this and pasting it here.

The original source is: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383500(v=vs.85).aspx

How long that remains is anyone’s guess.

But, I now have the important bits here.

The following is a list of the Remote Desktop Services shortcut keys.  Use these commands within a a Remote Desktop session when lacking mouse control.

ALT+PAGE UP  =  Switches between programs from left to right.

ALT+PAGE DOWN  =  Switches between programs for right to left.

ALT+INSERT  =  Cycles through the programs in the order they were started.

ALT+HOME  =  Displays the Start menu.

CTRL+ALT+BREAK  =  Switches the client between full-screen mode and window mode.

CTRL+ALT+END  =  Brings up the Windows Security dialog box.

ALT+DELETE  =  Displays the Windows menu.

CTRL+ALT+MINUS SIGN (-)  =  Places a snapshot of the active window, within the client, on the Remote Desktop Session Host (RD Session Host) server clipboard (provides the same functionality as pressing ALT+PRINT SCREEN on the local computer).

CTRL+ALT+PLUS SIGN (+)  =  Places a snapshot of the entire client windows area on the RD Session Host server clipboard (provides the same functionality as pressing PRINT SCREEN on the local computer).

Note that on an NEC98, the following shortcut keys are different:

  • CTRL+ALT+BREAK is replaced by F12.
  • CTRL+ALT+END is replaced by 15.

Wednesday, February 22, 2012

Uninstalling the SCVMM Agent from Server Core

Okay, I admit it, I screwed up.

In testing the various releases of SCVMM 2012 I dorked up my environment.  I tossed away my database when moving between builds. 

And then when I wen to re-add my Hyper-V Servers I got an error that an unsupported version of the agent is installed.

Okay, fine - I thought.  I will simply uninstall it.  Um..  But this is Server Core I am using.  There is no Control Panel, Uninstall a program.

This article is more about doing something on Server Core than fixing my SCVMM 2012 situation.

Okay.  Well, um.  Regedit works on Server Core.  Okay fire that up.  But I don’t want to manually clean the registry, there must be something useful there though.  And a bit of searching pointed me where I need to be:


This is where all the good programs go that use Windows Installer.

Under here I found the Virtual Machine Manager Agent.  And under that I found a very useful Registry Key:  “UninstallString

The UninstallString key is a string value.  It is the command line option that is invoked when uninstall is performed.  How super handy (I think I might use this all the time).

At your command prompt, run the string you find there and you will be ready to finish repair.

Monday, February 13, 2012

Skimming a Hyper-V Service Event log with PowerShell

We are all used to opening up our friend the EventLog Viewer GUI.

But what do you do when you don’t have the GUI (Server Core or Hyper-V Server for example)?  How do you get to those trusted events?

Well, PowerShell of course!  And there are even two different cmdlets to get there.

Get-EventLog and Get-WinEvent

Get-EventLog focuses on the top level event logs; Application, System, PowerShell, Security.

Get-WinEvent can get you anything, any event log.  the hardest part is identifying the Event Logs for Get-WinEvent.

Recently I have been focused on disk conversion, so lets focus on that.  VHD conversion events are in the Hyper-V Image Management event log under the Operational log.

The Hyper-V Image Management service manages VHDs (disk images) and this is also an event provider. 

If you type; get-winevent -ProviderName "Microsoft-Windows-Hyper-V-Image-Management-Service"

you will get all events, Admin and Operational.  In my case I just want Operational events, so I can do direct to the log.

get-winevent -LogName Microsoft-Windows-Hyper-V-Image-Management-Service-Operational

I cheated when I went looking for the logs, you can’t browse them like the tree view in the GUI.  I used the GUI on one server to locate the log I needed and I inferred its name from the Properties screen.

The series of EventIds that I even care about are:

  • 15110 - creating a VHD
  • 15111 - successfully created (finished creating)
  • 15106 - converting a VHD from dynamic to fixed
  • 15107 - successfully converted (finished converting)
  • And any fails, but didn’t find any

    What I know is that I have a VM name in the SCVMM job log that I want to find the corresponding Hyper-V event(s).  That should give me something to filter on.  In the GUI I see the information in the General TAB, in the event object this is in the Message property.

    get-winevent -LogName Microsoft-Windows-Hyper-V-Image-Management-Service-Operational | where {$_.Message -match "Xd1kD0229"}

    In my case the name of the VM is “Xd1kD0229” and in searching the message of the Event this gives me any instance where this string of characters occurs.  In most of the events it is a folder in the Literal Path of the VHD file.

    Now, how might you get PowerShell enabled on Server Core or Hyper-V Server?

    At the Command prompt you can add the PowerShell and .Net features:

    DISM /Online /Enable-Feature /FeatureName: NetFx2-ServerCore

    DISM /Online /Enable-Feature /FeatureName: NetFx3-ServerCore

    DISM /Online /Enable-Feature /FeatureName: MicrosoftWindowsPowerShell

    You can then launch PowerShell in the same shell session:


  • Tuesday, February 7, 2012

    Grouping and averaging time to execute execute SCVMM 2012 Jobs with PowerShell

    In my previous post I ended with a large array of System Center Job Objects.

    I need to now determine how long each job took to execute.  And since these are many jobs over time, I want to see if the time to execute changes over time.

    So, I need to sort.  I have my array from the previous post.  To sort I do this:

    $vhdConvertJobs = $vhdConvertJobs | Sort StartTime

    This will sort all Job objects in the Array ( $vhdConvertJobs )using the StartTime property of the Job object. 

    Nice and conveniently each job has a StartTime and EndTime, so I just need to subtract those.  The subtraction part is easy:

    Foreach ($job in $vhdConvertJobs) {
        ($job.EndTime - $job.StartTime).seconds

    Now the interesting part.  I have a huge list of numbers.  Great.  I could graph them. No.

    I had this great idea to group them into groups of ten and then average them.  So my averages are the first ten, then the second ten, and so on.  That is a bit easier to look at.

    $i = $vhdConvertJobs.Count
    $arrayTick = 0

    While ($arrayTick -lt $i){
        $loopTick = 0
        While ($loopTick -ne 10){
            $timeToConvert = $timeToConvert + ($vhdConvertJobs[$arrayTick].EndTime - $vhdConvertJobs[$arrayTick].StartTime).seconds
            if ($arrayTick -eq $i){break}
        "Average of " + $loopTick + " jobs: " + ($timeToConvert / $loopTick) + " seconds"
        $TimeToConvert = 0

    Put this all together and it looks like this:

       This script is dump the time to convert VHDs form Dynamic to Fixed from SCVMM 2012
       It first finds the VMs using the name match pattern (or you can leave that commented to simply get all VMs)
       Then It finds the virtual disk and the latest job on the virtual disks.
       The jobs are sorted from first started to last started.
       The different from StartTime to EndTime is output.
       Then the Jobs are averaged as groups of 10 and groups of 100 from first started to last started.
        Brian Ehlert, MSFT VM MVP

       In SCVMM Jobs age and the last job on an object changes.  So there is a timeliness to running this script.  If you
       wait too long you will get an incorrect number of jobs back.

    Import-Module VirtualMachineManager

    $vhdConvertJobs = @()

    $scVMs = Get-SCVirtualMachine | where {$_.Name -match "Xd1K"}

    Foreach ($scVM in $scVMs){
        $vhdConvertJobs += [Microsoft.SystemCenter.VirtualMachineManager.Task]( Get-SCVirtualDiskDrive -VM $scVM | where {$_.MostRecentTask -match "convert"} ).MostRecentTask

    $vhdConvertJobs = $vhdConvertJobs | Sort StartTime

    "Disk Conversion Jobs"
    "Listed first are the jobs started earlier, listed last are the jobs started later"

    Foreach ($job in $vhdConvertJobs) {
        ($job.EndTime - $job.StartTime).seconds

    # Break the sorted list of jobs into groups of 10, Average the seconds to convert per group
    "Disk Conversion Job Average per 10 conversions"
    "Sorted by StartTime.  First are the first jobs started, last are the later jobs started"

    $i = $vhdConvertJobs.Count
    $arrayTick = 0

    While ($arrayTick -lt $i){
        $loopTick = 0
        While ($loopTick -ne 10){
            $timeToConvert = $timeToConvert + ($vhdConvertJobs[$arrayTick].EndTime - $vhdConvertJobs[$arrayTick].StartTime).seconds
            if ($arrayTick -eq $i){break}
        "Average of " + $loopTick + " jobs: " + ($timeToConvert / $loopTick) + " seconds"
        $TimeToConvert = 0

    # Break the sorted list of jobs into groups of 100, Average the seconds to convert per group
    "Disk Conversion Job Average per 100 conversions"
    "Sorted by StartTime.  First are the first jobs started, last are the later jobs started"

    $i = $vhdConvertJobs.Count
    $arrayTick = 0

    While ($arrayTick -lt $i){
        $loopTick = 0
        While ($loopTick -ne 100){
            $timeToConvert = $timeToConvert + ($vhdConvertJobs[$arrayTick].EndTime - $vhdConvertJobs[$arrayTick].StartTime).seconds
            if ($arrayTick -eq $i){break}
        "Average of " + $loopTick + " jobs: " + ($timeToConvert / $loopTick) + " seconds"
        $TimeToConvert = 0