Monday, August 20, 2012

Server 2012 Core DHCP Server scope quick setup

I frequently have a need to use small VMS to quickly prototype things.  And lately I have been all deep in Server 2012.

Just to give myself some extra pain I have also been using Server Core whenever I have no absolute need for a GUI (getting off that Windows graphical console point-and-click bandwagon).

My latest adventure is to quickly create a DHCP Server. 

Just to get this running is really rather painless in the end.  And figuring out the proper PowerShell commands was pretty straightforward as well.

Begin with a Server 2012 Core installation.

Make sure it has a manually assigned IP.  ( you can use SCVMM 2012 SP1 CTP1/2 – or Set-NetIPAddress ).

Add the DHCP role: 

Add-WindowsFeature DHCP

Create a scope: 

Add-DhcpServerv4Scope –StartRange 192.168.1.5 –EndRange 192.168.1.254 –SubnetMask 255.255.255.0 –Name “192.168.1.x” –State Active

Believe it or not.  That is it.  Just create a VM and test it.

Mind you, this is just a DHCP server, I did not do any Active Directory integration or extra DHCP custom scope configuration.  I figure that will come in due time.

Okay, lets do a couple settings, especially for Provisioning Server fans.

Set-DhcpServerv4OptionDefinition –OptionId 66 –DefaultValue 192.168.1.2

Set-DhcpServerv4OptionDefinition –OptionId 67 –DefaultValue ARDBP32.BIN

Lets suppose you want to change the IP range.  First you must know the ScopeId, as that is required by the Set-

Get-DhcpServerv4Scope

Set-DhcpServerv4Scope –ScopeId <> –StartRange 192.168.1.100 –EndRange 192.168.1.254

If you want the DHCP server to respond to both DHCP and BOOTP then you can change that option as well:

Set-DhcpServerv4Scope –ScopeId <> –Type Both

Wednesday, August 8, 2012

Handling Import-VM errors in Server 2012 Hyper-V

Import-VM.  A great cmdlet for Hyper-V.

Couple that being able to import a VM that was not exported and you have a golden feature.

And if all the stars align it works like a charm.  Also notice that in the GUI you import a path, here you import a configuration file.

But, what if you have problems to fix?  It isn’t as straightforward.  You have to fix the source VM to match your host.

Even though there is an example under Get-Help it isn’t easy to visually parse and is difficult to follow, so lets add some color and additional explanation.

 

I have a VM.  That VM was running on Hyper-V Server X and I export a copy and move it to Hyper-V Server Y.

These servers are not set up the same, they are in different environments.

I try Import-VM and get the following results:

Import-VM -Path 'C:\users\Public\VmPxe\Virtual Machines\339C3412-E83F-4EA7-9DE6-F9D388B196E2.XML'

Import-VM : Unable to import virtual machine due to configuration errors.
Please use Compare-VM to repair the virtual machine.
At line:1 char:1
+ Import-VM -Path 'C:\users\Public\VmPxe\Virtual
Machines\339C3412-E83F-4EA7-9DE6- ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
    + CategoryInfo          : NotSpecified: (:) [Import-VM], VirtualizationOpe
   rationFailedException
    + FullyQualifiedErrorId : Microsoft.HyperV.PowerShell.Commands.ImportVMCom
   mand

Not that error are easy to understand in PowerShell and your mileage varies by the folks that wrote a set, but I at least know that something is wrong and I have another cmdlet to find out what.  Fair enough.

Compare-VM -Path 'C:\users\Public\VmPxe\Virtual Machines\339C3412-E83F-4EA7-9DE6-F9D388B196E2.XML'


VM                 : Microsoft.HyperV.PowerShell.VirtualMachine
OperationType      : ImportVirtualMachine
Destination        : BJELKS4
Path               : C:\users\Public\VmPxe\Virtual
                     Machines\339C3412-E83F-4EA7-9DE6-F9D388B196E2.XML
SnapshotPath       : C:\users\Public\VmPxe\Snapshots
VhdDestinationPath :
VhdSourcePath      :
Incompatibilities  : {33012, 33012}

Excellent, I have incompatibilities 33012, twice.  What is that all about???  I guess that I need to capture that report to see the errors as I can see that it is in an array ( the {} told me so ).

$compareResult = Compare-VM -Path 'C:\users\Public\VmPxe\Virtual Machines\339C3412-E83F-4EA7-9DE6-F9D388B196E2.XML'

$compareResult.Incompatibilities

Message                                     MessageId Source
-------                                     --------- ------
Could not find Ethernet...                      33012 Microsoft.HyperV.Power...
Could not find Ethernet...                      33012 Microsoft.HyperV.Power...

$compareResult.Incompatibilities[0].Message
Could not find Ethernet switch 'VMs'.

Okay, some useful information.  Now, I need to fix that.  But how?  I have a Message, MessageId, and a Source.  I bet the Source is a Source object that can be acted upon. <light bulb on>

Lets just try to disconnect the VM from the virtual switch, but how? 

If I look at the –Examples under Get-Help I scan through and see that I can take this $compareResult and act upon the .Source objects.  What I have declared as variable $compareResult MSFT refers to as a “Compatibility Report”.  But the important part is that it is more than a report.

That array of objects behind $compareResult.Incompatibilities are actually objects.  They are references to the objects that are having problems.  Even though it began as an XML file.

In my case these are the VMNetworkAdapter objects of the VM that I am attempting to import.  So I can’t search on the name of the VMNetworkAdapter and are forced to work through the list of objects.  At this time it is a kind of temporary VM that is in a state of limbo, it is declared but not committed.

Lets work through and fix the VM that I am attempting to import.

$compareResult.Incompatibilities[0].Source | Disconnect-VMNetworkAdapter

Okay, that Disconnected one of the VMNetworkAdapaters, but I want to connect the other one to the proper Virtual Switch.

$compareResult.Incompatibilities[1].Source | Connect-VMNetworkAdapter -SwitchName "Virtual Network0"

compare-VM -CompatibilityReport $compareResult


VM                 : Microsoft.HyperV.PowerShell.VirtualMachine
OperationType      : ImportVirtualMachine
Destination        : BJELKS4
Path               : C:\users\Public\VmPxe\Virtual
                     Machines\339C3412-E83F-4EA7-9DE6-F9D388B196E2.XML
SnapshotPath       : C:\users\Public\VmPxe\Snapshots
VhdDestinationPath :
VhdSourcePath      :
Incompatibilities  :

Okay, looking good, no errors.  No what?  How do I import this silly Compatibility Report that I just modified?

Well, the option is there.

Import-VM -CompatibilityReport $compareResult

Name  State CPUUsage(%) MemoryAssigned(M) Uptime   Status
----  ----- ----------- ----------------- ------   ------
VmPxe Off   0           0                 00:00:00 Operating normally

My only big comment on this is that I have to act upon each individual .Incompatibilities.Message in the array on a case by case basis.  That is a bit of a pain.  And it took a while.  Especially because you have to be a bit familiar with the available PowerShell cmdlets to know what you might be able to apply to any particular object.

Seems that I need a script to automatically manage the incompatibilities…

Wednesday, August 1, 2012

My Windows Network Virtualization demo script

After all this work with WNV I wrapped everything up with a lunch time training session covering the entire topic.

Quite honestly, it is a big concept to get across in 1 hour, wrapping the brain around the management IP, the PA addresses, the CA addresses and getting that all straight is the most confusing part – the first time someone has this tossed at them.

Once folks get those three items all sorted out it all seems to fall into place naturally.

The other idea is that fact that you are doing demonstrations and presentations as if you were a management layer.  Not using a management layer, but you ARE the management layer.  You have to do everything that you would expect a SCVMM / CloudStack / vCenter to normally be doing on your behalf.

Here are the assumptions for my demo:

Two 2012 Hyper-V Servers, four VMs.  The VMs are Red11, Red22, Blue11, and Blue22.  Red and Blue represent different Customers / Tenants.  They are bringing their systems to my infrastructure and I will be hosting them on my two Hyper-V Servers.

Setup:

The VMs should have their IPs manually set in the following way and have PING enabled in the firewall.

  • Red11 = 192.168.1.11, mask 255.255.255.0, gateway 192.168.1.1
  • Red22 = 192.168.1.22, mask 255.255.255.0, gateway 192.168.1.1
  • Blue11 = 192.168.1.11, mask 255.255.255.0, gateway 192.168.1.1
  • Blue22 = 192.168.1.22, mask 255.255.255.0, gateway 192.168.1.1

The Hyper-V Servers should have one External Virtual network, with the VMs attached.  The Hyper-V Servers need to be joined to the same domain with Migration enabled (we will be moving one VM).  All VMs should be created on one of the Hyper-V Servers, this is also the Hyper-V Server where you will run the script.

And, the last point.  When you prepare the demo to run, be sure to power on all VMs first, but power on the Red VMs first, then the Blue VMs.  Red was on-boarded into my infrastructure first, and Blue’s VMs will catch an IP conflict.

Start-VM Red* -AsJob
sleep 60
Start-VM Blue* –AsJob

Once they are all running and report an IP address to the host, we are ready to go.

$vmNics = Get-VMNetworkAdapter *
do {
    $upCount = ($vmNics | where {$_.IPAddresses.count -ne 0})
    $upCount.Count
    sleep 30
}
until ($vmNics.Count -eq $upCount.Count)

At this time you can begin running through the following scenario, one command set at a time.  I used the PowerShell ISE and selected a block and then ran that block (F8).  If you want to make all of these commands into one-liners then you can use Start-Demo.ps1.

Here is the meat, with all my commentary in the comments.  Taking all the previous 6 posts and rolling them into something to demonstrate being the management layer.

<#
.SYNOPSIS
   This is the scripted demonstration. Of Windows Network Virtualization
.DESCRIPTION
   This assumes that there are two Server 2012 Hyper-V machines, joined to the
   same domain.  Both hypervisors have one External Virtual Switch.
   This script is executed on one hypervisor, and has portions against the
   remote hypervisor.
   That local hypervisor has 4 (Server 2012) VMs named Red11, Red22, Blue11, Blue22.
   On the remote hypervisor I used a PowerShell dedicated endpoint created
   to work around CredSSP issues -
http://blogs.msdn.com/b/taylorb/archive/2012/03/26/remote-administration-with-powershell-3-0-sessions-part-1.aspx
.EXAMPLE
   For the demonstration the PowerShell v3 ISE was used to execute individual blocks of
   commands.
.AUTHOR
   Brian Ehlert
   Senior Software Test Engineer 2
   Citrix Labs, Redmond
#>

# Open a session to my remote host that I will simply re-use
$waldorf = New-PSSession waldorf.brianeh.local -ConfigurationName HVRemoteAdmin
Clear-Host

# This is just a demo, no fancy stuff.  Everything is hard coded.
# Fancy scripting is for other examples.

# Red and Blue are customers that want me to host their VMs.

# There are four VMs Red1 Red2 Blue1 Blue2. (they need to live on one host and get along)

# They happen to have overlapping IP address schemes of 192.168.1.x

# IP Conflict - second powered up / set will not work. The OS auto disables and APIPA
Get-VMNetworkAdapter * | Format-Table VMName, Name, MACAddress, VirtualSubnetID, IPAddresses -AutoSize


# I could open a ticket with the Network folks to build a VLAN. This will take too long.

# I could force one of the clients to reconfigure their IP scheme. This is too complicated.

# Instead, I have set unique VirtualSubnetIDs for each customer (isolation boundary)

Get-VMNetworkAdapter Red* | Set-VMNetworkAdapter -VirtualSubnetId 445566
Get-VMNetworkAdapter Blue* | Set-VMNetworkAdapter -VirtualSubnetId 7788990

Get-VMNetworkAdapter * | Format-Table VMName, Name, MACAddress, VirtualSubnetID, IPAddresses -AutoSize

# IP Conflict is resolved.

# To clear the APIPA status and the default behavior of taking the conflicting IP offline:
Get-VMNetworkAdapter -VMName Blue* | Disconnect-VMNetworkAdapter
Get-VMNetworkAdapter -VMName Blue* | Connect-VMNetworkAdapter -SwitchName VMs

Get-VMNetworkAdapter * | Format-Table VMName, Name, MACAddress, VirtualSubnetID, IPAddresses -AutoSize


# Start a ping from Red11 to Red22
vmconnect Statler.brianeh.local Red11

# This works until I have to distribute VMs because one host is not enough capacity to carry everything for both customers.

Move-VM -Name “Red22” -DestinationHost Waldorf.brianeh.local –IncludeStorage –DestinationStoragePath D:\Red22

# ping is now broken as there is no way to route this additional tag

# So I set up Windows Network Virtualization


# Enable the WNV binding
# At my local host
$vSwitch = Get-VMSwitch -SwitchType External
Enable-NetAdapterBinding -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription -ComponentID "ms_netwnv"

# At my Remote host
Invoke-Command -Session $waldorf {
$vSwitch = Get-VMSwitch -SwitchType External
Enable-NetAdapterBinding -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription -ComponentID "ms_netwnv"
}


# Define the Provider Address in my address space
# At my local host
$vSwitch = Get-VMSwitch -SwitchType External
$swPhysIf = Get-NetAdapter -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription
New-NetVirtualizationProviderAddress -InterfaceIndex $swPhysIf.InterfaceIndex -ProviderAddress 172.16.0.1 -PrefixLength 21

# At my Remote host
Invoke-Command -Session $waldorf {
$vSwitch = Get-VMSwitch -SwitchType External
$swPhysIf = Get-NetAdapter -InterfaceDescription $vSwitch.NetAdapterInterfaceDescription
New-NetVirtualizationProviderAddress -InterfaceIndex $swPhysIf.InterfaceIndex -ProviderAddress 172.16.0.2 -PrefixLength 21
}


# Generate a GUID for each customer
$redGUID = [system.guid]::newguid()
$blueGUID = [system.guid]::newguid()

# Format the GUID string properly
$redGUID = "{" + [string]$redGUID + "}"
$blueGUID = "{" + [string]$blueGUID + "}"

# Define a Customer Route for each customer
# A the local host
New-NetVirtualizationCustomerRoute -RoutingDomainID $redGUID -VirtualSubnetID 445566 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255
New-NetVirtualizationCustomerRoute -RoutingDomainID $blueGUID -VirtualSubnetID 7788990 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255

# At the remote host
Invoke-Command -Session $waldorf -ArgumentList $redGUID, $blueGUID {
Param($redGUID, $blueGUID)
New-NetVirtualizationCustomerRoute -RoutingDomainID $redGUID -VirtualSubnetID 445566 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255
New-NetVirtualizationCustomerRoute -RoutingDomainID $blueGUID -VirtualSubnetID 7788990 -DestinationPrefix "192.168.1.0/24“ -NextHop 0.0.0.0 -Metric 255
}


# Define the Lookup Routes on all switches that require it
# First local
New-NetVirtualizationLookupRecord -VMName Red11 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.11 -MACAddress 00155D002009 -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.2 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Blue11 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.11 -MACAddress 00155D00200B -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID
New-NetVirtualizationLookupRecord -VMName Blue22 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.22 -MACAddress 00155D00200A -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID

# Then remote
Invoke-Command -Session $waldorf -ArgumentList $redGUID, $blueGUID {
Param($redGUID, $blueGUID)
New-NetVirtualizationLookupRecord -VMName Red11 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.11 -MACAddress 00155D002009 -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.2 -Rule TranslationMethodEncap -CustomerID $redGUID
New-NetVirtualizationLookupRecord -VMName Blue11 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.11 -MACAddress 00155D00200B -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID
New-NetVirtualizationLookupRecord -VMName Blue22 -VirtualSubnetID 7788990 -CustomerAddress 192.168.1.22 -MACAddress 00155D00200A -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $blueGUID
}

# Ping is working again

# Take a look and the LookupRoutes
Get-NetVirtualizationLookupRecord | ft CustomerAddress, CustomerID, Provideraddress, macaddress, rule, vmname -AutoSize

# Break one of the routes, this can be due to a VM HA event, or a mis-configuraiton.
Remove-NetVirtualizationLookupRecord -CustomerAddress 192.168.1.22 -VMName Red22
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.1 -Rule TranslationMethodEncap -CustomerID $redGUID

# Now the route is incorrect, what happens? (check ping)

# Repair the route

Remove-NetVirtualizationLookupRecord -CustomerAddress 192.168.1.22 -VMName Red22
New-NetVirtualizationLookupRecord -VMName Red22 -VirtualSubnetID 445566 -CustomerAddress 192.168.1.22 -MACAddress 00155D002008 -ProviderAddress 172.16.0.2 -Rule TranslationMethodEncap -CustomerID $redGUID