Tuesday, July 6, 2010

Have Some Fun With Powershell

Wow, two posts in one day. That's a record.

So I was on Twitter and saw @alasta tweet about Get-Process | Stop-Process, of which I replied with Get-Process | Get-Random | Stop-Process, basically Process roulette as we call it.

Was then thinking too bad that Stop-Process doesn't support remote computers, however have no fear, the built in taskkill of Windows will do the trick.

First off, I do not recommend using this, second thing to say it is kind of funny. Try this next time you want to annoy someone:

Get-Process -ComputerName $colleaguepc | Get-Random | ForEach-Object {Taskkill /f /s \\$colleaguepc /pid $_.pid }

Again, use at your own risk!

Where the heck did the time go?

Wow, what the heck happened?

As the great Scottish poet Robbie Burns wrote, "The best laid schemes o' mice an' men", I started this blog with good intentions. To let those in my sphere of influence know the cool things about Powershell. Then of course life takes another turn and I had to put it on the back burner.

Well thanks in a large part to Marco Shaw, I'm picking this up again. Couple of small things to talk about.

I saw on my Twitter feed recently someone complaining about the load time for Powershell, to go from seeing the window, to the point where you can actually type it. I find that most of these long load times has to deal with a heavily customized profile.

While loading all possible snap-ins, modules, and functions you have written at load time is a good idea in one way, since you have it at hand. It starts to get a little much when you are loading the Exchange Snap-In, PowerCLI for VMWare, and the Quest AD cmd-lets. Loading all three of those will take your load time for Powershell up into the 3 minute mark minimum. Don't even get me started on Exchange 2010 since it uses remoting features and literally downloads the cmd-lets you've been delegated via RBAC.

If your Powershell window is taking a long time to load check what is being loaded in your profile. Type:

PS H:\> $profile
PS H:\>

That will show you the actual ps1 file that Powershell loads up on start. Open that in your favorite text/script editor and clean it up a bit.

To check out what snap-ins you currently have loaded, use the good old:

PS H:\> Get-PSSnapin

Name        : Microsoft.PowerShell.Diagnostics
PSVersion   : 2.0
Description : This Windows PowerShell snap-in contains Windows Eventing and Performance Counter cmdlets.

Name        : Microsoft.WSMan.Management
PSVersion   : 2.0
Description : This Windows PowerShell snap-in contains cmdlets (such as Get-WSManInstance and Set-WSManInstance) that a
              re used by the Windows PowerShell host to manage WSMan operations.

Name        : Microsoft.PowerShell.Core
PSVersion   : 2.0
Description : This Windows PowerShell snap-in contains cmdlets used to manage components of Windows PowerShell.

Name        : Microsoft.PowerShell.Utility
PSVersion   : 2.0
Description : This Windows PowerShell snap-in contains utility Cmdlets used to manipulate data.

Name        : Microsoft.PowerShell.Host
PSVersion   : 2.0
Description : This Windows PowerShell snap-in contains cmdlets (such as Start-Transcript and Stop-Transcript) that are
              provided for use with the Windows PowerShell console host.

Name        : Microsoft.PowerShell.Management
PSVersion   : 2.0
Description : This Windows PowerShell snap-in contains management cmdlets used to manage Windows components.

Name        : Microsoft.PowerShell.Security
PSVersion   : 2.0
Description : This Windows PowerShell snap-in contains cmdlets to manage Windows PowerShell security.

Name        : Quest.ActiveRoles.ADManagement
PSVersion   : 1.0
Description : This Windows PowerShell snap-in contains cmdlets to manage Active Directory and Quest ActiveRoles Server.

Name        : Microsoft.Exchange.Management.PowerShell.Admin
PSVersion   : 1.0
Description : Admin Tasks for the Exchange Server

As you can see from the above I have two extra snapins loaded, which I did to show some decent output. Decent coding practices state only use what you need, no sense in bloating your environment with stuff your not using.

Finally, a lot of people find Powershell to be fairly daunting as they hear all that you can do with it. Hands down in my opinion the best way to start using Powershell is to simply open it up at your desk, and try some stuff. If there is a task you need to do, say find out what version of Windows a server is using try to find it with Powershell:

PS H:\> Get-WmiObject -Class Win32_OperatingSystem -ComputerName test_server1 | Select-Object Caption
Microsoft Windows Server 2008 R2 Enterprise
PS H:\>

Finally, ask for help. There are tons of forums out there with very knowledgeable people willing to help you out. Check them out.

PS: Anyone on the east coast check out Dev East, particularly in August Marco Shaw(http://marcoshaw.blogspot.com) will be doing a presentation on Powershell, and I'll be doing a short 15 minute one on some real world examples of Powershell use.

Wednesday, November 4, 2009

Professional Looking Scripts With Custom Objects

One of the things I've ran into frequently in my experiences with scripting is making them look professional, and have them taken seriously like most applications are.

Before I get all the developers out there mad at me I'm not saying that scripting is on the same level of say C++ or other languages. I am saying fervently, that scripts can do the work of applications in certain niches.

One of my pride and joy scripts, which one of these days I'll port to Powershell, is a VBScript which does the following:

1. Searches through Active Directory, and retrieves all Servers.
2. Reports back the name and creation date of the computer account. This list is sorted by Domain Controllers, Exchange Servers, then Member servers.
3. After that it brings back total disk size, free space, and reports on Operating System version, and Service Pack level.
4. Finally it will again loop through all servers, querying them for Error events in the System and Application logs in the last 7 days.

Oh, and did I mention all this output is put into a HTML document, complete with tables, coloring, and links to eventid.net for the Event Logs entries. I would say that approaches if not meets the capabilities of some programs. But I digress.

One of the best features of Powershell is the ability to create your own custom objects. Custom objects are a large topic, with many different uses, so I'm only going to focus on a part of them. How they can make your scripts look more professional. Take this output from Get-User, getting my user account with a few properties:

Get-User -Identity DHenshaw | Select-Object SamAccountName,Company,WhenCreated

SamAccountName      Company      WhenCreated
--------------      -------      -----------
DHenshaw            Company A    12/5/2006 14:31:27

That looks quite nice, you see the default format of Format-Table shows you the Property name, followed by the value below. Looks very professional and nicely formatted doesn't it? That's all well and good all the information you need can be gleamed from one cmdlet alone, but frequently we as scripters need to pull in data from multiple source, or multiple cmdlets. It would be possible to simply run the two cmdlets and send the output to the host, but it's quite simple(once you get the hang of it) to get all that information and display it in a professional manner.

As an example of how custom objects can help you in your scripts, take a look at this one below:

$mbxs = Get-Mailbox
$now = Get-Date
$colEmails = @()
foreach ($mbx in $mbxs)
      $ProgressPreference = "SilentlyContinue"
      $colmsgs = Get-ExchangeServer | Where-Object {$_.IsHubTransportServer -eq $true -or $_.IsMailboxServer -eq $true} | Get-MessageTrackingLog -Recipients $mbx.PrimarySmtpAddress -start $now.AddDays("-7") -End $now -ResultSize Unlimited | Where-Object {$_.EventId -eq "Receive"}

    if ($colmsgs.Count)
        $objMail = New-Object System.Object
        $objMail | Add-Member -type NoteProperty -name Name -value $mbx.Name
        $objMail | Add-Member -type NoteProperty -name Emails -value $colmsgs.Count
        $colEmails += $objMail
$colEmails | Sort-Object Emails -descending

This script was created for our Exchange Server 2007 environment as something we were curious to see, it basically pulls together information from the Message tracking logs and gets a list of the users who have sent the most emails over the last week. Now I'll say this now because there are people who'll notice this script is not bullet proof, but it did the job for us. Let's walk through it and you will see how it works, and how custom objects can help you, shall we:

Part 1 - Initialize and populate variables:

$mbxs = Get-Mailbox
$now = Get-Date
$colEmails = @()

With a quick glance you can see here that I create three variables, and populate two with data. The third $colEmails I simply create it as an empty array, this is huge later on in the script, but we'll get to that in a minute. The first variable, uses Get-Mailbox to get a collection of all mailboxes in my company. The next variable simply uses the Get-Date cmdlet to get the current date.

Part 2 - Looping and Getting the Real Data

foreach ($mbx in $mbxs)
      $ProgressPreference = "SilentlyContinue"
      $colmsgs = Get-ExchangeServer | Where-Object {$_.IsHubTransportServer -eq $true -or $_.IsMailboxServer -eq $true} | Get-MessageTrackingLog -Recipients $mbx.PrimarySmtpAddress -start $now.AddDays("-7") -End $now -ResultSize Unlimited | Where-Object {$_.EventId -eq "Receive"}

First off here we simply create the foreach loop to go through each of the mailboxes one by one. The next line of the script does the real work here, it starts by getting all the Exchange servers in my infrastructure, and filtering all but my HubTransport servers. I then pipe those objects, to the Get-MessageTrackingLog cmdlet, searching where the recipient is the Primary email address of the mailbox, starting from one week ago up until now, you'll note to get our start date we use the AddDays method of the System.DateTime object specifying a negative days, giving us one week in the past, sneaky huh? Following that we again use Where-Object to get those log entries were the EventID is Receive. This gives us all the receive entries for our users. Onward and downward??

Part 3 - Create my custom object and fill it up

    if ($colmsgs.Count)
        $objMail = New-Object System.Object
        $objMail | Add-Member -type NoteProperty -name Name -value $mbx.Name
        $objMail | Add-Member -type NoteProperty -name Emails -value $colmsgs.Count
        $colEmails += $objMail

As you can see the first here simply uses an if statement to check whether msgs.count is not Null, which could be possible if the user did not send any email during the last week. If the property actually has a value, then we proceed to the steps within the if statement.

Subsequently we actually create the our custom bject using the New-Object cmdlet, declaring it the type of System.Object(For more info on System.Object check out this link, http://msdn.microsoft.com/en-us/library/system.object.aspx). Think of System.Object as the end all be all of objects, since it can literally be all objects. All .NET classes are derived from System.Object and as such every method defined on System.Object is available to .NET objects.

Once we have the object created and have specified it's type, we have this nice little object sitting there looking very pretty, lets actually fill it with some fields and data shall we? Next off we simply pipe our object into the Add-Member cmdlet, this cmdlet allows us to add members(or properties) to objects, the first thing we add is the name of the mailbox we've got the message tracking data for. We are using a noteproperty to specify a property with a static value, once we set it it's not going to change, which is great for our purposes(There are many different types you can add, for a complete list check out the TechNet info on Add-Member here, http://technet.microsoft.com/en-us/library/dd347695.aspx). Once that property is set, we need to actually add the number of Receive events we got earlier. To do this we use the same cmdlet this time however, we specify a different name for this property, and the value is $msg.Count.

A side note here. How did I know that my variable $msg had a count property? Well that's where Add-Members big brother comes to play, Get-Member. Take any object in Powershell and pipe it to Get-Member, and see what you get? Go ahead, I'll wait until you do it . . . . . . . Pretty cool wasn't it? Get-Member will show you all properties and methods available for an object. I honestly, haven't seen it in use in a script, but for creating a script and working on the command line it is unbeatable, and must be in every Powersheller's repertoire. But I've digressed again, which is the great thing about having your own blog if you want to go on a tangent it's your call. Ahem, tally-ho?

So now we have our object populated with the properties we wanted, it shows us the Name of the mailbox that got the mail, and the number of receive events from the Message tracking log. Now what do we do with it? Well that is solved by the next line, in which we simply take our custom object and add it to the array we created earlier, using the += operator. This simply says take what's on the left, and add to it what's on the right. Now that we've stored our object in something more permanent, we close out our if statement and close our foreach loop as well.

Now one thing to note you may have noticed. You'll see above that if you follow the logic of the script my custom object, $objEmail, will get re-created each time the loop processes. This is fine and will work to our advantage, as it clears all properties we set before and give us a pristine empty object, ready to receive the next users information.

The final line simply takes our array, and outputs it to the console. The output you will receive will be something similar to this:

Name        Emails
----        ------
User, A     17072
User, B     8946
User, C     609
User, D     575
User, E     255

Looks pretty nice doesn't it? As you can see custom objects give you the ability to organize your data, and display it in such a way that makes it easier to read, and understand. With a little extra work, and some basic concepts you can take your scripting to the next level.

PS: Forgive me for any formatting issues, this is my second time and I'm getting better.

Tuesday, October 20, 2009

Inaugural Post

So pat yourself on the back(but not too hard) you are now reading my inaugural post for my blog, Power Of The Shell.

One of the best things I find about Powershell is simply what it can do. The answer is EVERYTHING! I have yet to find a task that's been brought to me, that required something to be done automated, or something that needed to be done repeatedly that I couldn't do with Powershell.

Need to setup Terminal services profiles for your users? One liner it with Powershell:

Get-QADUser -IncludedProperties LogonName |Foreach {Set-QADUser -Identity $_.name -TsProfilePath (Join-Path -path \\ServerA\TsProfiles\ -childPath $_.LogonName)}

The possibilities are almost endless

Personally, I've done some pretty cool stuff with Powershell.

One client needed specific permissions set on a certain folder, the only problem was their were hundreds of these folders, and the permissions needed to be kept up to date, and be applied to newly created folders. Well with Powershell I created a template folder with the right permissions took those permissions assigned them to a variable, then recursed. Whenever I found the folder I was looking for, I set the permissions on it.

At one time we needed to modify the client permissions on Public Folders within Outlook, again it was multiple folders, and with Powershell it was a snap.

I hope if you've gotten anything from this, it's that you CANNOT afford to think Powershell is only for programmers, or for script monkeys. It is for anyone in IT at all, not just Windows as well. Manage Citrix, or VMWare, check out the cmdlets for those products. The future is now people!

Look forward to more posts in the future.