Saturday, July 4, 2015

Announcing the winner of the Powershell Oneliner Contest 2015

A week ago I invited the Powershell Community to take part to a Powershell Oneliner Contest. People joined from across the world in search of glory and of riddles that could put them at wits' end. Today a winner has been selected:

Here's the top 10 oneliner warriors in the contest:

1st - Johannes Rössel (aka Joey)
2nd - John Roos

3rd - Simon Wåhlin
4th - Simon Walsh
5th - Bartek Bielawski
6th - Nathan Hartley
7th - Kevin Marquette
8th - Max Kozlov
9th - Rhys Edwards
10th - Wes


Congratulations to Joey for winning the Powershell Oneliner Contest 2015 with the following solutions:
 
Task 1: Who's taller? - 34 chars
($n-match'\.'|sort)[-1]-replace'-'
Task 2: Can you count to five? - 8 chars
+'〹'['']
Task 3: PowerShell is the secret word - 164 chars
-join(-split'XXXXXXXXXXXX XXXXXXXXXXX XXXXXXXXXXXXXXXXXXX X XXXXXXXXXXXXXX XXXXXXXXXXXXXXX XXXX X XXXXXXXX XXXXXXXX'|%{$x=0}{[char]($_.Length+100-32*($x++%5-eq0))})

 - GUEST BLOGGER JOEY -

I am proud to announce that Joey has accepted to be our special guest blogger today. I am very honored to have you here.

Joey, tell us a bit about yourself.
 
I live in a small town in southern Germany named Tübingen (near-ish Stuttgart), working on a graph visualization (Note: We do have an awesome free graph/diagram/flowchart editor called yEd). I grew up and studied computer science with specialization in usability and UX in Rostock in northern Germany, which is a quite pretty city. This also wasn’t too long ago, so my professional experience is still quite limited. That doesn’t stop me from reading and trying to learn all kinds of new things in my spare time.

I am a daily bicycle commuter, even in winter. And I love board games.
 
How did you get to Powershell?
 
I read the announcements in the very beginning and actually found the concept quite cool. I think I played around with the beta back then, but not very productively. Around the time PowerShell v1 was current I decided on a whim to start learning it – by trying to golf Project Euler tasks. Since those tasks progress slowly in difficulty and the initial ones are trivial to solve with a bit of programming experience it’s actually quite a nice way of starting out. You don’t need to think so much about how to even solve the problem and instead can concentrate on how to express it in the language before you. And in the process you actually learn quite a few things how to use a language well. In PowerShell’s case this means for me using the pipeline as much as possible. Advice I’ve frequently given to others on Stack Overflow as well.

Eventually, although currently less active, I became involved with Pash, the open-source re-implementation of PowerShell on Mono.
 
Where does you one-liner passion comes from?

Well, I said before, I started learning the language by golfing Project Euler tasks. This eventually grew into a contest with a friend who was using Ruby. It was akin to a battle between the languages (PowerShell is better with date/time stuff, but overall golfs worse, sadly). I wouldn’t exactly narrow it down to only one-liners, though. You can golf quite well on multiple lines, too ;-)

The nice thing about golfing to learn a language is that eventually you learn every obscure little corner of the language and learn how to use it to your advantage. This also sometimes helps with real-world code in explaining surprising behavior of a particular script.
 
How did you approach Task 1?

Task 1 was the trivial one and didn’t leave that much room for varying approaches (at least not approaches that were also short). After initial confusion about the task itself it came down almost immediately to my final answer. First you need to grab all double numbers. You could do this with $n|?{$_-is[double]}, but that’s horribly long. What’s more, we actually need to iterate over the array for that to work, so that was right out.

The nicer approach here was $n-match’\.’ which exploits how comparison operators work in PowerShell: When the left operand is an array, they return all matching values instead of a boolean result. Another thing that’s at work here is that –match implicitly converts its left argument to a string – and those string conversions always use the invariant culture, thus ensuring that the decimal numbers have a dot in them. This implicit conversion is only applied during evaluation of the result, so the resulting items are still doubles, not strings.

Then we obviously needed to find the largest number. After a while of golfing you realize that there is only one way of doing that shortly and that is via sort and either using [-1] or [0], depending on whether you want the first or last result.

After that, the only thing left is trying to obtain the absolute value of the result. The obvious solution would be [Math]::Abs(), but that weighs in at 13 characters. Surely there are better options. Remember the part about implicit string conversion earlier? Turns out we can just use the –replace operator to get rid of the minus sign. Of course, in this case the result will be a string, but hey, that was allowed, so be it.

Another approach I tried, which didn’t work out was (''+($n-match'\.'|sort)[-1]).Trim('-') which has the unfortunate drawback that .Trim() needs the left operand to be a string. And there is no nice way of enforcing that here, thus requiring too many characters to fix that, pushing it to 38 – four more than my actual solution.

How did you cope with Task 2?

Task 2 had an evil restriction, in that no ASCII decimal digits were allowed in the code. This makes some things longer, of course. But then the challenge lies in trying to figure out how to avoid doing those things in the first place.

My very first attempt was abysmal: +-join([char[]]'+,-./'|%{[char](' '.Length+$_)}). 53 characters, just trying to represent the five digits by shifting their Unicode values down by six and then adding that again, converting the result into a char, then joining them into a string and converting that into an integer. Lots of conversions going on here, and the calculation isn’t pretty because I’m not allowed to use numbers (and there was no constant handy I could rely on). One thing is handy here, though, which is the leading unary + to convert the operand to a number; this will stick with us through the end.

A fun thing to try can be to just use control or otherwise unprintable characters. Heck, PowerShell strings may contain almost anything, so getting rid of part of the length above can be done simply by removing the need of adding numbers – there are Unicode characters with numbers 1 through 5. We can just use them (the following code uses the old IBM character set glyphs for those characters, so you can’t just copy the code, but you can enter those characters with Alt+01 through Alt+05): +-join[int[]][char[]]'☺☻♥♦♣'. 28 characters, just about half of the previous attempt, with essentially the same approach.

But maybe there’s another approach that cuts down on the frequent conversions here. Remember that PowerShell strings may contain any character. Maybe there is a Unicode character with number 12345? Well, sure, there is: , U+3039, Hangzhou Numeral Twenty. Now, in this context I don’t particularly care about what the character represents as long as it’s a convenient encoding for the number 12345. +[char]'〹' brings us down to 10 characters, which is much better than the previous attempts. This just leaves the pesky conversion of the string to char (PowerShell has no character literals, but you can cast and convert one-character strings to char). Well, the shortest way of casting a one-character string to char is by indexing into it, but +'〹'[0] wouldn’t be allowed. But there are quite a few things that can get coerced to the number 0 in this context: Uninitialized variables (and thus $null), $false, or, conveniently, the empty string. I’m fairly confident that it’s hard to get shorter than +'〹'['']. Unless we allow digits again.

What's your take on Task 3? It was quite of open question, wasn't it?

It was, indeed and usually I don’t like such questions too much in a contest, since they simply invite way too many non-serious answers. I’ve hosted contests myself, I also posted tasks to the Programming Puzzles and Code Golf StackExchange site. Golfing is ultimately about finding creative ways to solve a task within the confines of the task restrictions. However, in my experience too vague restrictions and conditions just invite people to be lazy and “funny” by posting joke answers one then has to sift through.

Given that you actually accepted my worst attempt at this task it seems like your interpretation of the task was more strict than mine. I interpreted the whitespace requirement way more loosely, coming up with all kinds of other solutions along the way.
As far as the task specification goes, “starting from a string” is too vague. Of course, it implies it has to be input of some sort, but I’d probably have defined it such that changing the string would have to change the output and that, depending on the string, more than one output would be possible.

So, task 3 went through quite a lot of iterations for me, most of them apparently invalid ;-). The first and accepted one was a trivial attempt: Subtract a common number from all characters in the output, then do an unary encoding of the resulting numbers and put that in the string. I chose to be clever with the two uppercase letters by conditionally adding 32, but that didn’t help too much: -join(-split'…'|%{$x=0}{[char]($_.Length+100-32*($x++%5-eq0))}) – 102 characters for the string alone (164 total), and the decoding logic is also quite long (the {$x=0} can be omitted, depending on how one’s stance on running in an unknown environment is – I chose to err on the safe side here). The part in the end can get a bit shorter with 32*!($x++%5), but unary encoding of direct character values is quite long regardless. In fact, I didn’t even bother submitting this variant.

I dabbled a bit with binary encoding and eventually hexadecimal with 15 different whitespace characters, with only minor reduction in character count. Eventually I used unary encoding of indices into a string with all the necessary characters to reduce it to 89, which probably was against your ideas of the task as well ;-).

Should I have accepted the answer "X"|%{"PowerShell"}? What is to you the spirit of a oneliner contest?

No, you shouldn’t. The spirit to me is to have fun, the other part of it is finding creative solutions around the restrictions of the tasks (if there are any). Such solutions don’t require any cognitive effort, many people will come up with the exact same one and to me they don’t represent the spirit of such contests. Now, I’ve said I hosted a few contests myself and one thing I learned is that the task specification should be very clear and, if possible, leave little room for language-lawyering that would result in solutions as the above one. In this particular case it’s difficult to specify the task where there is a clear line between solutions that are allowed and those that are not allowed. I usually try to have such a clear line, which is easiest by not including restrictions for the program at all, apart from the usual ones (e.g. putting the program in the file name and eval-ing that).

What's your opinion of loops in a oneliner... like for loops or while do until?

Well, in your particular case at least for loops weren’t allowed because of the no-semicolons rule. Generally, why not? If they make the solution shorter, I’m all for them. However, in my experience this will only be the case for the for loop which has a few opportunities for shortening that aren’t possible with pipelines. With while or do I never saw any benefit.

What's your take on Powershell for one-liners if compared to other languages you might know?

Mostly it works quite well. It’s about on par with Python, but can’t reach Ruby or Perl levels. Overall still nice for a language that wasn’t designed to be concise. The most annoying parts when golfing are the $ for variables, and forced braces around control structures like if.

Is there any oneliner-oriented community you know and would like to talk about?

Oh, well, there is the Programming Puzzles and Code Golf StackExchange site which has grown quite a bit in recent months. By now I’m rarely active there anymore, though, mostly because of a lack of time.

I have collected a few golfing tips for PowerShell there, too: http://codegolf.stackexchange.com/q/191/15

Thank you, Joey, for sharing your time and knowledge! The 'Hyper-V Best ¨Practices' book is yours!

For those wishing to contact Joey, here's where you can find him:
For those wishing to make the project Euler experience with Powershell, here's the place to go.

 - GUEST BLOGGER JOHN -

As a bonus, let me now introduce you John Roos. John came second in the contest and I got in touch with him as soon as I saw the nice Powershell V5 solution he produced for task 3. He immediately accepted to be my guest blogger today.
 

John, tell us a bit about you and show us the way to your v5 solution to Task 3.

Hi, I am currently working with Business Intelligence within Operations at H&M in Stockholm and spend a decent amount of time with PowerShell. I first started with PowerShell last year. I think it was around September when I wanted to get started with C# and stumbled upon the "Getting started with PowerShell 3.0" series on Channel 9 by Jason Helmick and Jeffrey Snover and I was hooked right away.

When I saw the third task in this contest I immediately started to think about a binary string where "X" would represent number 1 and a space would represent 0. That was my starting point.

Since I already had the result that this one-liner will produce I thought about this backwards. What number would represent a "P" and what number would be "o" and so on. The following code gets the numbers I need to have represented as binary:

[int][char]'P'
[int][char]'o'
[int][char]'w'
[int][char]'e'
[int][char]'r'
[int][char]'S'
[int][char]'h'
[int][char]'e'
[int][char]'l'
[int][char]'l'
Results:

80
111
119
101
114
83
104
101
108
108
Since I am lazy I used an online tool to convert these numbers to binary and ended up with the following:

80 = 01010000
111 = 01101111
119 = 01110111
101 = 01100101
114 = 01110010
83 = 01010011
104 = 01101000
101 = 01100101
108 = 01101100
108 = 01101100
Next is to put it all togehter in one string and then replace 1 with "X" and 0 with a space:

'01010000011011110111011101100101011100100101001101101000011001010110110001101100'.Replace(1,'X').Replace('0',' ')
Result:
' X X     XX XXXX XXX XXX XX  X X XXX  X  X X  XX XX X    XX  X X XX XX   XX XX  '
So now I had the starting string with just "X" and spaces and need to work my way back to the "PowerShell" output. The string need to be split into an array of strings with 8 characters each (one for each letter of the word). Regular expressions works well for this:

[regex]::Matches(' X X     XX XXXX XXX XXX XX  X X XXX  X  X X  XX XX X    XX  X X XX XX   XX XX  '.Replace(' ',0).Replace('X',1),'\d{8}')
Now lets pipe it further so that each binary string can be converted to [INT] (using base 2 since its binary) and then convert that to [CHAR].

[regex]::Matches(' X X     XX XXXX XXX XXX XX  X X XXX  X  X X  XX XX X    XX  X X XX XX   XX XX  '.Replace(' ',0).Replace('X',1),'\d{8}')|%{[char][Convert]::ToInt32("$_",2)}
Result:
P
o
w
e
r
S
h
e
l
l
Almost there. Now the result need to be converted from an array to a string. In Powershell 4 it doesnt really work well with string for this particular case:

[string]([regex]::Matches(' X X     XX XXXX XXX XXX XX  X X XXX  X  X X  XX XX X    XX  X X XX XX   XX XX  '.Replace(' ',0).Replace('X',1),'\d{8}')|%{[char][Convert]::ToInt32("$_",2)})
Result:
P o w e r S h e l l
I dont want those spaces between every character, but fortunately in Powershell 5 we get access to a new string method called New() which accepts an array of chars. So lets encapsulate the entire thing with this method:

[string]::new(([regex]::Matches(' X X     XX XXXX XXX XXX XX  X X XXX  X  X X  XX XX X    XX  X X XX XX   XX XX  '.Replace(' ',0).Replace('X',1),'\d{8}')|%{[char][Convert]::ToInt32("$_",2)}))
Result:
PowerShell
Now I have the answer to the question, but its 191 characters long. At this point I realised that all the binary strings start with "01". What if I remove that from the long string and add them in the loop in the end? In that case I would have to split the string with regex on only 6 chars instead of 8 and add "1" when converting back to integers (the leading zero is assumed since its binary):

[string]::new(([regex]::Matches(' X    X XXXXXX XXXX  X XXX  X  X  XXX X   X  X XX XX  X XX  '.Replace(' ',0).Replace('X',1),'\d{6}')|%{[char][Convert]::ToInt32("1$_",2)}))
The one-liner is now down to 172 characters and thats as far as I got. I tried lots of different solutions but this was the shortest I could come up with.

Thanks John for your explanation!

For those wishing to contact John Roos, here's where you can find him:
A last mention goes to a couple of fellow heavyweigth Microsoft MVPs who took part in the Contest:
  • Bartek Bielawski, who wrote a must-read blog post on his solutions. Check it here. Bartek was the winner of the Powershell Scripting Games in 2011, and he is an author at Powershell Magazine with excellent skills in Powershell golfing.
  • Emin Atac, who wrote an excellent blog post on his approach to the three tasks. Check it here. Even if Emin is not on the top-10 (just because he offered a solution to task 3 that didn't match my very subjective restrictions), this guy knows what he is talking about.
That's all for the Powershell Contest 2015. I hope you enjoyed reading the expert answers of our winners. Personally, I was very happy to host such an event and I want to thank you everyone who entered the game as well as our sponsor, Packt Publishing, for offering the prize.

Wednesday, June 24, 2015

Powershell Oneliner Contest 2015 - Win a Hyper-V book

I have always enjoyed taking part in most of the Powershell contests out there since Powershell is kind of a recreational administration language to me. I am thinking, for example, of the fun I had taking part in the oneliner contest (which I won) organized by fellow MVP Mike F Robbins, or of the Powershell Golf organized in 2013 by Robert Robelo on Twitter.
 
THE IDEAS BEHIND THE CONTEST
 
Being quite proud of my blog, and despite the fact that I am more of a system administrator than a developer, I have unilaterally decided to organize my self-hosted, self-managed Powershell Oneliner Contest.
 

The three ideas behind this game are that:
  1. experienced competitors (and I, of course!) should come away with a lot of tips from the brightest Powershell minds that will hopefully take part in the event
  2. novice Powershell scripters should learn that persistence pays off when looking for a solution to such a contest
  3. this is a fairly good occasion to sharpen your skills for the soon-to-start Powershell Scripting Games held by the Powershell.org community
GENERAL RULES
 
  • The contest is split into three tasks. Each task consists of a simple scenario for which you have to produce a oneliner solution.
  • Submit your solutions via a comment to this blog article by 11AM (PDT) on Wednesday, July 1st, which is the deadline.
  • You MUST submit only one task solution per comment, so that I can easily see who was the first to submit the shortest solution on a per-task basis.
  • Should you find a shorter oneliner to solve a task you are allowed to post additional comments (just remember to sign your comments so that I know who's who).
  • Aliases are of course accepted
  • Backticks accepted for readability
  • No semi-colons
  • UPDATE - No Here-String
  • UPDATE - Please don't sign your comments as Unknown or I won't be able to know who you are.
  • If you use a Powershell version other than v4, I am okay with it, but please mention it in the comment so that I can test it on the same version. Powershell v5 is welcome.
  • Entries (comments) will not be made public until after the submission deadline.

SCORING
  • The shortest solution in terms of chars wins.
  • The first person to produce the shortest working solutions to a task will get 1 point, the second 2 points, the third 3 points and so on.
  • The person with the lowest total score over the three mandatory tasks will be the winner.
  • The winner will be announced on Saturday, July 4th on this blog and the prize will be awarded at that time.
  • I'll be the only judge.

THE PRIZE
 
What? Is there a prize? Yes, of course. The prize will be a printed copy of the excellent book 'Hyper-V Best Practices' (5-Stars on amazon.com) by fellow MVP Benedict Berger  (@Benedict_Berger) offered by Packt Publishing!!!
I was the reviewer of the book as well of the ton of Powershell code it contains. I can tell you this is the book to read in these Hyper-V times!


As a bonus, and if the winner agrees, he/she will intervene as a guest blogger on this blog and will explain how one goes about learning the hidden bits of Windows Powershell. Sounds cool, doesn't it?

Let's now dive into the contest tasks.
 
TASK 1 - Who's taller?
 
This is a rather easy challenge and I hope it will make for a good warm-up session for your Powershell muscles, especially if you are a beginner. Given a $n variable showed below, write the shortest possible oneliner that outputs the absolute value of the largest double-precision floating-point number in the array.
$n = -1,-2,-5,-8.9,'b',-9.11,-6,-3,-2,-9.1,-1,-1.4,'a'

Expected output
Task-specific rules
  • The object in the output can be of any type
  • UPDATE - The one-liner must work also with different values of $n

TASK 2 - Can you count to five?

Time to delve into something harder. Write the shortest possible Powershell oneliner that outputs the number 12345.

Expected output
 
Task-specific rules
  • You are not allowed to use the digits 0 to 9 in your oneliner
  • The object in the output must be of type Int32
  • Your displayed output shouldn't contain any other char

TASK 3 - Powershell is the secret word

I hope that by this time you are enjoying the competition. It's time for the last hard task. Write the shortest possible Powershell oneliner that outputs the word 'PowerShell' starting from a text string composed only of uppercase Xs' and whitespaces.

Expected output
Task-specific rules
  • The string composed of X's and whitespaces must be part of the oneliner (backticks accepted!) and not be a variable defined on a separate line
  • You can use as many X's and empty spaces you like or need
  • P and S in the output must be uppercase
  • UPDATE - No Here-String
  • UPDATE - I consider jokes answers like "X "|%{"Powershell"}
On your marks, set, go! And remember to have fun, it's just a game meant to produce something useful for the growing and growing Powershell community!

UPDATE - Check-Task Function

I have decided to update this blog post with a function that will allow you to check if your proposed solutions are correct in terms of output and respect the task assignments. Kudos for this function go to my friend Joey!

function Check-Task ([int] $Task, [scriptblock] $Command) {
        $n = -1,-2,-5,-8.9,'b',-9.11,-6,-3,-2,-9.1,-1,-1.4,'a'
        $result = & $command
        $check = $false
        switch ($Task) {
            1 { $check = $result -eq '1.4' -or 
                [double]::Parse("$result") -eq 1.4 }
            2 {
                $check = ($result -is [int] -and $result -eq 12345) -and
                    ("$Command" -notmatch '[0-9]')
              }
            3 {
                $check = ($result -is [string] -and $result -ceq 'PowerShell') -and
                    ("$Command" -match '([''"])[X`\s]+\1')
            }
        }
        if (!$check) {
            Write-Host -Fore Yellow "Expected: $((1.4,12345,'PowerShell')[$Task - 1])"
            Write-Host -Fore Red    "Actual:  $result"
        }
        $color = ('Red','Green')[$check]
        Write-Host -Fore $color ('{0,4}: {1}' -f "$Command".Trim().Length,$command)
    }
Sample usage:
Check-Task 1 { $n.gettype() }
Do not hesitate to test your solutions before posting them to my blog! Better safe than sorry.

SPREAD THE WORD

If you want to give some visibility to this contest, feel free to twit about it. You can use the hashtags #poshcontest2015 and #powershell so that other competitors can share their thoughts (not the solutions of course!).

Thanks again to Packt Publishing (and to Mary in particular) for sponsoring this event!

UPDATEClick here to find the winner's name and his solutions!

Tuesday, June 2, 2015

Copy-Item improvements to support file copy over WinRM to NanoServers

If you follow this blog (you should!), then you know that I have just published a post on the procedure to install a Microsoft NanoServer.

If you read through the whole post, you know that this small footprint operating system is supposed to be managed through Powershell Remoting since it has no local logon capabilities.

Now, if you look well into the new cmdlets/parameters that come with the Windows Management Framework 5 (the one that comes with Windows 2016 TP2), you'll see that Copy-Item now supports file copy operations through WinRM.

The new key parameters are -ToSession and -FromSession.

Let's see with an example how they shoud be used., even do they are pretty self-explanatory.
 
First of all open a remoting session to the NanoServer:

$session = New-PSSession -ComputerName 192.168.1.5 -Credential administrator
Then, to copy any file to the NanoServer over WinRM, just use the following syntax:

Copy-Item -ToSession $session -Path .\samplescript.ps1 -Destination c:\temp\
The operation can be performed both directions, so to retrieve a file just use:

Copy-Item -FromSession $session -Path c:\results.txt -Destination .
If you liked this post, please share!

How to build your first fantastic Nano Server

As it has been announced in recent events such as Ignite, Microsoft is working hard for the introduction of Nano Server, a cloud-oriented, GUI-less, small-everything, Powershell-managed version of Windows Server. This server version comes with no logon capability, which means that management tasks are performed remotely, from the very first logon. And, since the system is built to be extremely light, we can suppose that it will be blatantly fast for the specific workloads that come from the devops/virtualization/containerization world.
 
NanoServer goes even further than Server Core in terms of lightness
Sounds good, you say. But how to test it? Well, today Nano Server is part of Windows 2016 Technical Preview 2, which has been announced less than a month ago and that I have had the occasion to test in one of my labs. It was a tricky job that I am going to detail as long as the issues I faced during the deployment.
 
STEP 1: PREPARE THE VHD

The starting point is the NanoServer folder stored in the Windows 2016 TP2 iso:


The folder size is a mere 181 MB, and it contains a .wim file as well as the following six file cabinets:

Directory: H:\NanoServer\Packages

Mode            LastWriteTime     Length Name
----            -------------     ------ ----
--r--    09/05/2015     03:01   10849819 Microsoft-NanoServer-Compute-Package.cab
--r--    09/05/2015     03:01    7690908 Microsoft-NanoServer-FailoverCluster-Package.cab
--r--    09/05/2015     03:01     327347 Microsoft-NanoServer-Guest-Package.cab
--r--    09/05/2015     03:01   12785830 Microsoft-NanoServer-OEM-Drivers-Package.cab
--r--    09/05/2015     03:01    6900267 Microsoft-NanoServer-Storage-Package.cab
--r--    09/05/2015     03:01      49656 Microsoft-OneCore-ReverseForwarders-Package.cab
Let's begin with building the source repository by copying the whole content of this folder:

Copy-Item H:\NanoServer\* D:\nanoserver -Recurse
Then we have to convert the WIM file contained in the Windows iso file to a VHD that we can mount in Hyper-V. This is easily achieved with the Powershell script that Pronichkin developed and that you can download from here.

Put the script in the same repository you created above:

Set-Location D:\NanoServer

.\Convert-WindowsImage.ps1 -WIM .\NanoServer.wim -VHD .\NanoServer.vhd -DiskType Fixed -VHDFormat VHD -SizeBytes 1GB -Edition 1

Windows(R) Image to Virtual Hard Disk Converter for Windows(R) 8
Copyright (C) Microsoft Corporation.  All rights reserved.
Version 6.3.9600.7.amd64fre.fbl_core1_hyp_dev(mikekol).140217-3000 Release to Web

INFO   : Image 1 selected ()...
INFO   : Creating fixed disk...
INFO   : Attaching VHD...
INFO   : Disk initialized with MBR...
INFO   : Disk partitioned...
INFO   : Volume formatted...
INFO   : Access path (D:\) has been assigned...
INFO   : Applying image to VHD. This could take a while...
INFO   : Signing disk...
INFO   : Image applied. Making image bootable...
INFO   : Fixing the Device ID in the BCD store on VHD...
INFO   : Drive is bootable. Cleaning up...
INFO   : Closing VHD...
INFO   : Closing Windows image...
INFO   : Done.
As you can see, I created a 1GB disk, and this is enough for a test environment.

STEP 2: ADD PACKAGES

Next we need to stream in some dlls and packages into the VHD.

New-Item Dism -ItemType Directory
Set-Location Dism
Copy-Item H:\Sources\api*downlevel*.dll
Copy-Item H:\Sources\*dism*
Copy-Item H:\Sources\*provider*
This will copy 30 files in your Dism folder. 
Preparing the vhd...
Now mount the image:

Mount-DiskImage D:\NanoServer\NanoServer.vhd
In my case it got mounted as G:

Then to add the packages I mentioned before (the ones under H:\NanoServer\Packages), you can leverage the Add-WindowsPackage cmdlet:

ISSUE 1: LOW LEVEL DISM AGAINST LEVEL UP IMAGE

In my case I encountered an issue with this action when I performed it on my Windows 2012 R2 server:

Add-WindowsPackage -Path G: -PackagePath *.cab
Add-WindowsPackage : To service this Windows image requires the latest version of the DISM. See http://go.microsoft.com/fwlink/?LinkId=293395 to find the latest version of DISM, 
and http://go.microsoft.com/fwlink/?LinkId=293394 to learn how to install the latest version of DISM from the ADK on your computer.
At line:1 char:1
+ Add-WindowsPackage -Path G: -PackagePath *.cab
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Add-WindowsPackage], COMException
    + FullyQualifiedErrorId : Microsoft.Dism.Commands.AddWindowsPackageCommand
In the DISM log I found the following error:

DISM Manager: PID=3092 TID=4604 It is an unsupported scenario to service an up level image using low level dism. - CDISMManager::CreateImageSessionFromLocation(hr:0x80070032)

I checked the version of the DISM cmdlets on Windows 2012 R2:

Add-WindowsPackage -Path G: -PackagePath .\Microsoft-NanoServer-Compute-Package.cab -Verbose
VERBOSE: Dism PowerShell Cmdlets Version 6.3.0.0
On Windows 10 Insider Preview we have version 10:

VERBOSE: Dism PowerShell Cmdlets Version 10.0.0.0
I tried to solve this by downloading the Windows Assessment and Deployment Kit (Windows ADK) for Windows 8.1 Update which I found here.

For your information, the adksetup.exe must be run from c:\ as an administrator otherwise it will fail with such an error:

Could not acquire privileges; GLE=0x514
Returning status 0x514

In any case it kept not working on Windows 2012 R2.

SOLUTION TO ISSUE 1: USE WINDOWS 10 DISM

The known workaround is to use DISM from either Windows 10 or Windows Server 2016 Technical Preview 2 machine. In my case I luckily had a freshly installed Windows 10 CTP2 sitting there in a VM. I just copied the vhd over there, enable the administrator account, which is needed to be able to mount the vhd file, through the command

net user administrator /active:yes

On Windows 10, Add-WindowsPackage worked flawlessly (apart for the drivers package which took much longer):
Add-WindowsPackage –Path G:\ –PackagePath C:\NanoServer\Packages\Microsoft-NanoServer-Compute-Package.cab
Add-WindowsPackage –Path G:\ –PackagePath C:\NanoServer\Packages\Microsoft-NanoServer-FailoverCluster-Package.cab
Add-WindowsPackage –Path G:\ –PackagePath C:\NanoServer\Packages\Microsoft-NanoServer-Guest-Package.cab
Add-WindowsPackage –Path G:\ –PackagePath C:\NanoServer\Packages\Microsoft-NanoServer-OEM-Drivers-Package.cab
Add-WindowsPackage –Path G:\ –PackagePath C:\NanoServer\Packages\Microsoft-NanoServer-Storage-Package.cab
Add-WindowsPackage –Path G:\ –PackagePath C:\NanoServer\Packages\Microsoft-OneCore-ReverseForwarders-Package.cab

Add-WindowsPackage in action on my Windows 10 Enterprise Insider Preview

Once all the packages are added, I dismounted the vhd image.

Dismount-DiskImage -ImagePath C:\Nanoserver\NanoServer.vhd
The vhd file is now ready.

STEP 3: CREATE A HYPER-V VIRTUAL MACHINE

One I copied the vhd back to my Windows 2012 R2 test bed I created the VM descriptors with Powershell:

New-VM -Name 'NanoServer1' -Generation 1 -MemoryStartupBytes 512MB -SwitchName LAN -Path 'N:\Hyper-V Virtual Machines\Virtual Machines\' -NoVHD
Copy-Item 'D:\NanoServer\NanoServer.vhd' -Destination 'N:\Hyper-V Virtual Machines\'
Get-VM 'NanoServer1' | Add-VMHardDiskDrive -Path 'N:\Hyper-V Virtual Machines\NanoServer.vhd'
Start-VM 'NanoServer1'
The machine booted, but the boot took almost twenty minutes. After a long wait a black screen with an underscore appeared. I didn't really expect something on the console to show, but I must admit I was unpleasantly surprised by the long boot up times.

BUG 1: LONG BOOT-UP TIME

After a long analysis and a bunch of re-deployments, I discovered that, even do the NanoServer virtual machines show the following screen, in reality they are already pingable and manageable:

Windows Nano Server is online well before the start-up is replaced by the black screen
Once Nano is up and running, the following screen appears:

The famous black screen with un underscore of a Windows NanoServer
As you can see it differs from the welcome screen of a standard Windows 2016 Technical Preview 2 installation in the fact that it lacks logon capabilities:

Windows 2016 logon screen
At this point you can connect to it remotely using PowerShell Remoting.

STEP 4: REMOTING INTO A NANOSERVER
 
From a PowerShell prompt on a management PC, run the following two commands, replacing the IP address with the one of your Nano Server (you can check this on your local DHCP server list of leases):

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 192.168.5 -Concatenate
Enter-PSSession -ComputerName 192.168.1.5 -Credential Administrator
The Set-Item cmdlet is used to add Nano server to the list of trusted hosts for Remoting.

Note that the administrator password is blank by default, so when Enter-PSSession asks for a password just hit Enter.

Once you are logged in, you can check the default name that a NanoServer is given, which is "miniwinpc".

[192.168.1.5]: PS C:\Users\Administrator\Documents> hostname
minwinpc
[192.168.1.5]: PS C:\Users\Administrator\Documents> ipconfig /all

Windows IP Configuration

   Host Name . . . . . . . . . . . . : minwinpc
   Primary Dns Suffix  . . . . . . . :
   Node Type . . . . . . . . . . . . : Hybrid
   IP Routing Enabled. . . . . . . . : No
   WINS Proxy Enabled. . . . . . . . : No

Ethernet adapter Ethernet:

   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : Microsoft Hyper-V Network Adapter
   Physical Address. . . . . . . . . : 00-15-5D-01-64-03
   DHCP Enabled. . . . . . . . . . . : Yes
   Autoconfiguration Enabled . . . . : Yes
   Link-local IPv6 Address . . . . . : fe80::b47c:4c39:433b:5628%4(Preferred)
   IPv4 Address. . . . . . . . . . . : 192.168.1.5(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Lease Obtained. . . . . . . . . . : Monday, June 1, 2015 11:07:57 AM
   Lease Expires . . . . . . . . . . : Monday, June 1, 2015 11:07:57 PM
   Default Gateway . . . . . . . . . : 192.168.1.254
   DHCP Server . . . . . . . . . . . : 192.168.1.254
   DHCPv6 IAID . . . . . . . . . . . : 67114333
   DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-1C-FE-57-D3-00-15-5D-01-64-03
   DNS Servers . . . . . . . . . . . : 192.168.1.254
   NetBIOS over Tcpip. . . . . . . . : Disabled

Tunnel adapter Local Area Connection* 2:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : Microsoft Failover Cluster Virtual Adapter
   Physical Address. . . . . . . . . : 02-92-9B-4B-EB-13
   DHCP Enabled. . . . . . . . . . . : Yes
   Autoconfiguration Enabled . . . . : Yes
Here's the output of some other generic commands on a NanoServer:
[192.168.1.5]: PS C:\> ls


    Directory: C:\


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         5/6/2015  10:05 AM                EFI
d-----         5/6/2015  10:32 AM                Program Files
d-----         5/6/2015  10:05 AM                Program Files (x86)
d-r---         6/1/2015  12:40 PM                Users
d-----         6/1/2015  11:22 AM                Windows
-a----         6/1/2015   6:44 AM            300 Convert-WindowsImageInfo.txt
[192.168.1.5]: PS C:\> Get-Process

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     94       6      596       1540 ...61     0.33    380 csrss
      0       0        0          4     0               0 Idle
    615      17     3160       9900 ...86     0.34    428 lsass
    243      10     2340       9140 ...81     0.34   1076 MsMpEng
    168       8     1452       4912 ...65     0.30    416 services
     44       3      280       1076 ...56     0.23    296 smss
    189       8     1396       5240 ...75     0.09    536 svchost
    223      12     1388       5484 ...75     0.06    576 svchost
    721      20    10732      20864 ...14     1.33    636 svchost
    354      14     3976      11628 ...26     0.13    692 svchost
    110       7     1136       5540 ...74     0.08    752 svchost
     93       6     1040       4552 ...71     0.00    768 svchost
    284      15     6972      11116 ...92     0.20    780 svchost
    209      14     2216       7180 ...80     0.00    884 svchost
    567      55     6152      15288 ...63     0.89    904 svchost
    263      26     3760       9264 ...86     0.23   1000 svchost
    329       0       76         80     2     4.16      4 System
     69       7      640       3476 ...63     0.05    404 wininit
    379      36    34160      54288 ...24     9.19   1544 wsmprovhost
[192.168.1.5]: PS C:\> gcim win32_operatingsystem | fl *


Status                                    : OK
Name                                      : Microsoft Windows Server Technical Preview 2
                                            Tuva|C:\Windows|\Device\Harddisk0\Partition1
FreePhysicalMemory                        : 3883052
FreeSpaceInPagingFiles                    : 233724
FreeVirtualMemory                         : 4188272
Caption                                   : Microsoft Windows Server Technical Preview 2 Tuva
Description                               :
InstallDate                               : 6/1/2015 11:07:56 AM
CreationClassName                         : Win32_OperatingSystem
CSCreationClassName                       : Win32_ComputerSystem
CSName                                    : MINWINPC
CurrentTimeZone                           : -420
Distributed                               : False
LastBootUpTime                            : 6/1/2015 11:07:46 AM
LocalDateTime                             : 6/1/2015 12:47:26 PM
MaxNumberOfProcesses                      : 4294967295
MaxProcessMemorySize                      : 137438953344
NumberOfLicensedUsers                     : 0
NumberOfProcesses                         : 20
NumberOfUsers                             : 0
OSType                                    : 18
OtherTypeDescription                      :
SizeStoredInPagingFiles                   : 233724
TotalSwapSpaceSize                        :
TotalVirtualMemorySize                    : 4427568
TotalVisibleMemorySize                    : 4193844
Version                                   : 10.0.10074
BootDevice                                : \Device\HarddiskVolume1
BuildNumber                               : 10074
BuildType                                 : Multiprocessor Free
CodeSet                                   : 1252
CountryCode                               : 1
CSDVersion                                :
DataExecutionPrevention_32BitApplications : True
DataExecutionPrevention_Available         : True
DataExecutionPrevention_Drivers           : True
DataExecutionPrevention_SupportPolicy     : 2
Debug                                     :
EncryptionLevel                           :
ForegroundApplicationBoost                :
LargeSystemCache                          :
Locale                                    : 0409
Manufacturer                              : Microsoft Corporation
MUILanguages                              : {en-US}
OperatingSystemSKU                        : 109
Organization                              : Microsoft
OSArchitecture                            : 64-bit
OSLanguage                                : 1033
OSProductSuite                            : 272
PAEEnabled                                :
PlusProductID                             :
PlusVersionNumber                         :
PortableOperatingSystem                   : False
Primary                                   : True
ProductType                               : 3
RegisteredUser                            : Microsoft
SerialNumber                              :
ServicePackMajorVersion                   : 0
ServicePackMinorVersion                   : 0
SuiteMask                                 : 272
SystemDevice                              : \Device\HarddiskVolume1
SystemDirectory                           : C:\Windows\system32
SystemDrive                               : C:
WindowsDirectory                          : C:\Windows
PSComputerName                            :
CimClass                                  : root/cimv2:Win32_OperatingSystem
CimInstanceProperties                     : {Caption, Description, InstallDate, Name...}
CimSystemProperties                       : Microsoft.Management.Infrastructure.CimSystemProperties
As a side note, notice that the good old Get-WMIObject (alias gwmi) is not implemented on NanoServer:

[192.168.1.5]: PS C:\>  Get-WmiObject win32_computersystem
The term 'Get-WmiObject' is not recognized as the name of a cmdlet, function, script file, or operable program. Checkhe the spelling of the name, or if a path was included, verify that the path is correct and try again.
    + CategoryInfo          : ObjectNotFound: (Get-WmiObject:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
Concerning Powershell, version 5 of the WMF is installed, exactly like on a Windows 2016:

[192.168.1.5]: PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSRemotingProtocolVersion      2.3
CLRVersion                     4.0.30319.34011
WSManStackVersion              3.0
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.10074.0
PSVersion                      5.0.10074.0
SerializationVersion           1.1.0.1
STEP 5: COMPARE POWERSHELL MODULES OF NANOSERVE AND WINDOWS 2016 TP2
 
Now it would be interesting to compare Powershell capabilities in terms of accepted cmdlets on both Windows 2016 TP2 and Windows NanoServer.

Here's what you have on a Nano Server:

[192.168.1.5]: PS C:\> Get-Command | Select Name,Module | Group Module | Sort Count -Descending

Count Name                      Group
----- ----                      -----
  140 Storage                   {@{Name=Disable-PhysicalDiskIndication; Module=Storage}, @{Name=Disable-StorageDiagn...
   92                           {@{Name=A:; Module=}, @{Name=B:; Module=}, @{Name=C:; Module=}, @{Name=cd..; Module=...
   74 Microsoft.PowerShell.U... {@{Name=Add-Member; Module=Microsoft.PowerShell.Utility}, @{Name=Clear-Variable; Mod...
   68 NetAdapter                {@{Name=Disable-NetAdapter; Module=NetAdapter}, @{Name=Disable-NetAdapterBinding; Mo...
   43 Dism                      {@{Name=Add-ProvisionedAppxPackage; Module=Dism}, @{Name=Apply-WindowsUnattend; Modu...
   38 Microsoft.PowerShell.M... {@{Name=Add-Content; Module=Microsoft.PowerShell.Management}, @{Name=Clear-Content; ...
   35 SmbShare                  {@{Name=Block-SmbShareAccess; Module=SmbShare}, @{Name=Close-SmbOpenFile; Module=Smb...
   34 NetTCPIP                  {@{Name=Find-NetRoute; Module=NetTCPIP}, @{Name=Get-NetCompartment; Module=NetTCPIP}...
   23 NetEventPacketCapture     {@{Name=Add-NetEventNetworkAdapter; Module=NetEventPacketCapture}, @{Name=Add-NetEve...
   14 EventTracingManagement    {@{Name=Add-EtwTraceProvider; Module=EventTracingManagement}, @{Name=Get-AutologgerC...
   14 CimCmdlets                {@{Name=Export-BinaryMiLog; Module=CimCmdlets}, @{Name=Get-CimAssociatedInstance; Mo...
   13 Microsoft.WSMan.Manage... {@{Name=Connect-WSMan; Module=Microsoft.WSMan.Management}, @{Name=Disable-WSManCredS...
   11 Defender                  {@{Name=Add-MpPreference; Module=Defender}, @{Name=Get-MpComputerStatus; Module=Defe...
    9 PcsvDevice                {@{Name=Clear-PcsvDeviceLog; Module=PcsvDevice}, @{Name=Get-PcsvDevice; Module=PcsvD...
    6 StorageQoS                {@{Name=Get-StorageQoSFlow; Module=StorageQoS}, @{Name=Get-StorageQoSPolicy; Module=...
    5 Microsoft.PowerShell.S... {@{Name=Get-AuthenticodeSignature; Module=Microsoft.PowerShell.Security}, @{Name=Get...
    4 PnpDevice                 {@{Name=Disable-PnpDevice; Module=PnpDevice}, @{Name=Enable-PnpDevice; Module=PnpDev...
    3 SmbWitness                {@{Name=Move-SmbClient; Module=SmbWitness}, @{Name=Get-SmbWitnessClient; Module=SmbW...
That's just a subset of what one can find on the last version of the Windows Server OS:

[192.168.1.10]: PS C:\> Get-Command | Select Name,Module | Group Module | Sort Count -Descending

Count Name                      Group
----- ----                      -----
  140 Storage                   {@{Name=Disable-PhysicalDiskIndication; Module=Storage}, @{Name=Disable-StorageDiagn...
  105 Microsoft.PowerShell.U... {@{Name=Format-Hex; Module=Microsoft.PowerShell.Utility}, @{Name=Get-FileHash; Modul...
   98                           {@{Name=A:; Module=}, @{Name=B:; Module=}, @{Name=C:; Module=}, @{Name=cd..; Module=...
   86 Microsoft.PowerShell.M... {@{Name=Add-Computer; Module=Microsoft.PowerShell.Management}, @{Name=Add-Content; M...
   85 NetSecurity               {@{Name=Copy-NetFirewallRule; Module=NetSecurity}, @{Name=Copy-NetIPsecMainModeCrypt...
   78 RemoteDesktop             {@{Name=Add-RDServer; Module=RemoteDesktop}, @{Name=Add-RDSessionHost; Module=Remote...
   68 NetAdapter                {@{Name=Disable-NetAdapter; Module=NetAdapter}, @{Name=Disable-NetAdapterBinding; Mo...
   43 Dism                      {@{Name=Add-ProvisionedAppxPackage; Module=Dism}, @{Name=Apply-WindowsUnattend; Modu...
   42 NFS                       {@{Name=Disconnect-NfsSession; Module=NFS}, @{Name=Get-NfsClientConfiguration; Modul...
   41 MsDtc                     {@{Name=Add-DtcClusterTMMapping; Module=MsDtc}, @{Name=Get-Dtc; Module=MsDtc}, @{Nam...
   35 SmbShare                  {@{Name=Block-SmbShareAccess; Module=SmbShare}, @{Name=Close-SmbOpenFile; Module=Smb...
   34 NetTCPIP                  {@{Name=Find-NetRoute; Module=NetTCPIP}, @{Name=Get-NetCompartment; Module=NetTCPIP}...
   34 NetworkTransition         {@{Name=Add-NetIPHttpsCertBinding; Module=NetworkTransition}, @{Name=Disable-NetDnsT...
   32 BranchCache               {@{Name=Add-BCDataCacheExtension; Module=BranchCache}, @{Name=Clear-BCCache; Module=...
   27 IscsiTarget               {@{Name=Expand-IscsiVirtualDisk; Module=IscsiTarget}, @{Name=Export-IscsiTargetServe...
   23 NetEventPacketCapture     {@{Name=Add-NetEventNetworkAdapter; Module=NetEventPacketCapture}, @{Name=Add-NetEve...
   20 Pester                    {@{Name=AfterAll; Module=Pester}, @{Name=AfterEach; Module=Pester}, @{Name=Assert-Mo...
   19 ScheduledTasks            {@{Name=Disable-ScheduledTask; Module=ScheduledTasks}, @{Name=Enable-ScheduledTask; ...
   18 International             {@{Name=Get-WinAcceptLanguageFromLanguageListOptOut; Module=International}, @{Name=G...
   17 PSDesiredStateConfigur... {@{Name=Configuration; Module=PSDesiredStateConfiguration}, @{Name=Find-DscResource;...
   17 DnsClient                 {@{Name=Add-DnsClientNrptRule; Module=DnsClient}, @{Name=Clear-DnsClientCache; Modul...
   17 PKI                       {@{Name=Add-CertificateEnrollmentPolicyServer; Module=PKI}, @{Name=Export-Certificat...
   16 PSScheduledJob            {@{Name=Add-JobTrigger; Module=PSScheduledJob}, @{Name=Disable-JobTrigger; Module=PS...
   14 CimCmdlets                {@{Name=Export-BinaryMiLog; Module=CimCmdlets}, @{Name=Get-CimAssociatedInstance; Mo...
   14 UserAccessLogging         {@{Name=Disable-Ual; Module=UserAccessLogging}, @{Name=Enable-Ual; Module=UserAccess...
   14 EventTracingManagement    {@{Name=Add-EtwTraceProvider; Module=EventTracingManagement}, @{Name=Get-AutologgerC...
   13 Microsoft.PowerShell.S... {@{Name=ConvertFrom-SecureString; Module=Microsoft.PowerShell.Security}, @{Name=Conv...
   13 NetNat                    {@{Name=Add-NetNatExternalAddress; Module=NetNat}, @{Name=Add-NetNatStaticMapping; M...
   13 NetLbfo                   {@{Name=Add-NetLbfoTeamMember; Module=NetLbfo}, @{Name=Add-NetLbfoTeamNic; Module=Ne...
   13 Microsoft.WSMan.Manage... {@{Name=Connect-WSMan; Module=Microsoft.WSMan.Management}, @{Name=Disable-WSManCredS...
   13 iSCSI                     {@{Name=Connect-IscsiTarget; Module=iSCSI}, @{Name=Disconnect-IscsiTarget; Module=iS...
   12 Wdac                      {@{Name=Add-OdbcDsn; Module=Wdac}, @{Name=Disable-OdbcPerfCounter; Module=Wdac}, @{N...
   11 ServerManagerTasks        {@{Name=Get-SMCounterSample; Module=ServerManagerTasks}, @{Name=Get-SMPerformanceCol...
   11 SoftwareInventoryLogging  {@{Name=Get-SilComputer; Module=SoftwareInventoryLogging}, @{Name=Get-SilComputerIde...
   11 DirectAccessClientComp... {@{Name=Disable-DAManualEntryPointSelection; Module=DirectAccessClientComponents}, @...
   11 PowerShellGet             {@{Name=Find-Module; Module=PowerShellGet}, @{Name=Get-InstalledModule; Module=Power...
   11 Defender                  {@{Name=Add-MpPreference; Module=Defender}, @{Name=Get-MpComputerStatus; Module=Defe...
   10 PackageManagement         {@{Name=Find-Package; Module=PackageManagement}, @{Name=Get-Package; Module=PackageM...
   10 PSDiagnostics             {@{Name=Disable-PSTrace; Module=PSDiagnostics}, @{Name=Disable-PSWSManCombinedTrace;...
    9 PcsvDevice                {@{Name=Clear-PcsvDeviceLog; Module=PcsvDevice}, @{Name=Get-PcsvDevice; Module=PcsvD...
    8 BitsTransfer              {@{Name=Add-BitsFile; Module=BitsTransfer}, @{Name=Complete-BitsTransfer; Module=Bit...
    7 NetSwitchTeam             {@{Name=Add-NetSwitchTeamMember; Module=NetSwitchTeam}, @{Name=Get-NetSwitchTeam; Mo...
    7 ServerManager             {@{Name=Add-WindowsFeature; Module=ServerManager}, @{Name=Remove-WindowsFeature; Mod...
    7 TLS                       {@{Name=Disable-TlsCipherSuite; Module=TLS}, @{Name=Disable-TlsSessionTicketKey; Mod...
    6 Kds                       {@{Name=Add-KdsRootKey; Module=Kds}, @{Name=Clear-KdsCache; Module=Kds}, @{Name=Get-...
    5 AppLocker                 {@{Name=Get-AppLockerFileInformation; Module=AppLocker}, @{Name=Get-AppLockerPolicy;...
    5 PSReadline                {@{Name=Get-PSReadlineKeyHandler; Module=PSReadline}, @{Name=Get-PSReadlineOption; M...
    5 Microsoft.PowerShell.D... {@{Name=Export-Counter; Module=Microsoft.PowerShell.Diagnostics}, @{Name=Get-Counter...
    4 NetworkConnectivityStatus {@{Name=Get-DAConnectionStatus; Module=NetworkConnectivityStatus}, @{Name=Get-NCSIPo...
    4 PnpDevice                 {@{Name=Disable-PnpDevice; Module=PnpDevice}, @{Name=Enable-PnpDevice; Module=PnpDev...
    4 NetQos                    {@{Name=Get-NetQosPolicy; Module=NetQos}, @{Name=New-NetQosPolicy; Module=NetQos}, @...
    4 BestPractices             {@{Name=Get-BpaModel; Module=BestPractices}, @{Name=Get-BpaResult; Module=BestPracti...
    3 SmbWitness                {@{Name=Move-SmbClient; Module=SmbWitness}, @{Name=Get-SmbWitnessClient; Module=SmbW...
    3 WindowsErrorReporting     {@{Name=Disable-WindowsErrorReporting; Module=WindowsErrorReporting}, @{Name=Enable-...
    2 ServerCore                {@{Name=Get-DisplayResolution; Module=ServerCore}, @{Name=Set-DisplayResolution; Mod...
    2 Microsoft.PowerShell.A... {@{Name=Compress-Archive; Module=Microsoft.PowerShell.Archive}, @{Name=Expand-Archiv...
    2 NetConnection             {@{Name=Get-NetConnectionProfile; Module=NetConnection}, @{Name=Set-NetConnectionPro...
    2 PSWorkflow                {@{Name=New-PSWorkflowSession; Module=PSWorkflow}, @{Name=New-PSWorkflowExecutionOpt...
    2 Microsoft.PowerShell.Host {@{Name=Start-Transcript; Module=Microsoft.PowerShell.Host}, @{Name=Stop-Transcript;...
    1 PSWorkflowUtility         {@{Name=Invoke-AsWorkflow; Module=PSWorkflowUtility}}
    1 Microsoft.PowerShell.O... {@{Name=Export-ODataEndpointProxy; Module=Microsoft.PowerShell.ODataUtils}}
As you can see NanoServer boasts the whole bunch of Storage, SmbShare or NetTCPIP cmdlets, while it lacks modules like Netlbfo, Pester, NFS or PSDesiredStateConfiguration.

CONCLUSION

As a matter of fact, NanoServer is not aimed at being an all-round do-everything operating system, like any previous Windows till now. Nano Server has been designed for the automation of Cloud services. As Snover, the inventor of Powershell and lead architect for the Windows Server Division, stated recently: "I want automation. One of the key principles of the cloud is we want configuration as code."

Today it is up to you to find the most convenient use for this newborn technology. And even if there is for sure a lot of work to be done to correct the bugs that will come up, you can already forget about old style server management. Things are changing faster and faster. Powershell somewhat opened the way to everything you see now, and NanoServer is maybe the reason why all of this happened.
 
Stay tuned for more on NanoServer and Powershell.

Tuesday, May 26, 2015

How to check for Second Level Address Translation with Powershell

Not so long ago Microsoft added Client Hyper-V virtualization technology to its last OS, Windows 8. This move has given ITPros and developers like me the possibility to test the Hyper-V technology on their laptops and build test infrastructures aimed at learning, making demos and developing management scripts.

Now, most of the requirements to run the Hyper-V Client on your Windows 8.1 laptop are pretty simple to check. You need to own a 64-bit Pro or Enterprise version of Microsoft Windows, 4 GB of RAM and a processor (either AMD or Intel) with 64-bit wide registers. All of these requirement are easy to check.

There is one more requirement which is harder to check and to understand: your processor must, I repeat, MUST be able to perform Second Level Address Translation (SLAT).

What is that?

POPEK AND GOLDBERG AND THE DIFFICULTY OF VIRTUALISING X86

It's a long story that starts way back in 1964, when virtualization was born with the arrival of the first IBM/360 mainframe and a lot of efforts were put into optimizing environments upon which enterprises could rely . In 1974, when virtualization was already a pretty widely applied technique in the world of mainframe computing, computer scientists  Gerald J. Popek and Robert P. Goldberg published the three principles of computer virtualization we all know today:
  • fidelity/equivalence
  • safety/resource control
  • performance/efficiency
They basically mean that the executed virtual machine must be able to run as fast as a physical machine, act in the same way of a physical machine and never get out of the control of the virtual machine monitor (VMM).

Requirements for a virtual machine
(extract from 'Formal requirements for virtualizable third generation architectures'
by Popek and Goldberg
1974)
These three requirements were extremely hard to respect, largely because of the inner nature of x86 processors, which copes badly (read: doesn't cope at all) with the trap-and-emulate schema.

FROM BINARY TRANSLATION TO SLAT

Through Binary Translation, Shadow Page Tables and I/O Device Emulation, scientists achieved x86 virtualization in the late 1990s, and the company named VMWare was founded in 1998.

Nonetheless, performance stayed a issue for many years, and many administrators did not want server virtualization for some specific workloads and especially for SQL. That's why virtualizing SQL Server was once unheard of, and it's still refused by old style DBAs.

Here's where SLAT comes in. And it will make your DBAs change their mind.

SECOND LEVEL ADDRESS TRANSLATION
 
SLAT is a mechanism that enables the CPU to keep the mapping between the VM virtual memory (known as Guest Virtual Address, or GVA), the VM physical memory (aka GPA, or Guest Physical Address) and the physical memory in the virtualization host (aka System Physical Address, or SPA).
 
So, since the translation is done in the processor, the hypervisor processing overhead is significantly reduced.

There are many virtualization gurus out there explaining the ins and outs of SLAT as well as the advantages of this technology over older memory translation mechanisms. The important concept to grasp is that the goal of the Secondary Level Address Translation is to increase performance of your virtual machines by adding a new improved memory management capability inside the processors.

The third requirement of Popek and Goldberg is fulfilled. Virtual machines have finally become efficient and Microsoft is forcing us to take advantage of the most recent technology achievements by making SLAT mandatory.

HOW TO CHECK FOR SLAT
 
Now, which is the way to check for SLAT support on your computer? Powershell is one of the answers. There are others of course, like the small but powerful utility named Coreinfo (by Mark Russinovich), which if used in conjunction with the -v parameter dumps virtualization-related features including support for second level address translation.

Coreinfo must be launched with admin rights on Intel platforms otherwise you'll get the following message:

.\Coreinfo.exe -v

Coreinfo v3.31 - Dump information on system CPU and memory topology
Copyright (C) 2008-2014 Mark Russinovich
Sysinternals - www.sysinternals.com

Intel(R) Core(TM)2 Duo CPU     T7500  @ 2.20GHz
Intel64 Family 6 Model 15 Stepping 11, GenuineIntel
Microcode signature: 000000BA

Administrator rights are required to query Intel virtualization support.
So, right-click your Powershell icon, Run as administrator and get the desired information:

.\Coreinfo.exe -v

Coreinfo v3.31 - Dump information on system CPU and memory topology
Copyright (C) 2008-2014 Mark Russinovich
Sysinternals - www.sysinternals.com

Intel(R) Core(TM)2 Duo CPU     T7500  @ 2.20GHz
Intel64 Family 6 Model 15 Stepping 11, GenuineIntel
Microcode signature: 000000BA
HYPERVISOR      -       Hypervisor is present
VMX             *       Supports Intel hardware-assisted virtualization
EPT             -       Supports Intel extended page tables (SLAT)
In my case the laptop I am using has support for VMX, as indicated by the '*' in the output, but doesn't support EPT (which is the Intel acronym for SLAT), as indicated by the '-' in the output.
 
If I run the same tiny utility on a more recent processor I get a positive answer:

.\Coreinfo.exe -v

Coreinfo v3.31 - Dump information on system CPU and memory topology
Copyright (C) 2008-2014 Mark Russinovich
Sysinternals - www.sysinternals.com

Intel(R) Celeron(R) CPU G530 @ 2.40GHz
Intel64 Family 6 Model 42 Stepping 7, GenuineIntel
Microcode signature: 00000028
HYPERVISOR      -       Hypervisor is present
VMX             *       Supports Intel hardware-assisted virtualization
EPT             *       Supports Intel extended page tables (SLAT)

ENTER POWERSHELL

But why lose time installing legacy exes when you have Powershell? With Powershell you can easily check for this SLAT information:

(Get-CimInstance Win32_Processor) | Format-List Name,SecondLevelAddressTranslationExtensions


Name                                    : Intel(R) Celeron(R) CPU G530 @ 2.40GHz
SecondLevelAddressTranslationExtensions : True
Just remember that the SecondLevelAddressTranslationExtensions property is supported starting from Windows 8/2012, so the cmdlet won't work on older versions of Windows.

Other virtualization-related properties you should check when thinking of implementing Hyper-V on your laptop are:
  • VirtualizationFirmwareEnabled
  • VMMonitorModeExtensions
  • DataExecutionPrevention
MAKING A TOOL

The whole requirement check could be consolidated in a simple re-usable function:

function Check-HyperV{

    <#

    .SYNOPSIS

    Checks Hyper-V requirements

    .DESCRIPTION

    This check processor compatibility with Hyper-V

    .PARAMETER ComputerName

    A computer name.

    .EXAMPLE

    Check-HyperV -ComputerName server01

    #> 

    [CmdletBinding()]

    param(

        [Parameter(Mandatory=$True,ValueFromPipeline=$True)][string[]]$ComputerName,

        [switch]$detailed

        )

        foreach($computer in $computername){

            try{

                $ComputerInfo = Get-CimInstance Win32_Computersystem -ComputerName $computer

                $ProcInfo = Get-CimInstance Win32_Processor -ComputerName $computer

                $OsInfo = Get-CimInstance Win32_Operatingsystem -ComputerName $computer

                $VirtualizationInfo = [ordered]@{

               OSArchitecture = $OsInfo.OSArchitecture

               TotalPhysicalMemory=[math]::Round($ComputerInfo.TotalPhysicalMemory/1gb)

               SLAT = $ProcInfo.SecondLevelAddressTranslationExtensions;

               VirtualizationFirmwareEnabled = $ProcInfo.VirtualizationFirmwareEnabled;

               VMMonitorModeExtensions = $ProcInfo.VMMonitorModeExtensions;

               DEP= $OsInfo.DataExecutionPrevention_available

               }

                }

            catch{

                Write-Warning "$computer failed!"

                $ErrorHappened = $True

                }

            if(!$ErrorHappened){

                if($detailed)

                    {

                    $VirtualizationInfo

                    }

                " "

                if(

                    ($VirtualizationInfo.OSArchitecture -eq '64-bit') -and

                    ($VirtualizationInfo.TotalPhysicalMemory -ge 4) -and

                    ($VirtualizationInfo.SLAT) -and

                    ($VirtualizationInfo.VirtualizationFirmwareEnabled) -and

                    ($VirtualizationInfo.VMMonitorModeExtensions) -and

                    ($VirtualizationInfo.DEP)

                  )

                    {"Hyper-V requirements filled on  $Computer!"}

                else

                    {"Time to replace $Computer..."}

                ""

                }

     }

}

Check-HyperV srv01,srv02 -detailed
I hope you have enjoyed this blog post and learned some Powershell as well as some important pieces of the history of IT, which is so easily forgotten. Do not hesitate to comment on the subject as well to correct me if I am wrong or unclear.

Be kind, share!
Related Posts Plugin for WordPress, Blogger...