Capn’s log, stardate, uhhhh, shit.  I don’t even know.  I think it’s like 3:00 AM, but I’ve been working in multiple time zones so it might be 10 AM or 6 PM as well.  My cat is snoring.  I think I need sleep too.


Anyhow, working on a big project involving a bunch of people and stuff and PowerShell code.  Ran into some interesting things dealing with “very long string values” that led to taking a break and posting this.

Example 1

$roles = @"

Example 2

$roles = "Web-Default-Doc,Web-Dir-Browsing,Web-Http-Errors,Web-Static-Content, `
Web-Http-Redirect,Web-Http-Logging,Web-Log-Libraries `

Example 3

$roles = "Web-Default-Doc,Web-Dir-Browsing,Web-Http-Errors,Web-Static-Content,"
$roles += "Web-Http-Redirect,Web-Http-Logging,Web-Log-Libraries,"
$roles += "Web-Request-Monitor,Web-Http-Tracing,Web-Performance"

Example 4

(close text editor and open up a first-person shooter game and kick feet up on desk)

Ehhh.  Pfffft.  I’m starting to dig Adelle’s new album.  And Dallas Green, and First Aid Kit, and Cage the Elephant, and the new Robert Plant, and I think there’s an owl in my backyard.  Hold on…

Okay, all good.  It was an owl but he flew back to his nest.

So, what’s with the strings above?  They’re obviously Windows Role/Feature codes.  I mashed them into a delimited string because until you reach sufficient scalar manifestations of binary bi-directional pointer linking and permutations of random access memory index operations in combination with SSD proprietary index algorithms, there really isn’t a measurable benefit to using other data structure types such as arrays, hash tables, lists (another hash table), or even reading from a structured non-hierarchical file.

If stored in a structured hierarchical file, that adds measurable overhead due to second-level parsing, but even then, I’ve only seen a measurable impact after loading a book-load of content into the stupid thing, and by that time I’m spitting at the screen and shouting obscenities loud enough to make my neighbors poor dog cry in the next yard with my windows closed.  But I digress…

So, where was I?  Oh yeah, spewing rambling nothings.  But aside from that, the issue with building a string value using either of the first two (2) methods above is that they store the lines literally.  As in, they include any prefixed spaces (if you indent them), and line-feed control codes.  The third option stores the concatenated value as if it were fed in on one line.

And if you mistakenly wrap each line with beginning ” and ending “, for the first two examples, well, you end up with an inverse wormhole, mirrored inside of a mystery, wrapped inside of an enigma, stuffed inside a Joe Pesci.

So, for the first two, I shove the text lines to the left margin (avoid preceding spaces or tabs) and then shove it through the Replace() woodchipper and spit out a more compact value to stuff into the variable.  Then I flour the pan, roll the variable out and feed it into a hand-rolled foreach() loop until it’s golden brown.  Sorry, Rachel Ray was on a few minutes ago…

$roles = $roles.Replace("`n","")
$wflist = Get-WindowsFeature
$WinSxs = "\\smellslike\victory\Microsoft\WindowsServer\2012r2\Sources\sxs"

foreach ($role in $roles.Split(",")) {
  $status = $wflist | Where-Object {$_.Name -eq $role} | 
    Select-Object -ExpandProperty "InstallState"
  if ($status -ne "Installed") {
    Write-Verbose "installing role / feature: $role..."
    Add-WindowsFeature $role -IncludeManagementTools -Source $WinSxS

By the way, thanks Microsoft for deciding to kill “Get-WindowsFeature” and replace it with “Get-WindowsOptionalFeature”, in Windows 10 and Windows Server 2016.  Nice.

That’s all for now.  Time to sleep.  Peace be with you.


9 thoughts on “PowerShell Cranium Dranium with Fries and a Beer

  1. First off, you need to feel comfortable asking me for help or suggestions. Please. Ask me. I hate to see people struggle. This is a much easier task than you are making it out to be.

    To begin, PowerShell will treat any comma separated list of values into an array. Ideally you would define $roles like this:

    $roles = “Web-Default-Doc”,”Web-Dir-Browsing”,”Web-Http-Errors”,”Windows-Defender”,”EnhancedStorage”

    But if you find yourself with a single string like this:

    $roles = “Web-Default-Doc,Web-Dir-Browsing,Web-Http-Errors,Web-Static-Content,Web-Http-Redirect,Web-Http-Logging,Web-Log-Libraries.Web-Request-Monitor,Web-Http-Tracing,Web-Performance”

    All you need to do is split it:

    $Roles = $roles -split “,”

    Then you can pipe the collection of roles to Get-WindowsFeature, filter out those that are not installed and then add the feature. I’m running this from a Windows 8.1 desktop to a Server 2016 TP4 server.

    $roles | get-windowsfeature -ComputerName chi-hvr1 | where {-Not $_.installed} | Add-WindowsFeature -ComputerName chi-hvr1 -IncludeManagementTools -source $source -WhatIf

    Finally, Get-WindowsFeature is a command from the ServerManager module and Get-WindowsOptionalFeature is from the DISM module. One did not replace the other. You should be able to use the ServerManager cmdlets.

    There’s no reason for you and the cat to be up suffering with any of this.

    1. Thanks Jeffery. I actually tried that approach as well. The approach I ended up sticking with however is reading in a file and pipeline to the cmdlet. Cleaner and simpler, even if it might not be as process efficient. Not to devalue your suggestion, it’s obviously good and probably even better than this other one. But rather than deal with wrapping a bunch of ugly feature code names inside my cmdlet code, I drop them in a separate text file, one name per line, and then feed it in like $roles = Get-Content “daves_stupid_roles_list.txt”, and then pipeline it into Add-WindowsFeature (except on Windows 10, where they switched that cmdlet out). In any case, I edited this because my previous attempt to reply was from my phone as it was falling from 10% battery in a place with poor coverage. I wanted to give it proper attention as soon as I got home.

      1. Ok. Next time let me see if I can help you first. On Windows 10 you should download and install RSAT for Win10. Then you will have the ServerManager module which has the WindowsFeature cmdlets you are expecting.

      2. Wait. So I have to load ServerManager in order to use Get-WindowsFeature, Add-WindowsFeature and Remove-WindowsFeature under Windows 10? I see XXX-WindowsOptionalFeature only, but it’s slightly different than the predecessors.

      3. No. PowerShell will automatically load the necessary module when you run the command. The WindowsOptionalFeature cmdlets are part of the DISM module and duplicate some of the functionality as the WindowsFeature cmdlet. But the DISM stuff is intended to be used with clients in my opinion. Install RSAT and you will have the necessary commands to manage your servers from a Windows 10 desktop.

      4. When I run Get-WindowsFeature on all of my Windows 10 machines, it just says “is not recognized as the name of a cmdlet…” The script I was using for my example is being run directly on Windows Server 2012 R2 though. Sorry for the confusion. And I really do appreciate your help. 🙂

    1. I can build that into the script I’m working with. As long as my customers are aware of having to install RSAT they should be fine. Most probably have it already. I thought that cmdlet was part of the base PowerShell environment on earlier versions of Windows. But I’m getting old, so I probably forgot to take my medicine or something.

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s