Saturday, July 23, 2011

Control-Flow

PowerShell provides a comprehensive set of flow-control constructs, starting with if/elseif/else as follows:

$Value = 2
If ($Value -eq 1) {
Do-Something
}
ElseIf ($Value -eq 2) {
Do-SomethingElse
}
Else {
Do-SomethingCompletelyDifferent
}

Note that we have a completely different set of comparison and logical operators to remember as follows:


Equals -eq
Not Equal To -ne
Greater Than -gt
Greater Than or Equal To -ge
Less Than -lt
Less Than or Equal To -le
Not -not or !
And -and
Or -or

Additionally, if you're doing string comparison, you can force case-sensitive or case-insensitive (default comparison is case-insensitive) by prefacing the operator with a c or i e.g. -ceq is a case-sensitive equal comparison.

In addition to the if flow control, PowerShell also has:

  • Do While - Do {Code Block} While (Condition)
  • While - While (Condition) {Code Block}
  • Do Until -
  • For - For ($Index = 1; $Index -le 3; $Index++) {Code Block}
  • ForEach - ForEach ($MyObject In $MyObjectsArrayOrCollection) {Code Block}
  • Switch -
    Switch ($MyValue)
    {
        Result1 {Code Block}
        Result2 {Code Block}
        Default {Code Block}
    }

A couple of good articles on flow control are:

Friday, July 22, 2011

Use-Variable

I am pretty light on the details of variables, particularly around scope, expansion and built-in variables, but PowerShell does have them.  Variables are always prefaced with “$”.  Declaration and assignment of variables is as simple as:

$Number = 2
Or
$Files = Get-ChildItem

In the first case, we’re assigning the number 2 to the variable $Number.  In the second we’re assigning $Files an array of files in the current folder.  Remember that depending on the provider this may not be files.  In the case of:

SQL:\MYSQLSERVER\DEFAULT\Databases\MyDatabase\Tables
we’d be assigning an array of SMO table objects in the MyDatabase database.

I’m a little shaky on variable expansion.  For example:

$Subfolder = “MySubFolder”
Set-Location C:\MyFolder\$Subfolder

will set your current location to C:\MyFolder\MySubFolder.  If you have space in your path you could type:

Set-Location “C:\My Folder\$Subfolder”

and the value of $Subfolder will replace $Subfolder in the path.  However, I seem to have had cases where the string replacement doesn’t happen and I simply end up with a string “C:\My Folder\$Subfolder”

When in doubt, you can look to string concatenation as follows:

$Subfolder = “MySubFolder”
$Folder = “C:\MyFolder\” + $Subfolder

Or PowerShell’s equivalent of the .NET string.Format(string, arg1, arg2, …) as follows:

Set-Location (“C:\My Folder\{0}” –F $Subfolder)

Although you don't see it often, you can type variables by declaring them with a specific .NET type as follows:

[int]$MyInteger = 1

This will explicitly type the variable as an integer.  There are two special types of variables and literal assignments, arrays and hastables.  You can define and assign a literal array as follows:

$MyArray = @(1, 2, 3)
$MyValue = $MyArray[1]

This creates a 3 integer, zero-indexed array and then assign the second value, 2 to the variable $MyValue.  A hash table variable, or dictionary, creates a list of name/value pairs as follows:

$MyHashTable = @{"Value1" = 123, "Value2" = 456}
$MySecondValue = $MyHashTable["Value2"]

You can add a new value by simply assigning a value to an unused name as follows:

$MyHasTable["Value3"] = 789

Or remove a value using the .Remove method as follows:

$MyHashTable.Remove("Value2")

Don't forget you can do all of this interactively from the command line and viewing the value of the variable is as simple as typing the variable name at the command line and hitting enter.

As noted at the beginning, I’m still a little light on the details of variables.  I believe there are a number of scope rules around access to variables inside modules etc. that I am not familiar with so Google will be your friend with this.

A good article I found on variables is PowerShell Tutorial 7: Accumulate, Recall, and Modify Data.

Thursday, July 21, 2011

Process-Pipeline

PowerShell allows you to pipe the results of one command into the next.  It doesn’t simply pass along the text output but sends the entire resulting .NET object.  This may be an array or IEnumerable collection of objects.

For example, Get-ChildItem *.txt will return a list of files in the current folder.  However what it’s actually  returning is a list of System.IO.FileInfo objects.  Typing Get-ChildItem *.txt | Remove-Item will cause it to pass along that list of objects to the Remove-Item cmdlet which will subsequently delete them.

That’s not so interesting because you could have simply typed Remove-Item *.txt (or Del *.txt).  But what if you wanted to delete only text files larger than 10KB?  Try this:

Get-ChildItem *.txt | Where-Object {$_.Length -gt 10000} | Remove-item
So what’s going on here?  First we’re taking the output of the Get-ChildItem *.txt cmdlet which returns an array of System.IO.FileInfo objects representing all of the text files and passing it to the Where-Object.  One of the possible arguments for the Where-Object (aliased as Where) is a script block.

The script block here is {$_.Length > 10000}.  What’s going on here is that the Where-Object cmdlet is passing each input object (i.e. each FileInfo object) into the script block.  The passed in object is represented in the script block as “$_”.  We’re then testing to see whether it’s Length property is greater than 10000 using the –gt greater than operator and returning only those that evaluate to true. (Yes, PowerShell doesn’t use the standard operator symbols we’re used to but that’s another blog entry).

Next along we’re passing along the resulting text files bigger than 10KB to Remove-Item and deleting them.  Note that since we’re dealing with standard .NET FileInfo objects we could have used any of it’s properties such as LastAccessTime or Attributes.

Wednesday, July 20, 2011

WhatIs-PowerShell -?

PowerShell is Microsoft’s scripting alternative to ye olde DOS command line.  Although it shares many of it’s concepts, it it immensely more flexible and extensible.

It’s primary purpose is to allow system admins to accomplish more through a command line interface and scripts using a more standard command syntax than the collection of DOS commands, VBScripts and random executables that are available with the standard cmd.exe interface.

At it’s base it’s an interactive shell that allows you to type commands and view the results.  Like the DOS command line, you can create script files (.PS1 instead of .BAT).  Unlike the DOS command line you can load extensions in the form of Snap-ins and Modules (Snap-Ins are a version 1 concept that can still be loaded but have largely been replaced by Modules in version 2).

CmdLets & Providers

There are two important concepts in PowerShell, CmdLets and Providers.

CmdLets are commands you type at the command line such as Set-Location and Get-ChildItem.  Note that these can be aliased.  You can type Del *.txt and it will delete all text files in the current folder because Del is an alias of Remove-Item. 
You’ll notice that the non-aliased naming convention for CmdLets is Verb-Object.  Not only that, but if you define a CmdLet or function that doesn’t use one of the pre-defined verbs, PowerShell will yell at you, although it will still function correctly (You can get a list of approved verbs by typing Get-Verb).

Providers on the other hand allow you to navigate systems through a folder system.  The most obvious is the file system.  The File System provider allows you to type CD C:\Users (translates to Set-Location C:\Users) which will set your current location to the C:\Users folder.

However, unlike the DOS command line, we’re no longer limited to the file system.  Load up the SQL Server provider, type CD SQL:\MYSQLSERVER\DEFAULT\Databases\MyDatabase\Tables, type Dir (translates to Get-ChildItem) and you’ll get a list of the tables in the MyDatabase database on the default SQL instance on MYSSQLSERVER.   there are also providers for the registry and IIS, allowing for easy navigation.

Tuesday, July 19, 2011

Hello-PowerShell

So I’m going to go way off the data track for a few posts.  I’ve been working on a data integration project to better integrate our customer’s and our products.  As a SAAS company, I don’t need to write polished MSI installer for public consumption, but I do have to write an installer that will allow our IT staff to deploy and update thousands of instances used by our customers, and as my boss likes to put it, “works like gravity”.  It needs to have minimal touch points and minimal configuration to reduce the risk points. 
I considered MSIs, batch files, .NET apps, and a combination of the above with various command line utilities.  Ultimately, I decided that it was finally time to learn PowerShell.
After a headache inducing introduction, I’ve become quite comfortable with and somewhat enamored of it, for certain, sysadmin types of tasks, to the point that I’ve written initial versions of PowerShell modules in C# for the application to help simplify the automation/scripting of setting up integration configurations.  Although I didn’t completely get away from MSIs and command line utilities, PowerShell did eliminate the need for some and was able to tie the rest together much more nicely than would be possible with the old batch file.
I am by no means yet an expert, and based on some articles I've scanned I've barely scratched the surface, but over the next few blog entries will share some of the pleasure and pain I’ve experienced in working with it.
A couple of things that will greatly help you to start with.  First is Get-Help.  typing this followed by any cmdlet name will get you a brief description of the cmdlet, it’s syntax, list of arguments and related cmdlets.  Adding the –detailed argument to the Get-Help cmdlet will return detailed information on each of the arguments as well as examples.
If you don’t quite remember the name of the cmdlet, you can type in part of it and Get-Help will return a list of cmdlets containing that string.  e.g. searching on “Session” will return all of the session cmdlets.  Helpful when you forget that Get-PSSession has a “PS” in it.
Of course the next thing that will help is Google, but if you want to skip that there’s a couple sites that Google has taken me to several times:

Thursday, April 28, 2011

QUOTED_IDENTIFIER, Filtered Indexes, SSMS & SQLCMD

So I’ve been working on a project that utilizes filtered indexes and decided I needed to build an automated deployment routine.  I had built a set of build scripts, and utilizing SQLCMD mode in SSMS to allow for include files, built a master script to perform the entire build. 

During development and initial testing I ran this in SSMS and everything worked fine, but for automation purposes I used SQLCMD.EXE.  As soon as I started using SQLCMD Is started to get errors indicating that the QUOTED_IDENTIFIER was incorrectly set for indexed views, filtered indexes etc. 

Now I’m aware that indexed views among other objects are picky about their settings and the error message let me know that this was the case for filtered indexes as well.  However, the question was, why hadn’t it come up before?  In looking through the code I only found one reference where QUOTED_IDENTIFIER was set to OFF and that was in the create database script.  Even after changing that the errors were still showing up.

It turns out that by default, a connection through SSMS sets QUOTED_IDENTIFIER to ON, while a connection through SQLCMD sets it to OFF.  In SQLCMD it does this after each connect statement, so setting it at the beginning of a script may not cover it if you subsequently run a connect statement after it.  The best practice is to run the set statements after each connect.

Reading through the Remarks section of the following MSDN article,  SET QUOTED_IDENTIFIER (Transact-SQL), states that the defaults are set differently for different connection types (DB-Library, SQL Server Native Client, ODBC etc.) and thus indicates that SQLCMD and SSMS are probably using different connection types.

Tuesday, April 5, 2011

Debunking the Cult of the Machine SID

So this isn’t especially data oriented, but for any developer who is constantly creating new VMs for development or testing this info might come as a pleasant surprise.

Over the years, when cloning VMs, sometimes by simply copying the VHD files and attaching them to a new machine, we’ve been told that we’ll cause problems because we aren’t creating machines with unique SIDs.  As a result, we’ve had to use sysprep, sysinternals NewSID (discontinued) or the VM software of our choice’s cloning tool.  Turns out however, our obsession with SIDs was all for nought.  The following technet blog post by the author NewSID details why duplicate SIDs don’t matter:

The Machine SID Duplication Myth (and Why Sysprep Matters)

Note that it is still official Microsoft policy to require cloned machines to be syspreped, and as such I would recommend following that policy in a production environment, but for us developers, copy away and stop worrying about your SIDs!