Thursday, November 6, 2008

Like VIM? Like PowerShell? This link's for you!

Vim is a text editor that is popular with *nix users, but it works just fine on Windows, too.  

I subscribe to Andy Schneider's Get-PowerShell blog, and today he had an entry about a new extension for Vim that does your syntax highlighting, etc, for PowerShell.  I recommend checking out the Get-PowerShell blog, but if you just want the file, follow the link from Peter Provost's blog.

Tuesday, November 4, 2008

Did I Mention PrimalForms is free?

Here's that link again.

Oh, and for some reason this post shows up under the Google search , but click here if you want a PrimalForms example.

Windows Forms with PowerShell

SAPIEN came out with an awesome form builder for PowerShell called PrimalForms.  I decided to give it a whirl.  It really makes short work of making a native Windows GUI.  For my first project I made a quick and dirty app that allows me to get disk fragmentation data for a remote server using the DefragAnalysis method of Win32_Volume.  I used the PrimalForms GUI to create my form, and then I only had to fill in the button1 click handler and the form load handler, add a small function for printing to the text box, and voila!






Friday, October 31, 2008

Defrag Your Servers Remotely with PowerShell

Another quick and dirty script.  I've got a couple servers that are running applications that suffer from disk thrashing, and are running Windows Server 2003.  A defrag tends to noticeably increase performance.  With this script I can kick off a defrag from my desk whenever I want.






Friday, October 3, 2008

Get-SmsWmi and Find-SmsID

I work with SMS (Systems Management Server) at work a lot, so I whipped up two functions that I've found invaluable for automating tasks in SMS, which I alluded to briefly in this post, which contains the source for the functions.

I thoght I'd post today about how these can be used and why they would be useful.  Think of it as the beginnings of a Get-SmsWmi cookbook.

Note:  I alias Get-SmsWmi to 'gsms', so keep that in mind for the examples.

Get a list of all available object nicknames.

C:\PSScripts> gsms

ERROR: You must enter a class name or nickname.

Valid nicknames are:

  AddRemovePrograms
  AdStatus
  Advertisement
  Collection
  ComputerSystem
  DistributionPoint
  LogicalDisk
  MembershipRule
  NetworkAdapter
  NetworkAdapterConfiguration
  OperatingSystem
  Package
  PackageStatus
  Program
  Query
  Server
  Service
  Site
  StatusMessage
  System
  WorkstationStatus
  User

Note: You only need to type as many characters as necessary to be unambiguous.


Get all computers with 'tojo' in the name.

gsms sys 'Name LIKE "%tojo%"' |
  Format-List ResourceId, 
  Name, 
  LastLogonUserName, 
  LastLogonUserDomain, 
  IPAddresses


ResourceId          : 220647
Name                : SOMETOJO-CORP
LastLogonUserName   : sometojo
LastLogonUserDomain : MYDOMAIN
IPAddresses         : {1.1.1.1}


Get all advertisements for the package with PackageID 'S01002BE'

gsms adv 'PackageId = "S00002BE"' |
  select AdvertisementID,
  AdvertisementName,
  @{Name = 'PackageName'; Expression = {(Find-SmsId -p 'S00002BE').Name}},
  PackageID,
  ProgramName |
    Format-List

AdvertisementID   : MV12071F
AdvertisementName : iPass Upgrade
PackageName       : iPass Upgrade
PackageID         : S00002BE
ProgramName       : Update iPass


Delete all packages for a particular distribution point.

gsms pack 'ServerNALPath LIKE "%servername%"' | % {$_.Delete()}


Get a directory listing of all Collections and their subcollections.

# Get-SmsCollectionTree
#
# Writes an indented list of collecitons by parent
#
# Args:
#   $root: The CollectionID of the parent collection
#   $indent: The indentation level for screen output

function Get-SmsCollectionTree {
  param([string]$root = 'COLLROOT',
        [int]$indent = 0)

  Get-SmsWmi SMS_CollectToSubCollect "parentCollectionID = '$root'" |
    % {$name =  (Find-SmsID -c $_.subCollectionID).Name
       Add-Member -InputObject $_ -Name 'sub_name' NoteProperty $name
       $_} |
      sort sub_name |
        % {Write-Host (('    ' * $indent) +
                        "+ $($_.sub_name) : $($_.subCollectionID)")
           Get-CollectionTree $_.subCollectionID ($indent + 1)}
}

Add all packages to a DP.

$dp = gsms SMS_SystemResourceList 'ServerName = "sms-dp-01" and RoleName = "SMS Distribution Point"'

foreach ($pkg in (gsms package)) {
  $new_object = ([wmiclass]"\\sms_srv\root\sms\site_MV1:SMS_DistributionPoint").CreateInstance()
  $new_object.ServerNALPath = $server.NALPath
  $new_object.PackageID = $pkg.PackageID
  $new_object.SiteCode = 'S00'
  $new_object.SiteName = 'My SMS Site'
  $new_object.Put()
}

Note:  That wmiclass/CreateInstance trick I did isn't very well documented, but if you need to create an instance of a class then that's how you can do it.  Also remember that when reading and writing to WMI for SMS, you should be connecting to the server with the SMS Provider, not necessarily the site server.  If you have a separate SQL Server then it might be that server.

That's it for today, but you can see how easy it is to start grabbing data directly from WMI and using it in a script.  If you have any questions drop them in the comments or shoot me an email, I don't get that much traffic.

For a complete list of the WMI classes, download the Configuration Manager 2007 SDK.

Thursday, September 25, 2008

Pipes, Loops, and Exploding Memory Usage

Background

Recently I wrote a script to automate some reports that are a huge pain.  I was pretty pleased with myself when I finished, but when I ran it, it kept going...and going...  It was taking a really long time, which might not have been strange because there was a lot of data, but I popped up my Task Manager, and that's when I noticed that powershell.exe was using up 1GB of RAM and climbing.  Clearly I had a problem with the design of the script, but what shocked me was that I was able to fix this  by replacing a foreach loop with a pipe to foreach-object, and the end result was that my powershell.exe process never uses more than 55MB of RAM.


Passing Objects Down the Pipe

One of the cool things about pipes is that as data is generated by a cmdlet or function it is passed down the pipe to the next one without having to wait for all of the data finish being generated. 

Consider the following:

C:\PowerShell> dir c:\ -include *.log -recurse | % {$_.FullName}

As each file is found that matches the pattern, it will be returned.  Now let's try it with a foreach loop:

foreach ($file in (dir c:\ -include *.log -recurse)) {
  $_.FullName
}

This time we have to wait for the entire hard drive to be scanned before the output comes out, and we'll use a lot more memory.  Why?   Because when you use parentheses, the expression between them is evaluated BEFORE the loop is processed.  This is essentially the same as the following:

$files = dir c:\ -include *.log -recurse

foreach ($file in $files) {
  $_.FullName
}

Most things you use PowerShell for probably won't be so large that this becomes a huge issue.  In my case I was querying Systems Management Server for inventory information on tens of thousands of computers, so it really started to impact the other things I was using.


Planning Ahead

As you're creating your scripts, try to be conscious of where you're using piped commands vs. loops, and consider how it would change your script if you refactored the code to do it a different way.  I tend to use loops more when I'm writing scripts because they are generally more readable and easier to update for the next poor sap who has to edit my code, but it's important no matter which way you choose to get the job done that you try to understand the flow of execution of your script.  

Some questions I try to ask myself when I think I'm done with my scripts:
  • Where am I causing my script to stop and collect all of the input pipeline before continuing?  (sorting in the middle of the pipeline is the classic example of this)  Does it matter?
  • What variables am I declaring at the top level that can be moved so that they are deleted automatically when they leave scope?
  • What is the impact on readability?

Sunday, September 21, 2008

I'm Not Dead

I've been really busy at work but keep an eye out for a real post very soon. If you want to check out some PowerShell scripts head on over to http://poshcode.org, where you can find PowerShell scripts submitted by users.

There has been talk in the community about trying to come up with a CPAN for PowerShell.  This is not it, but it's a start.  For one thing, CPAN grew enough that they could enforce readability and technique requirements by having someone actually review each module.  PowerShell is still too young for that.

I decided to post my SMS.psm1 module for making command-line managment of SMS easier, so here goes nothing.  I'll do a post on how to use it later.






Note:  You need PowerShell v2 CTP2 in order to use this.   Copy it into %userprofile%\Documents\WindowsPowerShell\Packages\SMS\.