Tutorial – Installing PowerShell

Unfortunately there are a few different ways to install PowerShell, depending on the version of Windows. This tutorial provides the installation steps that are appropriate for your operating system.

Note: Before attempting to install PowerShell on a computer, confirm that it isn’t already installed!

PowerShell version 1.0 was available for installation by itself. PowerShell version 2.0 is installed as part of the Windows Management Framework. Don’t worry… WinRM is disabled by default, so this update can be safely installed without affecting the security of Windows.

Steps Common to Each Windows Operating System

There are a few requirements for installing PowerShell on Windows:

  1. Confirm that you have sufficient security privileges for installing software on Windows
  2. Verify that the pre-requisite—version 2.0 of the .NET Framework—is installed (and patched)
  3. Make sure that your system is up-to-date with its Service Pack and patches:

    Windows Version Minimum Service Pack
    Windows Server 2008 Service Pack 2
    Windows Vista Service Pack 2
    Windows Server 2003 R2 Service Pack 2
    Windows Server 2003 Service Pack 2
    Windows XP Service Pack 3

Installation on Windows 7 or Windows Server 2008 R2

PowerShell should already be installed and enabled. Inability to run PowerShell on a Windows 7 or Windows Server 2008 R2 machine indicates a problem, or it has been removed/locked down by your system administrator.

Installation on Windows Server 2008 or Windows Vista

  1. Review the information on the Windows Management Framework Core update
  2. Download the update package (“Windows6.0-KB968930-*.msu”) that is appropriate for your computer’s processor architecture
  3. Double-click on the “Windows6.0-KB968930-*.msu” file (that you just downloaded) to install it

Installation on Windows Server 2003

  1. Verify that version 2.0 of the .NET Framework is installed:

    1. Open the Windows® Run dialog box

      1. Press the Windows key + R key keys
    2. Type Cmd.exe into the Open text box and press the Enter key key
    3. At the command prompt, type the following command line and press the Enter key key:

      dir %SYSTEMROOT%\Microsoft.NET\Framework\ /AD /B

    4. The .NET Framework pre-requisite is satisfied if the dir output of the command includes a substring of “v2.0.50727″. Otherwise, download and install version 2.0 or later of the .NET Framework

      • You could (and probably should) install the latest version (each release of the .NET Framework includes each of the ones that precede it)
      • Be sure to check for and apply Windows updates afterward; there are Service Packs and patches for each version of the .NET Framework!
  2. Review the information on the Windows Management Framework Core update
  3. Download the installation program (“WindowsServer2003-KB968930-*-ENG.exe”) that is appropriate for your computer’s processor architecture
  4. Double-click on the “WindowsServer2003-KB968930-*-ENG.exe” file (that you just downloaded) to install it

Installation on Windows XP

  1. Verify that version 2.0 of the .NET Framework is installed:

    1. Open the Windows® Run dialog box

      1. Press the Windows key + R key keys
    2. Type Cmd.exe into the Open text box and press the Enter key key
    3. At the command prompt, type the following command line and press the Enter key key:

      dir %SYSTEMROOT%\Microsoft.NET\Framework\ /AD /B

    4. The .NET Framework pre-requisite is satisfied if the dir output of the command includes a substring of “v2.0.50727″. Otherwise, download and install version 2.0 or later of the .NET Framework

      • You could (and probably should) install the latest version—each release of the .NET Framework includes each of the ones that precede it
      • Be sure to check for and apply Windows updates afterward; there are Service Packs and patches for each version of the .NET Framework!
  2. Review the information on the Windows Management Framework Core update
  3. Download the installation program (“WindowsXP-KB968930-x86-ENG.exe”)
  4. Double-click on the “WindowsXP-KB968930-x86-ENG.exe” file (that you just downloaded) to install it

Installation on Windows 2000

PowerShell cannot be installed on Windows 2000it’s time to upgrade!

Posted in PowerShell | Tagged , , , , , , , , , , , , , | Leave a comment

Tutorial – Determining whether PowerShell is installed

The first step in using PowerShell is making sure that it’s installed on the computer(s) that need it. As time passes, it will become as ubiquitous as Cmd.exe and VBScript. However, it’s currently important to verify that PowerShell is available when getting started.

Beginning with Windows 7 and Windows Server 2008 R2, PowerShell is installed and enabled by default. As people realize its significance, more and more administrators and power users are installing PowerShell on earlier operating systems. There’s a good chance that PowerShell is already installed on your computer, even if you didn’t put it there yourself!

Here are the steps for verifying that PowerShell is ready to use:

  1. Open the Windows® Run dialog box

    1. Press the Windows key + R key keys
  2. Type PowerShell.exe into the Open text box and press the Enter key key
  • If PowerShell is installed, a console window will be displayed:

  • If PowerShell is not installed, you will see the following error message:

    Windows cannot find 'PowerShell.exe'. Make sure you typed the name correctly, and then try again.

    That means it’s time install PowerShell!

Posted in PowerShell | Tagged , , , , | 1 Comment

Reflecting on my first CodeStock session

Today I gave my first of two presentations at the 2011 CodeStock conference, my “An Introduction to Profiler and SQL Trace” talk. The session went really well! It had good attendance, the audience was engaged and asked great questions, and I am pleased with my delivery. I also received the kind of feedback that justifies all of the work invested… an audience member, Stephen Horne (blog, Twitter), said the following (in-person and on Twitter):

I really enjoy seeing people talking passionately & with significant knowledge about #SQL Server. @SQLServerSleuth #codestock.

During the session, I state that the following two ideas are the most important things to take away from the presentation:

I spend a long time during the presentation warning against using Profiler (in production) and explaining its performance impact (as well as the overhead of tracing). Still, Profiler can be a useful tool for defining trace sessions and analyzing trace data. Here is the proper workflow for reaping its benefits without shooting yourself in the foot:

  1. On your workstation, use Profiler to define the trace (event classes, columns, and filters)
  2. Use Profiler to generate a Transact-SQL script file with the system stored procedure calls for a server-side trace:

  3. Edit the script, including placing limits on the size and duration of the trace (using the @maxfilesize and @stoptime parameters of the [sys].[sp_trace_create] system stored procedure, as demonstrated in the presentation’s “Demo 07_Create a server-side trace to provide information on the next deadlock(s),2005+.sql” file)
  4. Execute the script on the server that is under investigation
  5. If the trace automatically stops more quickly than expected, refine the trace definition to be more conservative
  6. Once you have several trace files with useful data, compress them
  7. Transfer the archive of compressed trace files from the target server to your workstation
  8. Analyze the trace data using the [sys].[fn_trace_gettable] system function and/or using Profiler
Posted in SQL Server | Tagged , , , , , , , , , | Leave a comment

I am speaking at CodeStock 2011

I will be delivering two sessions at this year’s CodeStock conference in Knoxville, Tennessee:

If you happen to be attending, please stop by and visit!

Posted in PowerShell, SQL Server | Tagged , , , , , , , | Leave a comment

Dealing with full transaction logs

In a recent post, I mentioned a customer that takes some perverse pleasure in undermining point-in-time-recoverability of their databases. Here are some more details on the bad practice and what I wish they would do instead.

The customer has a two-node failover cluster with two instances of SQL Server 2005. Each clustered instance has dozens of databases, nearly all of them with identical schema (they have a Data-Dependent Routing/shard architecture), and each is in the full recovery model. They don’t have enough disk space allocated for the instances and, much too frequently, they encounter 9002 errors from SQL Server (when one or more of the transaction log files fill up and don’t have room to grow):

Msg 9002, Level 17, State 1, Line 1
The transaction log for database '____' is full. To find out why space in the log cannot be reused, see the log_reuse_wait_desc column in sys.databases

The customer reacts by looping through all of their databases and running BACKUP LOG … WITH NO_LOG against each. Let’s walk through the steps that they should be taking, instead of this knee-jerk overreaction.

The first problem is that, despite the hint in the 9002 error message, the customer doesn’t use the [log_reuse_wait_desc] column of the [sys].[databases] catalog view to determine which databases need attention. Instead, they resort to shotgun troubleshooting and truncate the transaction logs of all databases.

Next, the customer should perform appropriate capacity planning and provision enough disk space for their databases (now and in the future).

Since the customer won’t acquire sufficient disk space, I have asked whether their databases really belong in the full recovery model. They expect point-in-time-recoverability, but their frequent and ill-advised reaction to 9002 errors trashes the databases’ backup log chains. It will be an upsetting day when they need to restore to a certain time and cannot do so. It would be better to review the Recovery Point Objective (RPO) for each database (or define it for the first time) and then place the majority of the databases in the simple recovery model, saving the resources and administrative overhead associated with transaction log backups. Differential backups could be used to reduce data loss to levels consistent with the RPOs (YMMV).

As bad as it is to rely on sabotaged point-in-time-recoverability, the bigger issue is that the customer will follow-up the transaction log truncations with a round of shrinking the transaction log files! I don’t have time right now to delve into the perils of shrinking transaction log files, so here are some great posts from others in the community:

If, despite careful planning and database administration, the transaction log for one of your databases fills up (and you receive a 9002 error), take a deep breath and simply back up the transaction log for that database. It takes slightly longer than throwing away all of the committed transaction log records (via BACKUP LOG … WITH NO_LOG/TRUNCATE_ONLY) but it also avoids the problems discussed in this post and the last one. If absolutely necessary, discard the committed transactions using the following process (which still works even after the wretched BACKUP LOG … WITH NO_LOG/TRUNCATE_ONLY functionality was removed from SQL Server):

  1. Put the database in simple recovery model:

    ALTER DATABASE [YourDatabase] SET RECOVERY SIMPLE;
  2. Wait for the transaction log to automatically truncate… or force it:

    USE [YourDatabase];
    GO
    CHECKPOINT;
    GO
  3. If the database truly belongs in full recovery model:

    1. Change the recovery model back:

      ALTER DATABASE [YourDatabase] SET RECOVERY FULL;
    2. Immediately perform a full backup of the database to reestablish the backup chain

Note: While performing research for this post, I came across a similar one from Brent Ozar (blog, Twitter). Despite the overlap, I decided to publish anyway, hoping that we rank higher in search engine results than all of the bad Internet advice that advocates forcibly clearing the transaction log!

Posted in SQL Server | Tagged , , , , , , , , , , , , | Leave a comment

Please vote for PASS Summit 2011 sessions!

I have been extremely fortunate to have spoken at the last two North American PASS Summits. The experiences have been amazing and I’m hopeful about getting a chance to present at this year’s conference too. I received very good feedback from last year’s session, “Deadlock Detection, Troubleshooting, and Prevention“, which is reassuring, but so many fantastic speakers have submitted to speak that there is fierce competition.

This year, the session selection committee is trying something new and has requested input on the submitted abstracts. I strongly encourage you to take a few minutes and vote for abstracts that appeal to you in the PASS Summit 2011 Session Preference Survey. You can vote even if you might not be able to attend the conference… all that you need is an account on the SQLPASS.org web site. If you don’t already have one, registration is easy and free.

If you have attended any of my presentations and enjoyed them/learned from them, I would greatly appreciate the support of voting for some of my sessions. I submitted the maximum of four abstracts, but I would prefer to deliver either of my “cookbook” presentations:

  • A PowerShell Cookbook for DBAs

    • I have been delivering this presentation since SQLSaturday #22 on 2010-06-05
    • As a production/escalation DBA, I use PowerShell more than any other single program, especially when interacting with SQL Server
    • My goal is to convince fellow DBAs to try PowerShell by showing a variety of ways that PowerShell makes our jobs easier and by providing attendees with a collection of ready-to-use scripts for getting started
  • An XQuery Cookbook for DBAs

Note: The deadline for voting is tomorrow, so please do so as soon as possible!

Posted in PowerShell, SQL Server | Tagged , , , , , , , , , | Leave a comment

Code snippet – Reestablishing the backup log chain with PowerShell

We have a certain customer that has an incredible fondness for undermining the restorability of their databases. Almost all of their databases are in the full recovery model and all-too-frequently the customer will start backing up transaction logs with the dreaded NO_LOG option, which destroys the transaction log backup chain:

Msg 4214, Level 16, State 1, Line 1
BACKUP LOG cannot be performed because there is no current database backup.
Msg 3013, Level 16, State 1, Line 1
BACKUP LOG is terminating abnormally.

In a future blog post, I will go into why users resort to this bad practice and what they should be doing instead.

This solution is based on a great blog post from Kalen Delaney (blog, Twitter), that showed how the [sys].[database_recovery_status] catalog view can reveal databases suffering from this problem, using a query like so:

SELECT DB_Name([database_id]) AS [DatabaseName]
FROM [sys].[database_recovery_status]
WHERE (
    ([last_log_backup_lsn] IS NULL)
    AND
    (Cast(DatabasePropertyEx(DB_Name([database_id]), 'Recovery') AS NVarChar(16)) = N'FULL')
    AND
    (Cast(DatabasePropertyEx(DB_Name([database_id]), 'Status') AS NVarChar(16)) = N'ONLINE')
)
ORDER BY [DatabaseName] ASC;

The following PowerShell function uses the query to retrieve a list of problematic databases and then automatically backs up each one:

Function Repair-LogBackupChain (
    [String]$ServerName = $(Throw (New-Object -TypeName 'System.ArgumentNullException' -ArgumentList '$ServerName')),
    [String]$BackupDirectoryPath = $(Throw (New-Object -TypeName 'System.ArgumentNullException' -ArgumentList '$BackupDirectoryPath')),
    [String]$OutputDirectoryPath = $(Throw (New-Object -TypeName 'System.ArgumentNullException' -ArgumentList '$OutputDirectoryPath'))
) {
    [Int]$BackedUpDatabaseCount = 0;
    [String]$BackupFilePath = '';
    [String]$DatabaseName = '';
    [String]$ErrorMessage = '';
    [String]$OutputFilePath = '';
    [String]$Query = "SET NOCOUNT ON; SELECT DB_Name([database_id]) AS [DatabaseName] FROM [sys].[database_recovery_status] WHERE (([last_log_backup_lsn] IS NULL) AND (Cast(DatabasePropertyEx(DB_Name([database_id]), 'Recovery') AS NVarChar(16)) = N'FULL') AND (Cast(DatabasePropertyEx(DB_Name([database_id]), 'Status') AS NVarChar(16)) = N'ONLINE')) ORDER BY [DatabaseName] ASC;";
    [String]$TSQLCode = '';

    #   Create the necessary directories, if they don't already exist.
    $BackupDirectoryPath = Add-Directory -Path $BackupDirectoryPath;
    $OutputDirectoryPath = Add-Directory -Path $OutputDirectoryPath;

    #   Lookup the names of the databases, in the full recovery model, that
    #   lack an intact transaction log backup chain.
    [String[]]$DatabaseNames = @(& 'SQLCmd.exe' -E -S $ServerName -d 'tempdb' -Q $Query -b -h-1 -W);
    $DatabaseNames | Write-Debug;

    #   Verify that the list of databases was successfully retrieved.
    If ((Get-Item -Path Variable:'LastExitCode').Value -eq 0) {
        #   Iterate through the databases that need backups.
        $DatabaseNames | ForEach-Object {
            $DatabaseName = $_;
            Write-Host "Backing up the [$($DatabaseName)] database..." -ForegroundColor 'Cyan';

            $OutputFilePath = "$($OutputDirectoryPath)$(Get-EscapedFileNameComponent -FileNameComponent $ServerName),Backup database,$(Get-EscapedFileNameComponent -FileNameComponent $DatabaseName),$([DateTime]::Now.ToString('yyyyMMddTHHmmssfff')).out";
            Write-Debug "`$OutputFilePath = `"$($OutputFilePath)`"";

            #   Construct the name of the backup file.
            $BackupFilePath = [String]::Format(
                '{0}{1}_{2}_{3}.bak',
                $BackupDirectoryPath,
                (Get-EscapedFileNameComponent -FileNameComponent $ServerName),
                (Get-EscapedFileNameComponent -FileNameComponent $DatabaseName),
                ([DateTime]::Now.ToString('yyyyMMddTHHmmssfff'))
            );

            #   Generate the T-SQL for backing up the database.
            $TSQLCode = [String]::Format(
                "BACKUP DATABASE [{0}] TO DISK = N'{1}';",
                $DatabaseName,
                $BackupFilePath
            );
            Write-Debug "`$TSQLCode = `"$($TSQLCode)`"";

            #   Backup the database.
            & 'SQLCmd.exe' -E -S $ServerName -d 'tempdb' -Q $TSQLCode -o $OutputFilePath -b;

            If (Test-Path -LiteralPath $BackupFilePath -PathType 'Leaf') {
                $BackedUpDatabaseCount++;
            }
            Else {
                $ErrorMessage = "There was a problem backing up the [$($DatabaseName)] database.";
                Throw (New-Object -TypeName 'System.InvalidOperationException' -ArgumentList $ErrorMessage);
            }
        }
    }
    Else {
        $ErrorMessage = [String]::Format(
            "There was a problem querying the [sys].[database_recovery_status] catalog view:`r`n{0}",
            (
                [String]::Join(
                    "`r`n",
                    $DatabaseNames
                )
            )
        );
        Throw (New-Object -TypeName 'System.InvalidOperationException' -ArgumentList $ErrorMessage);
    }

    Write-Host "A total of $($BackedUpDatabaseCount) database(s) was/were backed up." -ForegroundColor 'Green';

    Return;
}

Here’s an example of using the function:

Repair-LogBackupChain -ServerName 'DatabaseServer01\InstanceA' -BackupDirectoryPath 'C:\Temp\' -OutputDirectoryPath 'C:\Temp\';

Note: The code snippet is dependent on two functions that are included in the download for my PowerShell cookbook presentation, Add-Directory and Get-EscapedFileNameComponent.

Maybe by the time that I write the follow-up blog post, I will have received permission to break the customer of this nasty habit (using a tip from Paul Randal (blog, Twitter)).

Hopefully this PowerShell snippet will save you as much time as it has for me!

Posted in PowerShell, SQL Server | Tagged , , , , , , , , , | Leave a comment

SQL Trace presentation materials posted

I just uploaded a new set of deliverables from my SQL Trace presentation (which can be found here). Let me know if you have any questions.

Posted in SQL Server | Tagged , , , , | Leave a comment

Script – Extracting XML deadlock graphs from a trace file

Today is my fourth day of SQLskills Immersion Events training, taught by none other than Paul Randal (blog, Twitter) and Kimberly Tripp (blog, Twitter). The material and delivery have been amazing and I hope to have a chance to blog about it this weekend. A fellow student, Eric Humphrey (blog, Twitter), asked about automatically extracting XML deadlock graphs from a SQL Trace file.

I’m a big proponent of using server-side traces for troubleshooting deadlocks. During my deadlocks presentation, I show how to manually extract XML deadlock graphs using SQL Server Profiler:

That works, but it’s a manual process, which is anathema to us automation adherents.

When processing XML deadlock graphs, I tend to:

  1. query the trace data, via the [sys].[fn_trace_gettable] system function,
  2. cast the [TextData] value to the XML data type, then
  3. analyze the deadlocks with XQuery.

I can see the benefit of having a few loose *.XDL files, so I spent a little time writing a PowerShell script that will automatically extract the XML deadlock graphs and save them as files. This zip archive contains the script and sample trace files.

Here is an example of using the script (assuming that you extracted the files to the “C:\Temp\” directory):

[String]$ScriptUtilityPath = 'C:\Temp\Export-XDLFromTraceFile.ps1';
[String]$OutputDirectoryPath = 'C:\Temp\';
[String]$SourceServerName = 'T-SQL001';
[String]$TraceFilePath = 'C:\Temp\DeadlockTrace_20100522T113514.trc';
& $ScriptUtilityPath -TraceFilePath $TraceFilePath -OutputDirectoryPath $OutputDirectoryPath -SourceServerName $SourceServerName -FormatXML | Out-Null;

Here’s another example that leverages PowerShell’s composable nature:

[String]$ScriptUtilityPath = 'C:\Temp\Export-XDLFromTraceFile.ps1';
[String]$OutputDirectoryPath = 'C:\Temp\';
[System.IO.FileInfo[]]$OutputFiles = @();
[String]$SourceDirectoryPath = 'C:\Temp\';
[String]$SourceServerName = 'T-SQL001';
[String]$TraceFilePath = '';
Get-ChildItem -LiteralPath $SourceDirectoryPath -Filter '*.trc' | ForEach-Object {
    $TraceFilePath = $_.FullName;
    $OutputFiles += @(& $ScriptUtilityPath -TraceFilePath $TraceFilePath -OutputDirectoryPath $OutputDirectoryPath -SourceServerName $SourceServerName -FormatXML);
}
$OutputFiles | Select-Object -Property Name, @{Name='Size_bytes'; Expression={'{0:N0}' -f $_.Length}} | Format-Table -AutoSize;

Let me know if this is useful to you, or if you have any suggestions to make it better.

NOTE: I haven’t had a chance to test the script with the SQL Server 2005 SQL Server Management Objects (SMO) assemblies. I suspect one or both of the following issues:

Posted in PowerShell, SQL Server | Tagged , , , , , , , , , , | Leave a comment