PowerShell – Strings Are Objects Too!

I find that as I become more comfortable with my new skills as a PowerShell junkie, I enjoy answering questions more and more.  Oftentimes, I find questions on various forums that challenge my knowledge and skill level to become even more adept at PowerShell.  Recently, I came across a task that not only challenged my skills, but also reinforced the need to break away from certain habits and concepts that I’ve been using over the years with my scripts.  This week’s lesson: Strings are not fixed data!

The challenge was an interesting one:  You have a CSV file with a bunch of names.  Some include First, Middle, and Last – some don’t.  Retrieve the first letter of the first name and the entire last name to generate an email address.

Easy enough if your CSV consists of three columns.  First we’ll use Import-CSV and see what we’ve got here.

Import-CSV 'C:\scripts\email.csv'

Strings0

Ah.  Looks like the file has no headers.  Easy enough of a fix:

Import-Csv 'C:\scripts\email.csv' -Header First,Middle,Last

Strings1Now that looks a lot better!  So we’ve got our data.  Let’s go ahead and break this thing down, starting with the first name.

$FName = $Name.First

This will return the names in the First column only.  Now what we’ll do is use the substring method to extract the first character from the string.  The two parameters after substring mark your starting point (which is 0, or before the first letter), and how many characters you want (which is the first one).

$First = $FName.substring(0,1)

So we’ll just run our script real quick here and…Strings2

 

 

 

 

 

There we go!  Part one accomplished!  The last name and email parts are pretty easy.  Just add in these lines:

$Last = $Name.Last
$Email = $First + $Last + '@company.com'

And you get…

Strings3 And there you have it!

But what if the full names are in a single column only?  Well, that’ll take a little bit different of an approach.  Fortunately, it doesn’t involve completely scrapping the script.  For the first initial, you can still use the substring method to grab it, but you need an easy way to identify the last name.  So let’s split the incoming data on the spaces:

$Last = $FName.Split(" ")

And we’ll get this return:

Strings4Now I started thinking, “Well that’s great!  But how do I tell PowerShell which one is which!?”  It took a little time, but I remembered that with PowerShell, we’re moving objects through the pipe, not fixed data.   And like any other object, I can filter them!

$Last = $FName.Split(" ") | Select-Object -Last 1

And voila!

Strings5Now you might decide, “you know, I think I’d like to have the middle name in there.  How are you going to do that, huh!?”  Well, I’ll show you. Just:

$Middle = $FName.Split(" ") | Select-Object -First 2 | Select-Object -Last 1

Strings6

Oh, but look.  Some of the strings have a middle name instead of just an initial.  We can easily remedy this by feeding our filtered object into a substring like we did with the first name:

$Middle = ($FName.Split(" ") | Select-Object -First 2 | Select-Object -Last 1).substring(0,1)

And now we get:

Strings7So then we put this all together and update our Email string…

Strings8Success!  Now we can start issuing emails from that list that HR sent us without having to do any manual formatting.  And we also reinforce the lesson that everything, including strings, are not fixed data; but objects that can be moved, and manipulated, through the pipe.

Have a happy holiday!

A PowerShell Life: Moving To Server Core

powershell3We’ve all had a good, long time to settle into the daily routine the goes with being a Windows Server administrator or engineer.  We’ve had time to learn all of the nuances behind the graphical user interface.  We know where to go to get our management consoles, what commands to run to make the tweaks we need and get the things we need to get done completed.  And I, like many of my counterparts, looked at Server Core when it was first introduced with Server 2008 with apprehension.  Why would I leave the confines of my comfortable UI for something so stripped down?  What was this BS that Microsoft was feeding us?  I’m not a UNIX guy!

Sure, you have a smaller foot print for your OS when it comes to storage; and yeah, it uses less compute and memory too.  Sure, it requires less patching, and fewer reboots.  But what about my tools!?  What about my Start button!?

Truth be told, ever since I started working in PowerShell, I’ve found that I’ve used the administration consoles less and less.  Oftentimes, working in the console requires a one-at-a-time mentality, whereas with PowerShell I can manage multiple machines at once.  Even remoting to a server’s desktop was far too time consuming, especially if I could just get the information or execute the command I wanted through PowerShell Remoting.  As a ConfigMan engineer, I’ve even challenged myself to move outside of the box and create my own modules to service SCCM Clients through PowerShell so I don’t even have to open my console to do any troubleshooting, and can share these tools with my colleagues so they too can troubleshoot without directly interacting with a console or the client.

So when it came to be time to start taking a good hard look at my environment, and the footprint it was leaving in virtualization resources, it was time to take a look at myself as an admin and engineer.  To get a little philosophical – we, as architects and stewards of technology, can view the design of our environment as a mirror reflection of our own selves.  Our knowledge and experience makes up just as much of the environment as the best practices and company directives that guide our hand in creating the infrastructure that will support our users for the next life cycle.  Once I realized that the graphical interface served no purpose other than being that comfortable space that I’ve known for the last 18 years, it no longer made sense to incur additional costs in resources and a larger attack surface in my environment.

Of course, we have our ways of easing into the water.  I’ve been testing the applications I’m responsible for in a core instance for a few months now.  Some applications made the cut and were able to be used effectively in Core, and some just weren’t.  When I was finally ready to take the leap with those that would, I was able to use a simple PowerShell script that removed the GUI and rebooted the server.

Piece of cake.

It’s since been a couple of weeks since that implementation, and so far so good.  If anything, the most notable change that I’ve encountered is with myself.  I wrote a lot of scripts when I began learning PowerShell, and now that I’m working with core, I’m writing new scripts more frequently and refining old ones as my knowledge grows.  My environment is using fewer resources, and I’m becoming a smarter PowerShell Administrator.  I’d say that’s a win-win scenario.

Getting Your Uptime – The PowerShell Way!

powershell3I remember, back in my admin days, we had an executable called uptime.exe.  It didn’t do much, but it gave us the amount of time that a system had been up and running for – which was something useful for checking if you suspected a server had gone offline and didn’t want to log in to verify if it crashed or not.

Some months back, I wrote a PowerShell script to give me the uptime on a remote system.  It worked well, but the formatting left something to be desired.  Using Get-CimInstance, and a little PowerShell math, I came up with this:

Get-CimInstance Win32_OperatingSystem -ComputerName Server01 | Select-Object CSName,LastBootUpTime,@{Name = 'Uptime';Expression = {$PSItem.LocalDateTime - $PSItem.LastBootUpTime }}

It does well enough, but the output format isn’t the prettiest.  In particular, I’m not really happy with the uptime output, as someone may not immediately realize that the first set of digits is days.

CSName      LastBootUpTime           Uptime
------      --------------           ------
Server01    11/19/2014 1:20:02 AM    14.08:16:27.5529900

So what to do?  I did a little reading and came across one of the Scripting Guys’ posts about the New-Timespan cmdlet and how it works.  Well, we know that the LastBootUpTime is a datetime data type; and we know that the New-Timespan cmdlet takes datetime datatypes; so we should be able to hook those two up.   So let’s do this!
#Define our current date and pull the last reboot date from a machine.
$Date = Get-Date
$Reboot = Get-CimInstance Win32_OperatingSystem -ComputerName Server01 |
Select-Object CSName,LastBootUpTime

This will give us our current date and time to feed in to the New-TimeSpan cmdlet, and the LastBootUpTime from the Win32OperatingSystem class.  I’m also grabbing the system name from the CSName property to feed into my output.  Now let’s build our command:
#Put it all together to get system uptime information
New-TimeSpan -Start $Reboot.LastBootUpTime -End $Date |
Select-Object @{Label = "System Name"; Expression = {$Reboot.CSName}},@{Label = "Last Reboot Time"; Expression = {$Reboot.LastBootUpTime}},Days,Hours,Minutes,Seconds |
Format-Table -AutoSize

I formatted the table to make it a little bit cleaner and relabeled the CSName and LastBootUpTime properties to make things a bit easier to read.  Now we get the following output:

System Name      Last Reboot Time         Days Hours Minutes Seconds
-----------      ----------------         ---- ----- ------- -------
Server01         11/19/2014 1:20:02 AM    14   9     19      29

Now just add some parameterization for the computer name, maybe give it some logic to scan multiple machines, and you’ve got yourself a very fine replacement for uptime.exe.

Feel free to download the script from the TechNet Script Center.