Art of the DBA Rotating Header Image

Slipstreamin’ Away

It’s nothing new: I love anything that makes less work for me.  One thing that has been a great contributor to this has been slipstream installs.  While that’s a bit of a fancy name, basically it’s an install of SQL Server where the service packs and cumulative updates are rolled up into one install package.  This saves the tedious chore of constant install after install to get a new SQL Server installation up to a patch level consistent with the rest of your enterprise.

Now, the problem with slipstream installs is that they’re a pain in the butt to create, at least previous to SQL 2012.  Not difficult, but tedious.  You have to copy some files, merge some directories, and edit some config files.  It’s nothing that can’t be done, but very repetitive.  To boot, if something isn’t copied or configured right, the whole package won’t work and you won’t know until you attempt the install (and it fails).

Enter Powershell.

Building a slipstream is no different than any other task that we should automate.  After all, we’re doing the same actions every time, the only thing that might change are the files we use to build our slipstream.  What I’ve done is put together a script that does all this for me.  As with any script, there are a few rules to how you use it:

  • The script was designed for building SQL 2008 and 2008 R2 slipstreams.  I tried it with 2005, but that process is different enough that this script won’t work.
  • RTMSource and output are the two mandatory parameters.  The SPFiles and CUFiles parameters will be skipped if left null.  Basically, if you are making just a SQL 2008 R2 SP2 slipstream, you would only need to supply the service pack files.
  • The script expects the base install (the RTM media) to be un-extracted and the service packs or cumulative updates in their .exe form.  The script will extract the additional files from the .exe into the slipstream package.
  • I built the script to work along my organizational thought process, which means I put each set of files in their own subdirectory.  This shouldn’t be a big deal, but be aware that if you point the script at a location, it won’t just pull out the files it needs, it will try and use every .exe file in the subdirectory.

Other than that, here’s the script:

<#
.SYNOPSIS
   Builds a SQL Server slipstream install.
.DESCRIPTION
   This script will take the various components of a SQL Server isntall (RTM binaries, SPs, CUs) and assemble
	 them to create a slipstream install.  To use this script, you will need the unextracted RTM binaries (.iso
	 or DVD media) and the executables for the SP and CUs you want to use.
	 Mike Fal (htp://www.mikefal.net) 2-28-2013
.PARAMETER <paramName>
   RTMSource - File path for the RTM source files
	 SPSource - File path for the Service Pack executables (if no value is passed, no SP will be added)
	 CUSource - File path for the Cumulative update executables (if no value is passed, no CU will be added)
	 output - filepath where slipstream will be written, directory will be created if it doesn't exist.
.EXAMPLE
   .\Build-Slipstream.ps1 -RTMSource "C:\Users\mfal\Downloads\en_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522665"
	 												-SPSource "C:\Users\mfal\Downloads\2008R2SP1"
													-CUSource "C:\Users\mfal\Downloads\2008R2SP1CU2"
													-output "C:\SQL2008SP1CU2_Slipstream"
#>

param([parameter(Mandatory=$true)][string] $RTMSource,
			[parameter(Mandatory=$false)][string] $SPSource,
      [parameter(Mandatory=$false)][string] $CUSource,
      [parameter(Mandatory=$true)][string] $output)

#function to extract and copy SPs and CUs
function Modify-Source{
	param($TYPE="PCU",$WORKDIR,$PATCHDIR)
	$PATCHFILES=get-childitem "$PATCHDIR\*" -include *.exe
	if (!(test-path("$WORKDIR\$TYPE"))) {mkdir $WORKDIR\$TYPE }
	if (!(test-path("$WORKDIR\$TYPE"))) {Throw "Error: Patch file destination not available"}

	set-location $PATCHDIR

	foreach($FILE in $PATCHFILES)
		{

			iex "./$($FILE.basename)$($FILE.extension) /x:""$WORKDIR\$TYPE"" /q"
			"Waiting for $FILE to extract..."
			while (@(Get-Process $FILE.Name.Replace(".exe","") -ErrorAction SilentlyContinue).Count -ne 0)
			{
			Start-Sleep 1
			}
		}
  "Copying $TYPE files..."
	robocopy "$WORKDIR\$TYPE" "$WORKDIR" setup.exe /ndl /nfl /njh /njs
	robocopy "$WORKDIR\$TYPE" "$WORKDIR" setup.rll /ndl /nfl /njh /njs

if (test-path("$WORKDIR\$TYPE\x86")) {robocopy "$WORKDIR\$TYPE\x86" "$WORKDIR\x86" /XF Microsoft.SQL.Chainer.PackageData.dll /ndl /nfl}
if (test-path("$WORKDIR\$TYPE\x64")) {robocopy "$WORKDIR\$TYPE\x64" "$WORKDIR\x64" /XF Microsoft.SQL.Chainer.PackageData.dll /ndl /nfl}
if (test-path("$WORKDIR\$TYPE\ia64")) {robocopy "$WORKDIR\$TYPE\ia64" "$WORKDIR\ia64" /XF Microsoft.SQL.Chainer.PackageData.dll /ndl /nfl}

set-location $WORKDIR
}

#Test path locations for validity

if (!(test-path($RTMSource + "\setup.exe"))) {Throw "SQL RTM Source does not exist!"}
if (!(test-path($SPSource)) -and !($SPSource -eq $null)) {Throw "Invalid Service Pack file location!"}
if (!(test-path($CUSource)) -and !($CUSource -eq $null)) {Throw "Invalid Cumulative Update file location!"}
if (!(test-path($output))) {mkdir $output}
if (!(test-path($output))) {Throw "Unable to create build directory!"}

#begin merge process, RTM
"Copying RTM..."
robocopy "$RTMSource" "$output" /s /ndl /nfl
$startloc=pwd
set-location $output

Copy-Item ".\x86\DefaultSetup.ini" ".\"

#merge Service Pack
if(!($SPSource -eq $null))
{
	Modify-Source "PCU" $output $SPSource
	&quot;<code>n</code>nPCUSOURCE=<code>&quot;.\PCU</code>&quot; <code>n&quot; |Out-File &quot;DefaultSetup.ini&quot; -Append
}

#merge Cumulative Update
if(!($CUSource -eq $null))
{
	Modify-Source &quot;CU&quot; $output $CUSource
	&quot;</code>n<code>nCUSOURCE=</code>&quot;.\CU<code>&quot; </code>n&quot; |Out-File &quot;DefaultSetup.ini&quot; -Append
}

#cleanup
copy-item &quot;defaultsetup.ini&quot; .\ia64 -force
copy-item &quot;defaultsetup.ini&quot; .\x64 -force
copy-item &quot;defaultsetup.ini&quot; .\x86 -force

Set-Location $startloc

5 Comments

  1. Perry Whittle says:

    Hi
    Good article, just want to clear up the terminology here. What you are creating is a merged drop. Slipstreaming is the process of launching the RTM media and pointing it to a CU or SP source extract on the fly. The advantage of a slip stream over a merged drop is the fact you can point the install to any CU and\or SP source combination to achieve your desired install version level
    Regards Perry

  2. […] Slipstreamin’ Away – Practising a philosophy I am particularly fond of, Mike Fal (Blog|Twitter) shares some automation wisdom with us. […]

  3. Hey,

    Thanks for writing this. I made a few tweak here:
    http://pastebin.com/dyvcD30m
    For status messages I used Write-Host because they don’t belong on the pipeline IMHO. I also used [String]::IsNullOrEmpty() instrad of -eq $null for $CUSource and $PUSource. On my machine the parameters were defaulting to empty strings.

    Finally on lines 59 and 60 I reversed the null checks and the test path. If the left side of an -and evaluates to false, the right is not executed. This is true in powershell and almost any language.

  4. Oh One more thing, The 2008R2 Sp2 CU6 updates do not extract in our script. They also don’t respect the /? /h or /help options so I can’t troubleshoot. I captured the processes with process monitor, and as far as i can tell you are calling them ccorectly. However, whatever parameters you pass to tese files, you get the extract GUI prompt:

    437405_intl_i386_zip.exe
    437407_intl_ia64_zip.exe
    437408_intl_i386_zip.exe
    437409_intl_i386_zip.exe
    437411_intl_i386_zip.exe
    437413_intl_ia64_zip.exe
    462311_intl_x64_zip.exe
    462313_intl_x64_zip.exe
    462315_intl_x64_zip.exe
    462317_intl_x64_zip.exe

  5. Oh I also changed the robocopy to remove the readonly attributes from the files if you copied them from a cd or mounted ISO image, because default.ini was failing to be modified.

Leave a Reply

Your email address will not be published. Required fields are marked *