PowerShell Tools and Books That I Use

Tools I Use (note…these are all free!):

  • PowerGUI Script Editor (I haven’t ever gotten the hang of PowerGUI itself)
  • Powershell Community Extensions 1.2  (PSCX)
  • PowerTab
  • SQL PowerShell Extensions 1.61 (SQLPSX)
  • PrimalForms Community Edition

Books:

  • PowerShell In Action by Bruce Payette
  • Professional Windows PowerShell Programming: Snapins, Cmdlets, Hosts and Providers by  Arul Kumaravel et. al.
  • Mastering PowerShell by Dr. Tobias Weltner

I’ve tried a lot of other tools (several IDE’s, for example), but this is the list I keep returning to.

Getting Scheduled Tasks in PowerShell

There are a few approaches to manipulating scheduled tasks in PowerShell.

  • WMI – Useful if you are only going to manipulate them via script.  The tasks will not be visible in the control panel applet.
  • SCHTASKS.EXE – Works ok, but has a somewhat arcane syntax, and is a text-only tool.
  • Task Scheduler API -Best of both worlds, but only on Vista (not XP).

My current environment is an XP laptop, and hundreds of W2k3 and W2k8 servers.  I really need to be able to hit the tasks from the control panel (don’t have PowerShell installed everywhere…but that’s in process).  Since I have to use SCHTASKS.EXE , I figured I should write a PowerShell interface that makes things a bit easier.

The main thing I wanted was to get objects back.  Once you’ve started using PowerShell, you realize that “everything returns objects” makes for a very powerful scripting experience.  So, when you have to fall back to an external command like SCHTASKS.EXE, you really feel the pain.  The approach I took was to ask SCHTASKS for a verbose output (no reason not to get all of the properties), and also to write the output as CSV.  I then send the output into a file.  A problem that arises when trying to get PowerShell to import the CSV is that the column headers are not very friendly (e.g. “Repeat: Until: Time”).  So, before importing we need to “massage” a little bit.  I simply removed spaces (leaving Pascal-cased words) and replaced colons with underscores.  Another issue was that different versions of SCHTASKS.EXE would sometimes include an extra blank line in the file.  Finally, I decided that I should add some methods using Add-Member to allow me to run or delete the task without having to remember the syntax.

I hope you find this script useful.  If you have suggestions for improvements, or questions about how part of it works, feel free to leave a comment.

function get-tasks($server="", $taskname="",[switch]$help){
  if ($help)
    {
        $msg = @"
Get scheduled tasks from a remote server as objects.  Optionally you may supply a substring to be found in the task names.

Usage: get-tasks [Server] [substring] [-help]
ex:  get-tasks MACHINE1 LOGS    #to get all scheduled tasks from MACHINE1 containing LOGS
"@
        Write-Host $msg
        return
    }
    $filename = [System.IO.Path]::GetTempFileName()
 if ($server){
     schtasks /query /fo csv /s $server /v > $filename
 } else {
    schtasks /query /fo csv /v > $filename
 }
    $lines=Get-Content $filename
 if ($lines -is [string]){
    return $null
 } else {
        if ($lines[0] -ne ''){
   Set-Content -path $filename -Value ([string]$lines[0]).Replace(" ","").Replace(":","_")
   $start=1

  } else {
   Set-Content -path $filename -Value ([string]$lines[1]).Replace(" ","").Replace(":","_")
   $start=2
  }
  if ($lines.Count -ge $start){
   Add-content  -Path $filename -Value $lines[$start..(($lines.count)-1)]
  }
  $tasks=Import-Csv $filename
  Remove-Item $filename
  $retval=@()
  foreach ($task in $tasks){
   if (($taskname -eq '') -or $task.TaskName.contains($taskname)){
    $task.PSObject.TypeNames.Insert(0,"DBA_ScheduledTask")
    Add-Member -InputObject $task -membertype scriptmethod -Name Run -Value { schtasks.exe /RUN /TN $this.TaskName /S $this.HostName}
    Add-Member -InputObject $task -membertype scriptmethod -Name Delete -Value { schtasks.exe /DELETE /TN $this.TaskName /S $this.HostName}
    $retval += $task
   }
  }
  return $retval
 }
}

Links:

Why PowerShell?

Why am I writing a blog about PowerShell?  The answer is simple. I haven’t been as excited about a technology since I first learned SQL.  PowerShell allows me to do my job in a much more consistent, flexible, and scalable way.

What is it about PowerShell that has me so fascinated?

  • Ability to work with multiple technologies in a seamless fashion (.NET, WMI, AD, COM)
  • Dynamic code for quick scripting, strongly-typed code for production code (what Bruce Payette calls “type-promiscuous”)
  • High-level language constructs (functions, objects)
  • Consistent syntax
  • Interactive environment (REPL loop)
  • Discoverable properties/functions/etc. Example
  • Great variety of delivered cmdlets, even greater variety of community cmdlets and scripts
  • On a similar note, a fantastic community that shares results and research
  • Extensible type system MSDN
  • Everything is an object
  • Powerful (free) tools like PowerGUI, PSCX, PowerShell WMI Explorer, PowerTab, PrimalForms Community Edition, and many, many more.
  • Easy embedding in .NET apps including custom hosts.
  • The most stable, well-thought out version 1.0 product I’ve ever seen MicroSoft produce.
  • An extremely involved, encouraging community..

I have to admit that it wasn’t until the third time I looked at PowerShell that I finally “got it”.  I guess I didn’t have a problem in mind when I looked at it and was a bit overwhelmed by the syntax.  I think that trying to explain PowerShell as a “replacement for cmd.exe” really made it hard to see how all the new concepts (noun/verb combinations, for example) were going to pay off.  After all,cmd.exe is pretty painless to work with.  You can’t do much, so it’s not so hard.  With PowerShell, on the other hand, the sky’s the limit.

My first extensive use of PowerShell was to pull lists of shares from all of the servers that our group manages (about 450) along with Share and NTFS security.  Even to someone who hadn’t really ever used WMI, it was not too bad.

Once I had finished that, I started writing administration scripts for a purchased software package that used several different technologies and again spanned several servers.  We have about 60 different installations of this package on about 200 servers.  Managing them all through remote desktop and scheduled tasks was working, but was rapidly  becoming a maintenance nightmare.  PowerShell scripting has made the automation of most of our administrative tasks fairly painless.

I realize that this post was pretty low on technical details, but I hope to be following with several posts that include actual code. I’ve written several thousand lines of script and a custom PowerShell host, so I hope that something I write about is interesting and informative to you.

Feel free to comment if you have suggestions for topics you’d like to see me discuss.

Mike