Saturday, February 18, 2017

Changing Nano back to DHCP

I've been playing around some more with Nano server, but I didn't want to create yet another VM, so I decided to use one of my existing Nano servers. But it had an IP set already, and was set to an Internal switch (so no internet connectivity possible). So let's PowerShell-fix this!

First let's change the adapter of my VM called nano1:

Get-VM nano1 |Get-VMNetworkAdapter|Connect-VMNetworkAdapter -SwitchName external

There, that didn't hurt so much! Good, now let's change the settings inside the nano1 VM, to set the IP's back to DHCP. First, this very cool thing in Hyper-V called PowerShell Direct, allows me to enter the VM without needing any IP connectivity. All you need is the credentials of the VM, and to select the "-VMName" option with Enter-PSSession:

$cred = Get-Credential
Enter-PSSession -VMName nano1 -Credential $cred

This is what it looks like:

PS C:\> $cred = Get-Credential
Enter-PSSession -VMName nano1 -Credential $cred
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
[nano1]: PS C:\Users\administrator\Documents>

As Montgomery Burns says: "Excellent...": Did you notice the [nano1] in front of the last prompt? We're inside the VM! BTW, you don't need to add the credential, it will ask you for the credentials if you don't add them, but if you go in and out more than once, this is nicer.

Now that we're in a session, let's change the IP address and DNS back to DHCP enabled:

Get-NetIPAddress -InterfaceAlias "Ethernet" | Set-NetIPInterface -Dhcp Enabled
Set-DnsClientServerAddress -ResetServerAddresses -InterfaceAlias Ethernet

Done!

Sunday, February 12, 2017

Linked clones in Hyper-V


I've allways been a fan of VMware Workstation, but I've been spending a bit of time with Hyper-V on Windows 10 now too to see how the cookies are on the other side. One thing I thought I missed was creating what is called linked clones in VMware Workstation.



Linked clones allow a user to create multiple VM's that are referenced off of only one VM. The changes compared to the first VM are stored in a so called delta file under the covers. This way, you can build a large lab while needing a lot less space. You will however need access to the original VM files.


It turns out Hyper-V has the same functionality, but the steps to create a linked clone are a bit more hidden, and the operations are a bit more manual. The steps are as follows:
  • You need a template VM to reference your linked clones from. No different from VMware Workstation. The difference though, is that with VMware Workstation, you can keep working with this VM. With Hyper-V, this template is going to be left as an image on your harddrive, but you can't use it.
  • Sysprep the template VM. Theoretically you should do that in VMware Workstation too, but if you have some quick test you want to do and don't require domain functionality this could be omitted.
  • Turn off the VM, and mark the vhdx file of the VM as read-only. You can even remove that VM out of Hyper-V, so you don't accidentally turn it back on again
  • In Hyper-V, go to Action->New->Hard Disk. Select "Differencing Disk" as harddisk type, set the destination location and select the vhdx from the VM you created earlier.
  • Finally, create a new VM and select the newly created differenced disk instead of a new harddisk.
A lot of steps for what is relatively simple wizard in VMware Workstation. I think we can do better than that ;-) Powershell to the rescue! In the case below I've been lazy, and created a function that has paths and memory and such hardcoded inside, but the idea is what counts:


  • You create a differencing disk with New-VHD
  • You create the VM
  • You add the harddisk

$Template = "C:\VM\2012 Template.vhdx"


function New-CloneVM
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $Template,
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $NewVM
    )

    $destdisk = “C:\VM\$NewVM.vhdx”
    
    New-VHD –ParentPath $Template -Path $destdisk  –Differencing
    New-VM  $NewVM -MemoryStartupBytes 2048MB -SwitchName VLAN2 -Generation 2 -BootDevice CD
    Add-VMHardDiskDrive -VMName $NewVM -Path $destdisk -ControllerType SCSI
}


New-CloneVM -template $Template -NewVM Test1



If you quickly want to create a bunch of VM's for your testlab, Powershell and Hyper-V are a good alternative to VMware workstation, and in fact, since VMware Workstation doesn't support Powershell, Hyper-V might even be better.. I know, I know. blasphemy.

Wednesday, February 8, 2017

Turning on nested virtualization Hyper-V

I've been playing around with Hyper-V these past few weeks, and it seems to work rather well (VMware needs to start worrying).

One of the features I've been playing with is nested virtualization. Unlike VMware Workstation where you can select Hyper-V as one of  the install options, in Hyper-V on Windows 10 and Windows 2016, you can turn it on on a per VM basis, with the following command:

Set-VMProcessor -VMName NestHostVM -ExposeVirtualizationExtensions $true

Now you can have a Hyper-V inside your Hyper-V. I'm so hyped about this! <drumroll>

To get a list of which VM's have it turned on, do:

Get-VMProcessor -VMName * |select VMName, ExposeVirtualizationExtensions

Sunday, January 29, 2017

F5 management through PowerShell

I've mentioned in my vCPU upgrade article that one of the requirements of me being allowed to shut down the VM was to be sure that there were no connections on the F5 loadbalancer for the webserver.

So I needed to script against the F5 loadbalancer. The first thing you need to do, is to get the F5 PowerShell Snapin from the F5 site. An account can be made for free, then you can login and download the plugin and follow the instructions to get it installed.

Once installed, you can add the snapin with:

Add-PSSnapIn iControlSnapIn

Now you have commands to manage the F5. Great, now it's time to login. First you need to add some credentials to a variable, and I've mentioned this in a previous article too. After that, you can set up the connection to the loadbalancer:

$creds = Import-CliXml -Path "D:\akos\LB01.Cred"
Initialize-F5.iControl -HostName "192.168.1.10" -Credentials $creds

Great, now you have the power to enable and disable nodes within the loadbalancer pool with actions like:

Disable-F5.LTMNodeAddress -Node "10.0.20.3"

and

Enable-F5.LTMNodeAddress -Node "10.0.20.3"

Now disabling and enabling is fine, but knowing if there's really no connections left is what you want to have. Luckily the Internet is a great place for info, and someone on Stackoverflow created the following excellent code, which I adapted a little to suit my needs. These two excellent functions do exactly what I want it to do, namely wait around for connections to drop (with a max time, don't want to spend waiting forever), and a supporting function to get the number of connections:

function WaitForConnectionsToDrop(
    [int]$MaxWaitTime = 300,
    [string]$Node
)
{
    $connections = GetCurrentConnections -Node $Node

    $elapsed = [System.Diagnostics.Stopwatch]::StartNew();
    while($connections -gt 0 -and $elapsed.ElapsedMilliseconds -lt ($MaxWaitTime * 1000)){        

        Start-Sleep -Seconds 10

        $connections = GetCurrentConnections -Node $Node
    }
}

function GetCurrentConnections(
    [string]$Node
)
{
    $ic=Get-F5.iControl
    $connections = $ic.LocalLBNodeAddress.get_statistics($Node) | foreach{$_.statistics.statistics | where {$_.type -eq "STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS"} | foreach{$_.value.low} }
    Write-Host "$Node has $connections Connections"
    return $connections
}

(note, that last few lines are broken up due to the format of the blog, it should be one line)
The great thing is, now you can easily do the following:

foreach ($node in $serverlist){
    Disable-F5.LTMNodeAddress -Node "$node"
    WaitForConnectionsToDrop -Node "$node" -MaxWaitTime 300
    #insert whatever code you want to do, like, say upgrade vCPU's, or patch the host
    Enable-F5.LTMNodeAddress -Node "$node"
}

As you can tell, it is easy to create a script with this info that will easily disable and enable nodes. Yeay, PowerShell!

Saturday, January 28, 2017

Adding a Windows Nano server to the domain


In my previous article, I created a Nano server image. This one is about adding the Nano server to a domain. Adding Nano servers to a domain isn't a pretty little single cmdlet like many others are, but consists of a list of things to do:

  • Configure DNS on the Nano server
  • Create what's called a blob file on a DC (or a machine that is joined to the domain, and a user that has rights to add a computer to the domain or as a Domain admin user)
  • Copy the file over to the Nano server
  • Use the blob file to add the server to the domain
  • Reboot
I've found the following code on petri.com site, with an ingenious way to copy the blob file over. If you have the Storage package installed, you can of course just copy it using a share like \\nanosrv1\c$


#Set dns for Nano server
netsh interface ip set dnsservers name="Ethernet" static 10.0.0.4 primary


#Create domain blob file
djoin.exe /provision /domain mydomain.int /machine nanosrv1 /savefile c:\temp\odjblob


#get file over to nano server, needs trustedhosts first
Set-Item WSMan:\localhost\Client\TrustedHosts  "10.0.0.5" -Concatenate


#trick from https://www.petri.com/join-windows-server-2016-nano-domain
#to copy the blob over to the target server
$filePath = 'c:\temp\odjblob'
$fileContents = Get-Content -Path $filePath -Encoding Unicode
$session = New-PSSession -ComputerName 10.0.0.5 -Credential nanosrv1\username
Invoke-Command -Session $session -ArgumentList @($filePath,$fileContents) -ScriptBlock {
    param($filePath,$data)
    New-Item -ItemType directory -Path c:\temp
    Set-Content -Path $filePath -Value $data -Encoding Unicode
}


#Now add the nano to the domain with the blobfile
djoin /requestodj /loadfile c:\temp\odjblob /windowspath c:\windows /localos
shutdown /r

It is a bit convoluted, but once you get the hang of it, it is doable..

Creating a Nano Server Image

Installing Windows 2016 Nano server isn't simply adding the DVD and clicking next-next-finish, but it needs some preparation. The base nano image wim file is only 168MB, but that has no functionality in itself. More needs to be added through features, drivers and packages.

From a management workstation, mount the Windows 2016 ISO (i.e. doubleclick it, take note of the driveletter). On the ISO file you will see a NanoServer directory:


That directory has the NanoServer.wim file, Packages folder and a NanoServerImageGenerator folder. That last folder contains a PowerShell Module, which you need to import in an Administrator PowerShell window (which in my case is on the E: drive):

Import-Module E:\NanoServer\NanoServerImageGenerator\NanoServerImageGenerator.psm1

Now you should have a bunch of extra commands available:

PS C:\Users\Akos> get-command -Module *nano* | select Name

Name                
----                
Edit-NanoServerImage
Get-NanoServerPackage
New-NanoServerImage  

The New-NanoServerImage cmdlet is the one you will need to create a fully functional image. The options are a slew of information. I've created a splatted variable set below, otherwise the line becomes far too long for the blog ;-)

$Pwd = ConvertTo-SecureString -String "Pa$$w0rd!" -AsPlainText -Force

$Options = @{
    MediaPath = 'E:' #Location of DVD
    BasePath = 'D:\TMP\Base' #Location of install files
    TargetPath = 'D:\TMP\NanoWeb01.vhdx' #Where the vhdx will come
    ComputerName = 'NanoWeb01' #No idea, I think the computername..
    AdministratorPassword = "$Pwd" #A secure string password
    DeploymentType = 'Guest' #This will make it a vhdx file
    Edition = 'Standard' #Windows version (Standard or Datacenter)
    Package = 'Microsoft-NanoServer-IIS-Package' #Packages, in this case IIS
}

New-NanoServerImage -EnableRemoteManagementPort -Storage @Options

This example creates a VM called NanoWeb01, with a password of "Pa$$w0rd!", and has IIS installed. The switch "-EnableRemoteManagementPort" does what it says. You can then connect to it via PSRemoting. The switch "-Storage" allows the Nano server to have filesharing available. If you don't need that, you can let it go, but for testing it's nice to have. This VM is ready for Hyper-V, and is about 560MB as it is configured like this.

There are several packages you can choose from, which can be found by the Get-NanoServerPackage cmdlet:

PS C:\Users\Akos> Get-NanoServerPackage -MediaPath e:
Microsoft-NanoServer-Compute-Package
Microsoft-NanoServer-Containers-Package
Microsoft-NanoServer-DCB-Package
Microsoft-NanoServer-Defender-Package
Microsoft-NanoServer-DNS-Package
Microsoft-NanoServer-DSC-Package
Microsoft-NanoServer-FailoverCluster-Package
Microsoft-NanoServer-Guest-Package
Microsoft-NanoServer-Host-Package
Microsoft-NanoServer-IIS-Package
Microsoft-NanoServer-OEM-Drivers-Package
Microsoft-NanoServer-SCVMM-Compute-Package
Microsoft-NanoServer-SCVMM-Package
Microsoft-NanoServer-SecureStartup-Package
Microsoft-NanoServer-ShieldedVM-Package
Microsoft-NanoServer-SoftwareInventoryLogging-Package
Microsoft-NanoServer-Storage-Package

Next to this there are other switches too, of which a complete list and more info can be found on Microsoft's site. In all, Nano server takes a little work to get it running, but once it does, it starts in seconds.

Edit: You may have noticed the -Storage and -EnableRemoteManagementPort switches in my New-NanoServerImage command. I just saw in a different article, you can put them in the splatted options as well, by doing "Storage = $true" and the same for EnableRemoteManagementPort. You learn something new every day..

Thursday, January 26, 2017

Getting rid of trailing spaces in CSV's with Powershell

There I was, banging my head against the table because I thought I was perfectly able to select stuff from a CSV file, but I could not figure out why my selections weren't showing the stuff I wanted. It turns out my CSV file entries were full of trailing spaces!

Googling around, I saw this in a forumpost somewhere, but to prevent me from losing it, I've put the script below:

$CSV = import-csv  'C:\scripts\trailingspaces.csv'
        $CSV | Foreach-Object {  
            $_.PSObject.Properties | Foreach-Object { $_.Value = $_.Value.Trim() }
        }

$CSV | ConvertTo-Csv -UseCulture -NoTypeInformation | Out-File 'C:\scripts\trimmed.csv'

Yeay, no more going mad!

Sending emails

Short one for my own recollection. Sending emails is easy from within PowerShell

Send-MailMessage -From "fromthevcenter@whatever.com" -To "first.email@someaddress.com", "second.email@someaddress.com" -SmtpServer "thesmtpaddress" -Subject "VM Expansion $VM Completed" -Body $body

This was one of my requirements of my other post about upgrading the vCPU's ;-)

Wednesday, January 25, 2017

Store credentials securely for later use

A command you use quite often is Get-Credential. For instance for creating a PSSession to a server:

$cred = Get-Credential "admin"
$s = New-PSSession -Computername myserver -Credential $cred

It's nice for on the spot use, but in a script that might not work. There are ways of doing that plaintext, but I saw a webpage that unfortunately I don't have the link for anymore, but if I find that reference I will add, but you can store those credentials securely too:

$Credential = Get-Credential
$Credential | Export-CliXml -Path "D:\akos\Myserver.Cred"

This small piece of code lets you store credentials in a directory that you can secure with NTFS rights, and also, it is encrypted, so you don't see the password in plain text in your script.

Now if you want to use those credentials again, you can just load in the file:

$cred = Import-CliXml -Path "D:\akos\Myserver.Cred"

Very nice!!

Waiting for a VM to come back

In my previous post, I wrote about upgrading vCPU's, and that I want to do several other things as well. If you upgrade a vCPU on a VM, it could very well be that the Windows guest wants a second reboot. I've found that simply rebooting and waiting for a while is inefficient. So I usually check if the VMware tools are back online.

I've made that into a function, to exactly that:

function WaitforVM($VM)
{
    while (((get-vm $VM ).ExtensionData.Guest.ToolsRunningStatus ) -ne "guestToolsRunning" )
        {
            Write-Host "Waiting for machine to come back....." -ForegroundColor Yellow
            Sleep 5
        }
}

Now all you have to do, is to start the VM after the CPU addition, wait, then restart and wait again:

Start-VM -VM $VM -Confirm:$False
WaitforVM -VM $VM
Restart-VMGuest -VM $VM -Confirm:$False
WaitforVM -VM $VM

Nice, right? Obviously this function can be used for other things, like being used in scripts when you create a new VM from template and boot it up to see if that VM is live already.

Automatically upgrade vCPU's

I am lazy. Well, not lazy, I just don't like working at ungodly hours. Here's the story: I got this request to upgrade 2 customer VM's from 3 vCPU's to 4 vcpu's. No biggy, I thought. But he wanted it done after 1 AM.. Wait, that's not during office hours!

OK, I could fix that with my trusty Powershell toolkit, and a scheduled task. But then I talked to the customer who went like: "Sure that's fine with me to script it, but make sure the sessions are drained from the webserver".. Hmm, F5 loadbalancers. I've not worked too much with those before, really. I get the general ideas within loadbalancing, but let's try and script against it.

So I had a number of things I wanted to happen:
  • Log in to the F5 loadbalancer, preferably not with a script filled with credentials hardcoded into it. 
  • Wait an X amount of time for connections to drop from the specific pool
  • Shut down the machine, and wait for it to be properly turned off.
  • Set the number of CPU's and memory. This had an extra challenge, namely that the person who built the VM put in 1 vCPU with 3 cores, instead of 3 vCPU's.
  • Send an email with the results, so I can see what happened from my phone.
I'll spend a few posts on those different things.

Firstly the vCPU upgrade, which is the basis of this entire story:

Normally, you can turn off a VM, upgrade vCPU's, then start a VM again, through this simple set of commands:

$VM=Get-VM -Name 'WEB01'

Stop-VMGuest –VM $VM –Confirm:$False
do {
        $status = (get-VM $VM).PowerState
    }until($status -eq "PoweredOff")

$VM | Set-VM -NumpCPU 4 –Confirm:$False | Start-VM

However, since someone used 1 vCPU with multiple cores instead of multiple vCPU sockets, things work a little differently. If you would try this command, the VM would be shut down, but no upgrade would happen, and start back up with the same amount as before.

Fortunately there's another trick for that:

$VMSpec = New-Object -Type VMware.Vim.VirtualMachineConfigSpec -Property @{"NumCoresPerSocket" = 4;"numCPUs" = 4}
$VM.ExtensionData.ReconfigVM_Task($VMSpec)

(That first line shouldn't be cut off like that, should be 1 line, but alas: blog template gets in the way)

Now the VM gets upgraded to 4 vCPU's, although you would think it'd be 4x4 cpu's looking at the syntax.

So now the final complete code would be:

Add-PSsnapin VMware.VimAutomation.Core
Connect-VIServer myVcenter -ErrorAction Stop


$VM=Get-VM -Name 'WEB01'


Stop-VMGuest –VM $VM –Confirm:$False
do {
        $status = (get-VM $VM).PowerState
    }until($status -eq "PoweredOff")


$VMSpec = New-Object -Type VMware.Vim.VirtualMachineConfigSpec -Property @{"NumCoresPerSocket" = 4;"numCPUs" = 4}


Start-VM $VM


Yeay, success! Now on to the next bit...