PowerShell oneliner contest

The pipeline in PowerShell offers an avenue to solve many issues with oneliners.
My fellow Windows PowerShell MVP Carlo Mancini proposed a very interesting and high quality oneliner contest. Perfect, challenge accepted :D
After reading the rules twice, I jumped into trying to figure out what’s the best approach to get the shortest solution for each puzzle. I usually focus on the approach before trying to shorten my solution because when you started digging in a hole, it’s hard to get out of it and come with a brand new approach to solve the puzzle.

  • Task 1: get the absolute value of the highest double floating point in an array

I noticed that the match operator with a regular expression allowed to filter out only double.

$n = -1,-2,-5,-8.9,'b',-9.11,-6,-3,-2,-9.1,-1,-1.4,'a'

# match with regular expression
$n -match '\d\.'

# vs. a more classic approach
$n | ? { $_ -is [double] }

There were only negative double numbers in the array. To get the shortest absolute value, I just multiplied by -1 instead of using the abs method of the [math] .Net class.

# .Net abs approach
[math]::abs(-1.4)

# vs. me cheating ;)
-1.4*-1

After bringing all the pieces together, my shortest solution for task 1 is:

# task 1
($n-match'\.'|sort)[-1]*-1

posh-contest-task1

  • Task 2: display 12345 as a int32 without using digits

To solve this task, my idea/approach was to use hexadecimal and do some substractions

0xb-0xa -eq 1
0xc-0xa -eq 2
0xd-0xa -eq 3
0xe-0xa -eq 4
0xf-0xa -eq 5

…except that I can’t use the leading 0.
So, I wondered how do I get a hexadecimal without using the 0x notation.
Well the answer is:

# you can actually do 
[char]'f' - [char]'a'

My shortest solution for task 2 is:

# task 2
[int]-join('b','c','d','e','f'|%{([char]$_-[char]'a')})

posh-contest-task2

  • Task 3: Display PowerShell from ‘X ‘

When I saw this puzzle, I immediately thought, use only 1 occurrence of ‘X ‘, get rid of the whitespace behind X and use the ascii table to get P from X, o from X, w from X,…

My shortest solution was:

#task 3 split for readability
-join(
-8,23,31,13,26,-5,16,13,20,20|%{
[char]($_+[int][char]('X '-replace' ',''))
})

posh-contest-task3

PowerShell rocks 8-) and Bravo Carlo, I enjoyed this very nice contest :-)

Bonus: my favourite PS golfer Bartek also proposed his solutions: https://becomelotr.wordpress.com/2015/07/04/the-only-golf-i-play/

A simple PackageManagement (aka Oneget) Provider for Windows Update

One of the breaking changes that comes along with PowerShell 5.0 is OneGet PackageManagement.
What is OneGet? Oneget is a package providers source aggregator, a unified package management tool like APT is on Linux.

When I said like Linux, I also meant it’s open-source :D and that you can actually write your own provider. The list of desired package providers is listed on this page.

To get a better understanding of what OneGet PackageManagement is, I’d recommend you read these two posts:

To get my hands-on this new tool on Christmas holidays, I wrote a prototype for the Windows Update Agent (WUA) provider that can query dynamically updates from locally registered sources: Windows Update (by default), Microsoft Update (after opt-in), WSUS, the Windows Store,… Unfortunately, I can’t present this code as it’s too clunky with the latest PackageManagements builds over the last 6 months. To get things working smoothly, I revised the code and tried to keep it simple. This post will present a Windows Update Agent provider that exclusively queries only 1 source: Windows Update. It could probably meet the following expectations recently reported on MS connect website:
WU-provider-feedback

Let’s see how this provider works:

  • List package providers and sources available on the box
  • WUA-01

  • List what was installed from Windows Update on the box
  • Get-Package -ProviderName WUA
    

    WUA-02

  • Install missing updates from Windows Update
  • Find-Package -Source  'Windows Update' | Install-Package
    

    WUA-03

Bonus: To be able to see what happens behind the scene and be able to choose and confirm the updates you want to install, you can do:

Find-Package -Source  'Windows Update' -Debug | 
Install-Package -Debug

I’ve hosted the Windows Update Agent provider for Windows Update on github using this link: https://github.com/p0w3rsh3ll/OneGetWUAProvider

By the way, I’m not the only one who writes PackageManagement providers using PowerShell.
Windows PowerShell MVP Doug Finke already wrote two providers for gist and github that inspired me and helped me while working on mine :-D

system.environment OSVersion property

I was using the Get-LatestWMF5 function in the ISE on a Windows 2012 R2 (PowerShell version 4.0 by default) and it downloaded the wrong file :(

I was using the [system.environment] .Net class to get the version of the Operating System.
It works fine in the console but not in the ISE.

As you can see, it’s reported as 6.2 which means Windows 8/2012 although I’m on Windows 2012 R2.
ISE.v4

There’s actually a change introduced the API that controls this behavior.
It’s documented on this page: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451%28v=vs.85%29.aspx
VersionExAPI
Windows PowerShell MVP Dave Wyatt provided this link. Thx Dave :)

I propose to replace the code that uses the

[environment]::OSVersion.Version

prev.code.getlatestWMF5
by this:

[Version]$((Get-CimInstance Win32_OperatingSystem).Version)

Get the latest WMF5

Michael Greene tweeted the following a few days ago
M.Greene-tweet-wmf5latest

I love his idea and his code shows a few awesome tricks I ignored:

I propose the following improvements:

  • Insert a require statement to tell people that the code is only v3 compatible. There’s no Invoke-Webrequest in PowerShell 2.0.
  • Download the required version based on the OS where you run the code. If you’ve PowerShell 3.0 on Windows 7 64bit, you’ll get the Windows6.1-KB2908075-x64.msu file, etc.

Let’s have a look at my proposal

On Windows 10, you don’t need this piece of code, so you’ll just get a warning:
get-latestwmf5-onWX

On Windows 7 as of PowerShell 3.0, you get the following experience:
get-latestwmf5-onW7

On Windows 2012 R2, you’ll experience the following:
get-latestwmf5-onW81

Last warning: WMF 5.0 requires .Net Framework 4.5, make sure this requirement is met before installing the WMF 5.0.

First look at Nano Server

What is Nano server?
Nano-server-slide

Nano server was first presented at the Build Conference

At #MSIgnite 2015 conference there were more presentations and a new ISO that contains Nano server (a.k.a. Windows Server Technical Preview 2) was published:

Some blog posts started to talk about it:

And the official “Getting Started with Nano Server” guide was published: https://technet.microsoft.com/en-us/library/mt126167.aspx

Hands-on!

I’ve installed the Hyper-V feature on my Windows 10 laptop.
I’ve downloaded the Windows Server Technical Preview 2 iso file I mentioned above.
I’ve installed an Internal switch in Hyper-V and set a static IP Address as I don’t have a DHCP server.
New-VM-Switch-Internal

New-VMSwitch -Name "Internal" -SwitchType Internal
# Disable DHCP
Get-NetAdapter -Name "vEthernet (Internal)" | 
Set-NetIPInterface -Dhcp:Disabled
# Set a static IP
Get-NetAdapter -Name "vEthernet (Internal)" | 
New-NetIPAddress -IPAddress 192.168.137.1 -AddressFamily IPv4 -PrefixLength 24 -Verbose

If you double-click the Windows Server Technical Preview 2 iso file, it will be mounted and you’ll see:
Nano-DVD-Content

Let’s quickly have a look at the NanoServer.wim image
Nano-Get-DiskImage

I’ve created a script to provision Nano server virtual machines attached to the Internal switch from the ISO file. Every repeated task should be automated, right? ;-)
https://gist.github.com/p0w3rsh3ll/0bb479be3d7a547f5101

Let’s see it in action:
Nano00

When I booted the Nano server VM, I could see the output of the setupcomplete.cmd script that runs at the end of the OOBE phase.

I couldn’t apply the OfflineServicing phase to change the computername as it’s described in the getting stared guide. I’ve got error 0x80220001.
This means that the computer will be named ‘MINWINPC’.
nano01

nano02

I’ve also installed only 1 NiC and only loaded the Microsoft-NanoServer-Guest-Package.cab package as it contains drivers for hosting Nano Server as a virtual machine.
Because the administrator password wasn’t changed through the unattend.xml file or the setupcomplete.cmd script, it means it’s blank.

These are the process running on the VM
nano03
Here’s the list of modules available on the Nano server I provisioned (remember, I didn’t load all the packages: Hyper-V, Failover Clustering and Storage)
nano04

Both memory and the size of the VHD file are about 512MB. If I had loaded all the packages, the image size would have grown to ~1GB.

Get-VM -Name Nano002 | Get-VMMemory
(Get-item (Get-VM -Name Nano002 | 
 Get-VMHardDiskDrive).Path).Length /1MB -as [int]

Nano06

Nano Server looks very promising and it’s only the beginning.
Back in 2006, I created a WinPE 1.0 that was less that 100MB and as far as I remember, I was able to run some AV products, run Firefox, the minesweeper, the remote desktop client, load the firewall… and was also able to remote in the WinPE using the NT4 remote command server.
These days are dead but almost 10 years later Nano server is born with built-in PSRemoting. I believe it has a bright future and that miniaturization will carry on. It will for sure be a success because it’s robust, easily manageable (using PowerShell, DSC,…), cloud-optimized (scales more quickly, allows a better VM density), less vulnerable and exposed to security threats (attack surface is reduced, less security updates and reboots?) and will probably be the 1rst and a 1rst class cloud-OS for Windows Containers.

Minimal WSMan requirement to push locally a Desired State Configuration

I’ve been working on a deployment scenario where I’ll provision new computers from a PBR (Push Button Reset) image.

These laptops will run Windows 8.1 and the PBR image is actually a sysprep image that is configured to run a post-install script.

When the PC is provisionned or reset, the PBR image is applied/expanded to the C: drive and the post-install script is run at the end of the OOBE (Out-of-Box Experience) phase, just before the user can logon.

To configure Windows Updates settings, some registry keys, services,… DSC (Desired State Configuration) is the way to go as it’ll ensure the PC remains compliant even if there’s a drift later between 2 resets or/and the PC isn’t connected to any network.

As you may know DSC depends on WSMan and not on PSRemoting.
There’s a myth about PSRemoting that was uncovered by PowerShell Magazine and Windows PowerShell MVP Aleksandar Nikolic:
DSC-RemotingMyth

If I push a DSC config without configuring WSMan, I hit a wall and get this message:
The client cannot connect to the destination specified in the request.
Verify that the service on the destination is running and is accepting requests.
Consult the logs and documentation for the WS-Management service running on the destination, most commonly IIS or WinRM.
If the destination is the WinRM service, run the following command on the destination to analyze and configure the WinRM service: “winrm quickconfig”.
+ CategoryInfo : ConnectionError: (root/Microsoft/…gurationManager:String) [], CimException
+ FullyQualifiedErrorId : HRESULT 0x80338012
+ PSComputerName : localhost


I’d also get the above error message whenever the WinRM service is stopped or the WSMan listener is absent.

To fix it, I should run the following the Set-WSManQuickConfig because the computer isn’t joined to a domain.

Set-WSManQuickConfig -SkipNetworkProfileCheck

This would enable the WinRM firewall rule for the ‘Public’ profile and expose the WinRM to the localNetwork it’s connected to.
It would also set the LocalAccountTokenFilterPolicy registry value to remove the UAC remote restriction.

The above steps aren’t required to push locally (vs. over the wire) a DSC configuration.
The attack surface can actually be reduced so that the DSC configuration can only be pushed locally.
The non-domain joined Windows 8.1 PBR images can leverage DSC as soon as I:

# 1. Enable and start the WinRM service
Stop-Service -Name WinRM -PassThru | 
Set-Service -StartupType Automatic -PassThru | 
Start-Service

# 2. Enable and restrict the firewall rules 
# to localhost instead of LocalNetwork
Get-NetFirewallRule -Name @(
    'WINRM-HTTP-In-TCP', # Pubic
    'WINRM-HTTP-In-TCP-NoScope') | #Domain,Private
Enable-NetFirewallRule -PassThru | 
Get-NetFirewallAddressFilter | 
Set-NetFirewallAddressFilter -RemoteAddress "127.0.0.1"

# 3. Add a listener (the firewall already enforces a restriction)
Get-ChildItem -Path WSMan:\localhost\Listener -Include listener* | 
Remove-Item -Recurse
New-WSManInstance winrm/config/Listener -SelectorSet @{
    Address="*";
    Transport="http";
}

# 4. Disable Kerberos, not required in workgroup for local authentication
Set-Item -Path WSMan:\localhost\Service\Auth\Kerberos  -Value $false -Force
Set-Item -Path WSMan:\localhost\Service\Auth\Negotiate -Value $true  -Force

VoilĂ , my post-installation DSC configuration can be pushed locally whenever Windows 8.1 is reset on the device.