Chad's profileChad Miller's BlogBlogListsNetworkMore Tools Help

Chad Miller's Blog

SQL Server, PowerShell and so on
Public folders
November 07

Tampa Code Camp 2009

I presented a 1 hr session at Tampa Code Camp 2009: PowerShell for Developers In this sesssion we will look at the a main use cases for developer usage of PowerShell. An overview of the PowerShell development model will be provided. Specific topics covered include: ETL, Testing, deployment automation, and management API.
 
My thanks to everyone in attendence. Feel free to post questions and comments. The presentation and supporting materials are available here:
 
November 01

Stored Procedure Call Tree

I was reading a post by Linchi Shea in which he demonstrates a Perl script to Find the complete call tree for a stored procedure and thought, how would I do this in PowerShell?

Before we dissect a PowerShell approach, let’s look at a basic Perl approach. In Perl you typically find a command-line tool that produces the output you want albeit not in a usable format (Sure, sometimes you might get lucky and find a Perl module that does what you want, however when I used Perl I was never that lucky). This may involve several command lines tools. You’ll then parse the output of the tool and perhaps even use the output as input to other command-line tools. This is one of the strengths of a scripting language, you can quickly glue together tools to make a new tool--The scripter is a tool smith.

The Perl solution to getting a stored procedure call tree involves executing osql.exe and using some regular expressions to parse the output of sp_helptext, looking for EXECUTE statements. When I wrote Perl I would do much the same thing. As an example when I needed to report the share and NTFS permissions using Perl for all SQL Servers, I would execute rmtshare.exe parse the output which was then used as input to fileacl.exe and the output was then parsed.

We could do much the same thing in PowerShell, execute osql.exe and parse the output, however in the vast majority of cases if you’re parsing something in PowerShell you’re doing it the hard way. This is because PowerShell works with .NET, WMI and COM and you’ll typically find a .NET/WMI class or COM interface to do exactly what you want to do. Of course the hard part is knowing which class to use, if you’re not sure search and then ask. If you’re unable to find the answer yourself, both ServerFault and StackOverflow are good places to post these types of questions.

There is a nice set of .NET classes for parsing T-SQL code included with Visual Studio Team System 2008 Database Edition (VSDB) in the classes Microsoft.Data.Schema.ScriptDom and Microsoft.Data.Schema.ScriptDom.Sql. I’ve blogged about these classes before and even use them in SQL Server PowerShell Extensions (SQLPSX). At this point, you’re probably thinking I don’t have VSDB, well that’s OK, because the assemblies are redistributable I’ve included them with SQLPSX and the accompanying download to this post. There’s only one problem with the VSDB assemblies, they are implemented as interfaces. Let me explain the issue.

When writing PowerShell scripts you will often work with .NET classes. You’ll create an object from a .NET class and start working directly with it’s properties and methods. SQL Server Management Objects or SMO is great example of this. In one line of PowerShell code you have instant access to hundreds of properties and methods for a SQL Server object:

$srv = new-object ("Microsoft.SqlServer.Management.Smo.Server") Z002\SQL2K8

As you can see with the example above its very simple to start working with a .NET class. There is however, one notable exception when dealing with interfaces.  This is kind of fringe use case, in most instances you won’t have to deal with interfaces in PowerShell. The best way I can describe an interface is a fancy abstraction thing developers use when creating classes. Only interfaces aren’t real classes and in order to use an interface you have to implement a class. The important thing to note, as a scripter an interface just means we need to create a class to use it and this requires a .NET language other than PowerShell. Fortunately PowerShell V2 through the add-type cmdlet provides a way for us to do this within PowerShell.

Using add-type we can create a dynamic type in  .NET languages like C#. This means you can write C# within PowerShell. The first time I saw this I thought,”And why would I want to do that?” And then I remembered the problem with interfaces and in a way this functionality goes back to the main purpose of scripting, to glue together tools to create a new tool. Only in this case the tool is a snippet of C# code! So, I created a script called SQLParser that implements the Microsoft.Data.Schema.ScriptDom and Microsoft.Data.Schema.ScriptDom.Sql interfaces with a basic C# class.

To use SQLParser, source the script file and create a SQLParser object by specifying the SQL version, whether quoted identifiers are used and some valid T-SQL:

. ./SQLParser.ps1
$sqlparser = new-object SQLParser Sql100,$false,"Select * from dbo.authors"

The SQLParser class returns a fragment, which then is made up of batches and finally statements. We can iterate through the statements looking for a particular statement type. Having accomplished the hard part of finding and implementing a .NET class for T-SQL parsing, I then created a PowerShell script called Get-ProcedureCallTree.ps1 to return a stored procedure call tree:

If you run the script with the following parameters for HumanResources.uspUpdateEmplyeeHireInfo in the AdventureWorks database replacing Z002\SQL2K8 with your server name, you’ll should see the following output:

.\Get-ProcedureCallTree.ps1 uspUpdateEmployeeHireInfo "Z002\SQL2K8" AdventureWorks HumanResources            
            
Server    : Z002\SQL2K8            
Database  : AdventureWorks            
Schema    : dbo            
Procedure : uspLogError            
Source    : Z002\SQL2K8.AdventureWorks.HumanResources.uspUpdateEmployeeHireInfo            
Target    : Z002\SQL2K8.AdventureWorks.dbo.uspLogError            
            
Server    : Z002\SQL2K8            
Database  : AdventureWorks            
Schema    : dbo            
Procedure : uspPrintError            
Source    : Z002\SQL2K8.AdventureWorks.dbo.uspLogError            
Target    : Z002\SQL2K8.AdventureWorks.dbo.uspPrintError

Not bad, but what I really want is to visualize the call tree. One method that works well for dependency maps is a script called Show-NetMap created by Doug Finke featured in his blog post PowerShell, Visualize the Peanut Butter Recall Data.. I’ve previously blogged about this script using it to create a complete database dependency map. To use Show-NetMap, source the script and pipe the source and target properties to Show-NetMap:

. .\Show-NetMap.ps1            
            
./Get-ProcedureCallTree.ps1 uspUpdateEmployeeHireInfo "Z002\SQL2K8" AdventureWorks HumanResources | `
Select Source,Target | Show-NetMap F

ProcedureTree

That’s better! The scripts and assemblies demonstrated in this post are available here:

October 21

Operations Manager Shell

I've started using SCOM 2007 R2, which has given me a chance to try out the Operations Manager Shell. The Ops Mgr Shell is a customized PowerShell initialized through a console file and startup script. The important thing to note is that it's still regular PowerShell.
 
What's interesting about the Ops Mgr shell is the use of  both a provider and cmdlets. You can navigate groups and objects like a file system drive plus there are some nicely thought out cmdlets that can act within the current context of objects returned by the provider through pipeline input.  As someone coming from SQL Server PowerShell I couldn't help but think this is how the SQL Server Powershell host should work i.e. cmdlets integrated with provider. Although many of the Ops Mgr cmdlets allow you to specify paths and objects as parameters for their cmdlets, I found it much easier to use the provider in conjunction with cmdlets--navigating to a group or object and then executing cmdlets to get or change properties.
 
I'm more of a SCOM user than a SCOM administrator, so my use cases are simple and have nothing to do with administering SCOM. The only things I need to do is get a list of alerts for a particular group or alert and put groups or machines in maintenance mode to avoid alerts during planned maintenance. Here are some scripts I've used:
 
#Navigate to the SQL Server computer group
PS Monitoring:\sunfish1\Microsoft.SQLServer.ComputerGroup           
#Get all unresolved alerts for the current group           
get-alert -criteria "ResolutionState = 0" | Select Name, Description, MonitoringObjectPath, MonitoringObjectName, TimeRaised            
           
#Get alerts for where the alert name is logical disk frag and export results to CSV file
get-alert -criteria "Name like 'Logical Disk Frag%'" | Select Name, Description, MonitoringObjectPath, MonitoringObjectName | export-csv c:\users\u00\bin\DiskFrag.csv -noTypeInfo            
           
#Put an entire group in maintenance mode for one hour
get-monitoringobject | New-MaintenanceWindow -startTime:$(get-date) -endTime:$((get-date).AddHours(1)) -comment:"Testing PowerShell script"           
#Put the Z002 computer in maintenance mode for one hour           
get-monitoringobject | where {$_.Name -eq 'Z002.acme.com'} | New-MaintenanceWindow -startTime:$(get-date) -endTime:$((get-date).AddHours(1)) -comment:"Testing PowerShell script"            
           
#Note as a DBA two other groups you should look at:
Microsoft.SQLServer.InstanceGroup            
Microsoft.Windows.Clusters
 

A few tips

  • I find myself using the alert script just to export what I'm seeing in my Operations Console (GUI) to a CSV file. I'll then send the simple PowerShell generated CSV reports to my team to address. If you know of an easier way to do this through the GUI, let me know.
  • The maintenance mode scripts are not as useful, it's easier to use the GUI, right-click, and select maintenance mode. Even multiple computers can be selected. I suppose if I had a regularly scheduled maintenance, I would find more uses for a scripted maintenance window solution.
  • It is surprising what SCOM finds, the most common warning condition I've seen include databases with autoclose or autoshrink on. In an active environment with thousands of databases a few inherited databases from SQL Express installation are bound to exist with either autoclose or autoshrink on. Once identified these warning are easy to fix.
  • One other interesting warning is logical disk defragmentation. I had do a little research about this warning and posted a question to ServerFault. I won't repost here; see the ServerFault Q&A for detailed explanation. As part of the question I created a short script to retrieve logical fragmentation through WMI and PowerShell:

 $vols = Get-WmiObject -computername "Z002" Win32_Volume -filter "DriveType=3"           
$defragInfo = $vols | %{$_.DefragAnalysis() | add-member -membertype noteproperty vname $_.name -passThru}           
$defragInfo | %{$_.DefragAnalysis | add-member -membertype noteproperty DefragRecommended $_.DefragRecommended -passThru | add-member -membertype noteproperty vname $_.vname -passThru} | out-file ./tmp.txt

The script can be executed remotely, retrieves the logical disk fragmentation for computer, Z002 and sends the output to a text file. The fragmentation numbers produced by the script seem to match what Ops Mgr produces. This illustrates one of the cool things about PowerShell, with a little work you can use scripting to better understand how management tools like SCOM work!
October 18

SQL Saturday #21

I presented a 1 hr session at SQL Saturday #21 session SQL Server PowerShell Extensions: In this sesssion we will look at automating common DBA tasks through Powershell. An overview of the CodePlex project SQL Serer Powershell Extensions will be provided. Specific topics covered include PowerGUI, SQL Server security/permissions reporting, SSIS administration, replication and agent tasks, performing ETL with Powershell."
 
The two part format with Max Trinidad worked out very well. There's just too much material cover in one session and get into some advanced stuff. My thanks to everyone in attendence. Feel free to post questions and comments.
 
The presentation and supporting materials are available here:
 
October 13

SQLServerCentral Article on Database Space Capacity Planning

I wrote an article for SQLServerCentral entitled "Database Space Capacity Planning" that demonstrates a database and volume (disk) capacity planning solution I use in my production environment. Some highlights of the solution:

  • Powershell is used to collect database and volume space information from a list of SQL Servers, which is then loaded into a consolidated reporting database
  • A series of queries are used to calculate a days remaining metric i.e. the number of days until volume or database file runs out of space.
  • Uses SQL Server Reporting Services (SSRS)  to provide self-service reports

See the full article for details.

October 10

SQL Saturday #21 and Tampa Code Camp 2009

A couple upcoming events I'll be presenting at...
 

SQL Saturday #21 on October 17th in Orlando

 
I'm amazed at how SQL Saturday has grown since the first one just a couple of years ago. Nearly 300 attendees have registered and there's still time to sign up for this free event. There will be over 50 sessions with good mixture of veteran and new speakers. I enjoy listening to polished presentations that have the look and feel of something I would see at one of the large conferences. Just as important I appreciate many of the first time speakers that remind me of one of my team meetings, listening to a fellow peer talk about a subject they feel passionate about.  I will be presenting a two-part session on SQL Server and Powershell with Max Trinidad.
 
In the week leading up to SQL Saturday there are several low cost one-day seminars. As many of you know I manage a DBA team, so I'm sending a couple people to the pre-event training. 
  

Tampa Code Camp 2009 on November 7th

Max and I are teaming up once again to deliver a two-part PowerShell session on Saturday, November 7th. Because Powershell is among other things a .NET language, Code Camps presents a great venue to host PowerShell sessions. Although my domain SQL Server at Code Camp I'll try to explain PowerShell outside the context of a SQL Server and present use cases for developers. You can register for this free event here
October 04

Southwest Florida Code Camp II

I presented a Powershel session at the SW Florida .Net Code Camp II. The session demonstrated the following uses cases for developer usage of Powershell:
  • ETL
  • Testing (PSUnit and WatIN)
  • Deployment Automation
  • Management API
The presentation and supporting materials are available here:
 
Max Trinidad presented 3 one-hour sessions, so we were able to basically run a Powershell track at the code camp! It was fun taking part in a one day Powershell event. My thanks to everyone in attendence and to the SW Florida .NET Developers Group for all the work that went into hosting code camp. Feel free to post questions and comments.
September 24

External MAML Help Files

One of the really handy improvevements in Powershell V2 is around creating help information for your script users. I often find myself having to read through script and function definitions to figure out how to use them (and this is for scripts I write!), which becomes a real pain as your functions libraries grow. Starting in version 2 we can use comment-based help in function or scripts.
 
Comment-based help is great for creating simple help information for scripts and smaller collections of functions, however for large groups of functions you should consider using external help files. The format of the external help files is called Microsoft Assistant Markup Langauge (MAML). The XML-based, MAML help file format is used by compiled snapins and modules to produce all of the wonderful help information you see when you type get-help <command name>.
 
Using external help files provide several benefits over comment-based help:
For the reasons above and since my CodePlex project, SQL Server Powershell Extensions contains over 100 functions, I've decided to create all of my help documentation in MAML files. For compiled cmdlets there is a very nice utility called Cmdlet Help Editor by Wassim Fayed that autogenerates MAML files. Unfortunately the utility does not work for scripts, so, I created a script called New-MAML that accepts either CommandInfo or FunctionInfo objects emitted from Get-Command. The New-MAML script is posted on Poshcode.
 
New-MAML uses a function called New-XML created by Joel Bennet. The New-XML function leverages LINQ to make quick work out of generating XML documents, in this case a MAML file. Here's an example of using the New-MAML script for a function called Test-ISpath that is part of the SSIS related functions provided in SQLPSX:
 
Having sourced the function test-ispath, call the New-MAML function:
$xml = ./new-maml test-ispath
$xml.Declaration.ToString() | out-file ./test-ispath.ps1-help.xml -encoding "UTF8"
$xml.ToString() | out-file ./test-ispath.ps1-help.xml -encoding "UTF8" -append
 
Once the XML/MAML file is generated, you'll need to manually edit the MAML by filling in the TODO items and the parameters options that are defaulted to false. The position parameter option will need to be changed in the generated MAML also. For compiled cmdlets place the MAML file in the same directory as the binary module or snapin dll. For script modules/functions include a reference to the External MAML file for each function. Unfortunately script modules require that you specify the path to your MAML for EVERY functionNote: You can have multiple function help items within the same MAML file.
 
Finally edit the the Test-IsPath function by adding the path to your XML file.
 
#  .ExternalHelp C:\Users\u00\bin\Test-ISPath.psm1-help.xml
function Test-ISPath
 ..Rest of function ...
 
Note: when you specify an external help file, you need to use an absolute path (except for language subfolder) with no variables as part of the path name. For instance I'll often use $scriptRoot = Split-Path (Resolve-Path $myInvocation.MyCommand.Path) in my scripts, however this will NOT work:
#  .ExternalHelp $scriptRoot\Test-ISPath.psm1-help.xml
 
Interestingly enough, you will still get default help generated even though it will fail to use the help file.

Having specified an absolute path, if you re-source the function or reload the module you'll be able to call get-help test-ispath 
 
 
September 19

SQLPSX Developer Blog

Powershell enthusiast, DBA and new SQL Server PowerShell Extensions developer Mike Shepard started a blog. Mike addresses a common question I've seem in the SQL Server forums on PowerShellCommunity.org of handling parameters in queries in his post, Executing SQL the Right Way in PowerShell,. A future release of SQL Server PowerShell Extensions will incorporate a series ADO.NET functions into a ADO.NET library or module.
September 08

MIF Busters

Management Information Format (MIF) files are formatted text files containing additional information about hardware and software components. The MIF format originated out of the Desktop Management Interface Standards in 1996, but has since been displaced by newer technologies including CIM. As an aside Microsoft's implementation of CIM and WBEM standards is WMI.
 
Why am I explaining MIF files? Even though the DMI standard was end of life in 2005, Microsoft System Management Server (SMS)/System Center Configuration Manager (SCCM) use MIF files to extend the information collected on managed devices. If you use SMS/SCCM, MIF files are still relevant.
 
The format of a MIF files looks something like this:
 
Start Component
        Name = "Acme Server Location"
        Start Group
                Name    = "Acme Server Location"
                ID      = 1
                Class   = "Acme Server Location"
                Start Attribute
                        Name    = "Admin Contact"
                        ID      = 4
                        Type    = String(50)
                        Value   = "Chad Miller"
                End Attribute
                Start Attribute
                        Name    = "Admin Phone"
                        ID      = 5
                        Type    = String(40)
                        Value   = "55500"
                End Attribute
        End Group
End Component
 
The above MIF file is used to assign contact information for a server and may contain additional information including location, asset tag, or server type. If SMS/SCCM has been configured to collect MIF files as part of its inventorying process, this additional information will be added to SMS/SCCM. Depending on your organization's use of MIF files there could be alot of information contained in these files. So, I thought I'd write a PowerShell script to parse MIF files.
 
One of the things that struck me about the MIF file format is its resemblance to XML. Instead of closing and ending tags, there are "Start" and "End" sections. Its almost as if a MIF file is an XML file stuck in a text file body.  Converting a MIF into XML seems like a logically approach in extracting the data in a useable format. The script below uses a series of replace and regex to transform a MIF file into a XML document. Once we have an XML document, we can select the properties and obtain the parent group and component attributes:
 
param ($fileName, $computerName=$env:ComputerName)

#######################
function ConvertTo-MIFXml
{
    param ($mifFile)

    $mifText = gc $mifFile |
    #Remove illegal XML characters
    % { $_ -replace "&", "&amp;" } |
    % { $_ -replace"'", "&apos;" } |
    % { $_ -replace "<", "&lt;" } |
    % { $_ -replace ">", "&gt;" } |
    #Create Component attribute
    % { $_ -replace 'Start Component','<Component' } |
    #Create Group attribute
    % { $_ -replace 'Start Group','><Group' } |
    #Create Attribute attribute
    % { $_ -replace 'Start Attribute','><Attribute' } |
    #Create closing tags
    % { $_ -replace 'End Attribute','></Attribute>' } |
    % { $_ -replace 'End Group','</Group>' } |
    % { $_ -replace 'End Component','</Component>'} |
    #Remove all quotes
    % { $_ -replace '"' } |
    #Remove MIF comments. MIF Comments start with //
    % { $_ -replace "(\s*//\s*.*)" } |
    #Extract name/value and quote value
    % { $_ -replace "\s*([^\s]+)\s*=\s*(.+$)",'$1="$2"' } |
    #Replace tabs with spaces
    % { $_ -replace "\t", " " } |
    #Replace 2 spaces with 1
    % { $_ -replace "\s{2,}", " " }

    #Join the array, cleanup some spacing and extra > signs
    [xml]$mifXml = [string]::Join(" ", $mifText) -replace ">\s*>",">" -replace "\s+>",">"

    return $mifXml

} #ConvertTo-MIFXml

#######################
ConvertTo-MIFXml $fileName | foreach {$_.component} | foreach {$_.Group} | foreach {$_.Attribute} | select @{n='SystemName';e={$computerName}}, `
@{n='Component';e={$($_.psbase.ParentNode).psbase.ParentNode.name}}, @{n='Group';e={$_.psbase.ParentNode.name}}, `
@{n='FileName';e={[System.IO.Path]::GetFileName($FileName)}}, ID, Name, Value

Running the above script named mifparser.ps1 on the Location.mif file shown earlier produces the following output:
 
 
 
That's nice, but what if you wanted to collect the MIF files from every server you administer. The default location for MIF files on SMS/SCCM managed computers is C:\WINDOWS\System32\CCM\Inventory\noidmifs on x86 machines or C:\WINDOWS\SysWOW64\CCM\Inventory\noidmifs on x64 machines. If you query the SMS/SCCM database you can produce a CSV file listing the server name and MIF file path. Run the following SQL query from SQL Server Management Studio connected to your SMS/SCCM database and save the output as a CSV file:
 
SELECT 
DISTINCT SMS_R_System.Name0,
CASE
WHEN SMS_G_System_OS.Name0 LIKE '%x64%' THEN '\\' + SMS_R_System.Name0 + '\c$\WINDOWS\SysWOW64\CCM\Inventory\noidmifs\*.mif'
ELSE '\\' + SMS_R_System.Name0 + '\c$\WINDOWS\System32\CCM\Inventory\noidmifs\*.mif'
END AS OS
FROM SMS_Acme.dbo.System_DISC AS SMS_R_System

JOIN SMS_Acme.dbo.Operating_System_DATA AS SMS_G_System_OS
ON SMS_G_System_OS.MachineID = SMS_R_System.ItemKey

You can then use the CSV file as input to the mifparser.ps1 script:
 
import-csv C:\bin\servers.csv | %{$server = $_.server; get-childitem $_.path | select fullname, @{n='server'; e={$server}}} | %{c:\bin\mifparser.ps1 $_.fullname $_.server} | export-csv ./mif.csv -noTypeInformat
 
Now that's better, I now have a consolidated CSV file with all the MIF file information. I've parsed a few hundred MIF files without issues. I can then load the CSV file into a SQL table for further analysis.
 
CREATE TABLE [dbo].[mif](
        [SystemName] [varchar](1000) NULL,
        [Component] [varchar](1000) NULL,
        [Group] [varchar](1000) NULL,
        [FileName] [varchar](1000) NULL,
        [ID] [varchar](1000) NULL,
        [Name] [varchar](1000) NULL,
        [Value] [varchar](1000) NULL
);
 
BULK INSERT dbautility.dbo.mif
   FROM 'C:\Users\u00\Desktop\mif.csv'
   WITH ( FIELDTERMINATOR =',',
         ROWTERMINATOR ='\n')
 
 
September 01

SQLPSX 1.6.1 Release

I completed a maintenance release of SQL Server PowerShell Extensions, which address all known open issues. This release is still PowerShell V1, but I plan on working on a V2 release soon. The only notable change is in LibraryShowMbrs, which is used to recursively enumerate local and AD groups I know Quest makes a nice set of free cmdlets that provide this funcitonality. The Quest cmdlets are very good, I use them, however I chose to roll my own for two reasons. First, I did not want to build in a dependency for 3rd party cmdlets. Second, I want to be able to use SQLPSX from within the SQL Server 2008 PowerShell host, which does not support additional cmdlets--only scripts like SQLSPX.
 
The change in LibraryShowMbrs is in the way used to obtain group members. For several years I've used the WMI class Win32_GroupUser. Until recently this had been a reliable WMI class, however for some reason the class simply stopped returning even the name of sub groups and instead only user accounts are returned. I'm not really sure why and I haven't been able to find documentation on this change. I guess not finding documentation is not too suprising since I doubt many folks use WMI to enumerate local and AD groups. The library was re-written to use WinNT Provider and I found this post from Kristopher Bash helpful in creating the code.
 
I want to thank Jorge Seggara (@sqlchicken on Twitter) for helping to find several of the bugs addressed in this release. One of the issues fixed was a missing assembly that I did not find because I was loading it in my profile. I load a lot of things in my profile including initializing the SQL Server 2008 cmdlets and providers in my regular PowerShell using this script that contained the assembly I needed to include in one of the SQLPSX scripts. Due to this issue, I've learned a couple of things, first I need to test using the -noprofile switch. This will best mimic a clean PowerShell installation and I would encourage all script developers to do the same with scripts they distribute. Second, I need to do a better job testing. As scripts become complex its time to look at a more displined form of testing. One tool I learned about in a recent Episode of the PowerScripting Podcast called PSUnit. I'm going to check it out.

Internet Explorer Automation with WatIN

At my workplace I use an IT Service Desk application, called well, "Service Desk" from CA. The system is web-based and provides various queues for Change Orders, Requests and Incidents. Service Desk is used as used as workflow system instead of email. I'm sure this is a pretty common practice for many large IT shops, although they may use a different application. The Service Desk application is one the applications I like to keep open all day, however there is a 60 minute inactivity timeout. By doing any activity within a 60 minute timeframe you avoid timing out, even something simple like clicking an update count button. 
 
 
 
So, I thought I would create a PowerShell that would click an update count button every 50 mintues. I've done some scripting using Internet Explorer COM automation and PowerShell. This involves creating an  InternetExplorer.Application object and reading through the page source to find the HTML elements I want to select. I soon discovered issues pragmatically getting to the button through this technique due to Service Desk's heavy used of nested frames within nested frames. I decided to go another route using WaitINWaitIN Recorder and PowerShell.
 
Joel Bennet has a post on Using PowerShell and WatiN, which I found helpful. Although I did not use the functions he created, his post provides a quick introduction to PowerShell and WaitIN.  Running WaitIN requires starting PowerShell in STA mode, so this is a PowerShell V2 only script. To start PowerShell in STA mode run:
 
powershell.exe -STA
 

Getting Started

  1. Download WatIN
  2. Install WatIN Recorder
Usually working with PowerShell you'll create an object and explore it's property and methods, however in this case trying to find the button name was a little difficult. This is where WatIN Recorder helps out. After you've installed WatIN Recorder, run as a Administrator and navigate to the URL you want to automate:
 
 
Next click the record button and click the HTML element you want to automate. Then stop the WatIN recorder and click copy code to clipboard icon. This will produce some C# code that just needs to be translated into PowerShell:
 
// Windows
WatiN.Core.IE window = new WatiN.Core.IE();

// Frames
Frame frame_sd_scoreboard = window.Frame(Find.ByName("sd") && Find.ByName("scoreboard"));

// Model
Element __imgBtn0_button = frame_sd_scoreboard.Element(Find.ByName("imgBtn0_button"));

// Code
__imgBtn0_button.Click();
window.Dispose();

So, I now know the name of the button and that it is 3 frames deep. A little WatIN object exploration later, I came up with the follow script, which clicks a button every 50 mintues.
 
#Requires -version 2.0
#powershell.exe -STA

[Reflection.Assembly]::LoadFrom( "$ProfileDir\Libraries\WatiN.Core.dll" ) | out-null
$ie = new-object WatiN.Core.IE("https://sd.acme.com/CAisd/pdmweb.exe")
$scoreboard  = $ie.frames | foreach {$_.frames } | where {$_.name -eq 'sd'} |  foreach {$_.frames } | where {$_.name -eq 'scoreboard'}
$button = $scoreboard.Element("imgBtn0_button")

while ($true)
{
    $button.Click()
    #Sleep for 50 minutes
    [System.Threading.Thread]::Sleep(3000000)
}
 
August 25

Continuous Integration in a Development Environment using Red Gate Backup and SQL Compare

My co-worker and fellow DBA, Leigh Freijo has a blog entry about a Powershell script he created to re-integrate QA/Development database changes using Red Gate SQL Backup and Red Gate SQL Compare snapshot files. The script providers a wrapper around Red Gate's command line tools. This is a good demonstration of the strength of any scripting language -- The ability to glue together existing tools to make another tool. Nice work Leigh!
August 19

Tampabay SQL Server User Group Presentation

I presented a one-hour session for Tampabay SQL Server User Group on Powershell and SQL Server. I've previously delivered this session for the Sarasota and Orlando SQL Server User Groups. The presentation and supporting materials are available here:
 
My thanks to everyone who attended and all of your great feedback.
August 15

Installing SQL Server 2008 on Windows 7

When Installing SQL Server 2008 on Windows 7 RTM, I encountered a couple of issues. Supposedly you can install SQL Server 2008 RTM on Windows 7. You'll receive various warnings about compatibility issues which can be ignored and are fixed with the application of a SQL Server 2008 service pack. So, I attempted to install SQL Server 2008 RTM, this was somewhat successful, the database engine, integration services and reporting services installed, however the various client tools including SQL Server Management Studio did not install. So, I tried to install SQL Server 2008 Service Pack 1 and again received an error. Next I tried adding client tools to SQL Server 2008 RTM -- no luck.
 
Rather than wade through the SQL Server installation logs, I decided to start fresh by uninstalling SQL Server 2008 and try slipstreaming. The slipstreaming ability is a new feature added to SQL Server 2008 were you can merge the RTM bits with and updated service pack. When you install a slipstreamed SQL Server installation, the service pack is included. I followed the instructions on this msdn blog, re-ran setup using my new slipstreamed SQL Server 2008 drop and everything installed fine.
August 12

ThrowAway Scripts

Although I tend to write a lot of formal scripts, as part of the development process I'll explore an object looking at its data and available properties/methods using get-member. I'll then test individual pieces before glueing things together. However sometimes it isn't even necessary to write a well-crafted script and instead  I'll create a so-called "throwaway script" to accomplish a specific task. These scripts are especially useful when you will only need to run something once and the time to create a script out weighs the time to do the thing manually. Case in point, I had a need to extract the email addresses from an Exchange 2003 public folder with 100+ emails for an upcoming PowerShell club meeting.
 
I've done a little bit of work with Outlook COM objects. There's probably a way to do this on the Exchange side, but this works well enough and in about 10 minutes I created the following script:
 
$outlook = new-object -comobject "Outlook.Application"
$nameSpace = $outlook.GetNamespace("MAPI")
$folder = $nameSpace.GetDefaultFolder(18)
$posh = $folder.Folders | ?{$_.name -eq "Technology"} | %{$_.Folders} | ?{$_.name -eq "Public"} | %{$_.Folders} | ?{$_.Name -eq "Powershell RSVPs"}
$posh.Items | select SenderEmailAddress
 
A quick note about the GetDefaultFolder method, 18 is the enum that represents public folders. You can change the value to another default folder including Inbox and Sent items, if needed. The list of available values can be found on here.
 
I thought this was an interesting script, hopefully it saves someone a few minutes of time should they need to do a similar task.
August 08

SQLSaturday #16 South Florida Downloads

I presented a 1 hr session at SQL Saturday #16 session PowerShell and SQL Server Administration: In this sesssion we will look at automating common DBA tasks through Powershell. An overview of the CodePlex project SQL Serer Powershell Extensions will be provided. Specific topics covered include SSIS administration, replication and agent monitoring, performing simple ETL with Powershell and building graphs, charts and diagrams using Powershell."  
 
The presentation and supporting materials are available here:
 
 
The two part format with Max Trinidad worked out well and we were able to cover alot of ground between our back-to-back sessions. My thanks to everyone in attendence. Feel free to post questions and comments.
 
 
August 02

Building PowerShell GUIs with Primal Forms

Although PowerShell is best suited for console applications there are times when a GUI interface just makes sense, however hand cranking PowerShell code to display a GUI seems almost anti-productive when we are used to rich IDE's with WYSIWIG development available in other programming languages. Fortunately there are several 3rd party tools like PowerGUI, CodePlex projects like Powerboots as well as many great data visualization scripts which reduce the amount of time to build GUIs in PowerShell. In this blog post we'll look at another option for buidling GUIs, a free utility from SAPIEN called Primal Forms
 
To demonstrate Primal Forms, I thought it would be interesting to create a basic CRUD form for a SQL Server table, so I adapted the example on this MSDN page to PowerShell. Although the example uses the authors table in the sample pubs database, I can think of many real-world applications with tables used solely by sys admins to control user access or store configuration data about an application. In order to provide an interface to security and configuration tables a web front end could be developed, PowerShell scripts or cmdlets created or an MS Access front end could be used. All true, but I think the use of PowerShell and a WinForm datagridview provides a light-weight alternative that is completely accessible to PowerShell savvy admins.
 
Primal Forms is a simple IDE for building WinForms; if you've used Visual Studio, the form development feels similar. In the screenshot below I created a form with three controls: a dataGridView, and two buttons (reload and submit). Once you've created the form you can save the form definition in an XML format for later editing within Primal Forms. When you're ready to create a PowerShell script, select Export to PowerShell. Primal Forms creates all the necessary PowerShell code for the WinForm and controls which you can save to a .ps1 file. In the example that follows, I've named the script dataGrid.ps1
 
Primal Forms IDE
 
All that's left to do is add event handling to the form load and various forms controls (buttons). Note the actual user of the PowerShell script does not need to have Primal Forms installed. Primal Forms simply generates 100% compliant PowerShell code for WinForms. Adding the event handling must be done in a text editor. Open the newly created dataGrid.ps1 file in your PowerShell script editor of choice. Primal Forms creates placeholders for the events. In this example the following TODO placeholders are generated:
  
#Provide Custom Code for events specified in PrimalForms.
$Form1_Load=
{
#TODO: Place custom script here

}

$submitButton_Click=
{
#TODO: Place custom script here

}

$reloadButton_Click=
{
#TODO: Place custom script here

}
 
To populate the datagridview and events for the submit and reload buttons add the following code:
 
#endregion Generated Form Objects
$bindingSource1 = new-object System.Windows.Forms.BindingSource
$dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$serverName = "$env:computername\sqlexpress"
$databaseName = "Northwind"
$query = 'select * from Customers'

#----------------------------------------------
#Generated Event Script Blocks
#----------------------------------------------
#Provide Custom Code for events specified in PrimalForms.
$Form1_Load=
{
    $dataGridView1.DataSource = $bindingSource1
    $connString = "Server=$serverName;Database=$databaseName;Integrated Security=SSPI;"
    $dataAdapter.SelectCommand = new-object System.Data.SqlClient.SqlCommand ($query,$connString)
    $commandBuilder = new-object System.Data.SqlClient.SqlCommandBuilder $dataAdapter
    $dt = New-Object System.Data.DataTable
    [void]$dataAdapter.fill($dt)
    
    $bindingSource1.DataSource = $dt

    $dataGridView1.AutoResizeColumns([System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::AllCellsExceptHeader)
}

$submitButton_Click=
{
    $dataAdapter.Update($bindingSource1.DataSource)
}

$reloadButton_Click=
{
    $dataGridView1.DataSource = $bindingSource1
    $connString = "Server=$serverName;Database=$databaseName;Integrated Security=SSPI;"
    $dataAdapter.SelectCommand = new-object System.Data.SqlClient.SqlCommand ($query,$connString)
    $commandBuilder = new-object System.Data.SqlClient.SqlCommandBuilder $dataAdapter
    $dt = New-Object System.Data.DataTable
    [void]$dataAdapter.fill($dt)
    
    $bindingSource1.DataSource = $dt

    $dataGridView1.AutoResizeColumns([System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::AllCellsExceptHeader)
}
 
One minor issue with adding custom code, if you later go back to edit the form in Primal Forms you'll have to re-add your custom code. Since the form definition is saved in seperate file this isn't a big deal, although it would be nice if Primal Forms stored allowed you to store the custom code.
 
Running the script ./datagrid.ps1 produces the following output
 
 
You can insert new rows or update existing rows and then click submit button to commit the changes to the SQL Server table. To delete rows highlight a row by right clicking and pressing the shift key and then the delete key. 
 
As a finishing touch I've created a cmd file to call PowerShell and execute the script with the following parameters:
 
powershell -noprofile -Noninteractive -command c:\users\u00\bin\dataGrid.ps1
 
If you load a lot of functions and snapins in my profile like I do, the -noprofile option starts PowerShell much faster. This is useful if all you want to do is load the WinForm and don't happen to have a PowerShell console open. As an added bonus, the cmd file allows you to launch a simple application with a double-click without first starting PowerShell.
 
Next Steps -- Get Primal Forms, download the sample code and create GUIs!
 
 
July 27

SQLSaturday #16 South Florida

I'm giving a presentation at SQLSaturday in South Florida on August 8th 2009. If you haven't been to a SQLSaturday, they're free one-day events put on by local SQLPASS chapters. As an added bonus there will be two PowerShell sessions:
 
PowerShell MVP and prolific presentor, Max Trindad will be presenting "PowerShell and SQL Server: Learn how DBA's and Developers can take advantage of this new .NET scripting technology - PowerShell 2.0. This is a "most" to learn IT/.NET skill. In this session will discuss the difference between using SMO and the new integrated SQLPS in assisting managing your SQL Server 2000/2005/2008."
 

I will present a session on "PowerShell and SQL Server Administration: In this sesssion we will look at automating common DBA tasks through Powershell. An overview of the CodePlex project SQL Serer Powershell Extensions will be provided. Specific topics covered include SSIS administration, replication and agent monitoring, performing simple ETL with Powershell and building graphs, charts and diagrams using Powershell."  

If you can attend both sessions we're planning a part 1 and part 2 format. This should be another great community event with plenty of PowerShell content and there's still time to register.

 
July 24

SQL Server Powershell Extensions PowerGUI PowerPack

If you've been working with Powershell, you most certainly have heard of PowerGUI, for those of you who haven't PowerGUI is a free utility provided by Quest. It's both a script editor with PowerShell syntax highlighting and GUI builder for PowerShell scripts. The editor is a very usable standalone component which I recommend to folks looking for a good PowerShell script editor. The other standalone GUI component returns the results of PowerShell commands and scripts in a grid format,  additional actions and links can be created on the objects displayed. PowerGUI  provides a quick method  to create simple GUIs in PowerShell which kind of have the look and feel of MMCs. PowerGUI has a bunch of management packs they call PowerPacks that address various technologies including AD, Exchange and SQL Server. All of this is usable out of the box without writing any code.
 
Should you want to build your own GUIs, there are a couple of ways you can approach PowerGUI development. You could create PowerShell scripts and functions directly in PowerGUI. Alternatively, simply call your functions which have been sourced in your profile. I've choosen to do the later and created a PowerPack for SQL Server PowerShell Extensions. Here's a few screenshots:
 
Server
 
Databases
Integration Services
 
As a DBA, I can see the potential for using PowerGUI to create administration views for areas outside of Database Administration. For many years I've thought it would be nice to have a scaled down Enterprise Manager or SQL Server Managment Studio tailed for particular job functions such as junior DBAs, computer operators, or login provisioning. With PowerGUI it would trivial for a DBA to create such a view. So, try out PowerGUI yourself, customize it for your needs and leave some feedback.
July 15

Orlando PASS User Group Presentation

I presented a one-hour session for Orlando PASS User Group on Powershell and SQL Server. I had previously given this talk for the Sarasota SQL Server User Group. The presentation and supporting materials are available here:
 
I'd like to thank Jack Corbett and Andy Warren for inviting me to speak and for the wonderful questions/feedback.
July 08

Powershell Charting with MS Chart Controls

Richard MacDonald's demonstrates using Microsoft Chart Controls with Powershell in his post, Charting with Powershell. The chart controls are free and work with standard Windows forms. Another nice thing is the ability data bind to any object that implements IEnumerable (arrays, hashes, data tables, etc.). This makes working with the charts particularly easy, just create a hashtable and bind it to the chart data series. Jeffery Snover provides us with a useful bit of code called ConvertTo-Hashtable which does what the name implies. Armed with this information, I thought it would interesting to take the concept of Powershell charting a few steps further and create a reusable charting library, called LibraryChart ***Updated 9/20/09*** available on Poshcode. The library implements several features including:
  • Pipe the output of a Powershell command to automatically create a chart.
  • Display the chart in a Windows Form
  • Save the chart to an image file
  • Specify either bar, column, line or pie chart types
  • Display real-time automatic updating charts by passing scriptblock to the function. The scriptblock will execute at the specified interval and display chart updates (think Perfmon)
  • Works with Powershell V1

To use the Library (Note: Requires .NET 3.5 framework):

Here are a few examples:

Create a column chart of process workingset information:

Get-Process | Sort-Object -Property WS | Select-Object Name,WS -Last 5  | out-chart -xField 'name' -yField 'WS'

Save the chart to a file instead of displaying:

Get-Process | Sort-Object -Property WS | Select-Object Name,WS -Last 5 | out-chart -xField 'name' -yField 'WS' -filename 'c:\users\u00\documents\process.png'

Get-Process | Sort-Object -Property WS | Select-Object Name,WS -Last 5  | out-chart -xField 'name' -yField 'WS' -chartType 'Pie'

Produce a real-time line chart of process working set by passing a scriptblock i.e. the Powershell command between the two curly brackets. (Image note shown):

out-chart -xField 'name' -yField 'WS' -scriptBlock {Get-Process | Sort-Object -Property WS | Select-Object Name,WS -Last 1} -chartType 'line'

I'm not entirely happy with the script (uses global variable, hash generation code repeated, pie and line chart appearance could be improved), so if anyone would like to take the charting library even further go for it!

July 01

Powershell SQL Server Backup/Restore

I posted a script on Poshcode for doing backups and restores of SQL Server databases using SMO. The script is adapted from SQL Server Powershell Extensions functions of the same name. SMO 9.0 (2005) and 10.0 (2008) have slightly different methods and properties at times. For the most part there is very little difference, however for the backup class there is one big difference--the backup class was moved from the base SMO assembly to the SMOExtended assembly. In order to account for using either assembly, the script does a couple of things:
 
  • Load the SMO version into a global variable $smoVersion which is then used in later sections of the code
  • Load the SMOExtended assembly in all cases. On a system with just SMO 9.0 the SMO Extended assembly will not be present, but that's OK. Loading a non-existent assembly does not produce an error in Powershell. Surprising, but true, try it yourself by misspelling an assembly name.
The other thing the script does is make use of the special error handling needed for SMO due to the use of innerExceptions.
 
Looking at the Powershell + SMO script you can't help but think, how much simplier backup and restores are in T-SQL.  Here's the equivalent T-SQL command to backup a database:
 
BACKUP DATABASE AdventureWorks TO DISK = 'C:\backup\adventureworks.bak' WITH FORMAT;
 
In this case T-SQL would be much easier and less code to accomplish the same task of backing up or restoring a database.
June 28

SQLPSX Release 1.6

I completed Release 1.6 of SQLPSX which adds support for SQL Authentication and addresses several issues. SQLPSX consists of 106 functions, 2 cmdlets and 12 scripts for working with SMO, Agent, RMO, SSIS and SQL script files.
 
Here's an example of using SQL Authentication:
 
$server = Get-SqlServer 'Z002\SQL2K8' 'sa' 'mypassword'
 
Because Get-SqlServer, Get-AgentJobServer, and Get-ReplServer support either Windows or SQL Authentication you can pass a server object to the additional functions. Here's an example of getting databases from the server variable creating above:
 
Get-SqlDatabase $server | Select name
 
Many of the functions support shortcuts when using Windows authentication. Because Windows authentication is used we do not have to first get a reference to a server object. manually Here's the same example getting databases using Windows authentication:
 
Get-SqlDatabase 'Z002\SQL2K8'
 
Other than the addition of SQL authentication support, SQLPSX 1.6 is largely a maintenance release. I've replaced calls to WMIC with Get-WmiObject and addressed issues documented in the Issue Tracker. I've also incorporated the error handling technique Allen White provided into several functions in order to provide better error reporting. I had hoped to have a provider for SSIS completed, however I ran into a few issues with weak support of functionality in the SSIS API. If the SQL product team doesn't include an SSIS provider in SQL Server 2008 R2, I'll look at creating one then.
 
With Release 1.6 complete I'm planning on the next release, 2.0, to re-implement the V1 functions as advanced functions in Powershell V2.
 
 
June 24

Powershell + SMO Error Handling Tips

Allen White posted a helpful post on SMO error handling with Powershell. Actually the same concept equally applies to SMO coded in any other .NET language. SMO uses an error object's InnerException which can be several layers deep and you must traverse the nested InnerExceptions to get to the detailed error message. Related to Allen's post, Michiel Worries gives us another useful technique to get back full error messages. If you find your Powershell scripts are throwing too generic of a error message, you may find some useful messages in InnerException.
 
Links:
 
 
 
 

Custom HTML

Chad Miller