Automating installations with PowerShell script

18. March 2009

I am currently testing Windows 7 and therefore I am deploying various builds quite frequently. This is nice because you have a chance to see the product evolving and at the same time you have a chance to send feedback to the product team. On the other hand installing OS frequently means to install all the other applications very often. I have created various PowerShell scripts to simplify the deployment process. Today I will try to describe my install-folder script.

The basic idea of the script is this. I want to put all my installations into a folder and then run the script against this folder. The script will enumerate over all the files in that folder and “run the installation” for each of them. The problem here is that some installations are delivered as MSI files, some are EXE files (not build using Windows Installer) and some are ISO files (those downloaded from MSDN usually).

MSI File Installation

To install an MSI file you run msiexec with /i msiFileName. If you want to run the installation automatically then add also /passive. If you want to prevent Windows Installer from automatically restarting your machine then use /norestart.

Note: When you need to troubleshoot MSI installation, it might be a good idea to turn on verbose logging in Windows Installer. To turn the logging on use /l*v logFilePath parameter. More information about this topic can be found at:

EXE File Installation

This one is tricky. If it is EXE created using Windows Installer then you might be able to use some of the Windows Installer parameters. On the other hand many other installers are available (like InnoSetup, Nullsoft Install System, etc.) and the parameters for these are different. That’s why when installing EXE I usually just invoke the EXE file.

ISO File Installation

ISO file installation is the most difficult because in the OS there is no direct support for these files. There are various utilities available for this purpose (SlySoft Virtual CloneDrive, Daemon Tools, Microsoft Virtual CD-ROM, etc.). I have decided to use SlySoft solution because I have had a really good experience with their other tools like AnyDVD (this is an awesome tool if you are dealing with DVDs from different regions for example). After you install the tool you can mount an ISO image by calling VCDMount.exe isoFilePath. To unmount the current ISO image execute VCDMount.exe /u.

InstallFile function

So to install the files from a folder we need to enumerate over all files in that folder and for each of these files execute InstallFile function:

    function InstallFile($file)
    {
        if ($file.EndsWith(".msi"))
        {
            execute-command -wait -command msiexec -parameters "/i $file /passive /norestart"
        }
        elseif ($file.EndsWith(".exe"))
        {
            execute-command -wait -command "$file"
        }
        elseif ($file.EndsWith(".iso"))
        {
            execute-command -command "$programFiles32\Elaborate Bytes\VirtualCloneDrive\VCDMount.exe" -parameters "/u"
            execute-command -command "$programFiles32\Elaborate Bytes\VirtualCloneDrive\VCDMount.exe" -parameters "$file"
            Pause "`tPress any key when finished with this installation."
            execute-command -command "$programFiles32\Elaborate Bytes\VirtualCloneDrive\VCDMount.exe" -parameters "/u"
        }
    }

 

This function references some of my other scripts/functions. First of all it uses execute-command script. The core of that script is the following:

param([string] $command = $(throw "Missing: command parameter"), [string[]] $parameters, [switch] $wait, [switch] $cmd)

#Main
    if ($cmd.IsPresent)
    {
        cmd /C $command $parameters
    }
    else
    {
        $process = [Diagnostics.Process]::Start($command, $parameters); 
        if ($wait.IsPresent)
        {
            $process.WaitForExit();
        }
    }

Save this script as execute-command.ps1.

The only extra thing we have in our InstallFile folder is reference to $programFiles32 folder. This is initialized in my profile script (which I will describe in one of the latter posts). For now I will just define the variables:

    #Define ProgramFiles* variables
    $is64 = $(if([IntPtr]::Size -eq 8) { $true } else { $false })
    if ($is64)
    {
        $programFiles32 =  $(get-item "env:ProgramFiles(x86)").Value
        $programFiles64 = $env:ProgramFiles
    }
    else
    {
        $programFiles32 = $env:ProgramFiles
        $programFiles64 = $env:ProgramFiles
    }

InstallFolder function

Now we have all we need to implement our main InstallFolder function:

    function InstallFolder($folder)
    {
        $files = get-childitem $folder -recurse -force
        foreach($file in $files)
        {
            if ($file -eq $null)
            {
                continue
            }
            $answer = Ask "Do you want to install $($file.FullName)? (y or n)?" "y"
            if ($answer -ne "y") { continue } 
            set-color "yellow"
            write-host "`tInstalling $file..."
            set-color
            InstallFile $($file.FullName)
        }
    }

In this function I am using two additional functions. Function set-color (is defined in profile script) and Ask function.

    function Ask($text, $default)
    {
        $answer = $(read-host "$text [default: $default]")
        if ($answer -eq "") { $answer = $default }
        return $answer
    }

    function Set-Color([string] $color)
    {
        if ($color -eq "")
        {
            # $myDefaultColor initialized in profile script to [Console]::ForeGroundColor
            $color = $myDefaultColor
        }
        $host.UI.RawUI.ForeGroundColor = $color
    }

Final Install-Folder script code

With all these we can now create the install-folder.ps1 script:

param([string] $path = $(throw "Missing: path parameter"), )

#Internals
    function Ask($text, $default)
    {
        $answer = $(read-host "$text [default: $default]")
        if ($answer -eq "") { $answer = $default }
        return $answer
    }

    function Set-Color([string] $color)
    {
        if ($color -eq "")
        {
            # $myDefaultColor initialized in profile script to [Console]::ForeGroundColor
            $color = $myDefaultColor
        }
        $host.UI.RawUI.ForeGroundColor = $color
    }

    function InstallFolder($folder)
    {
        $files = get-childitem $folder -recurse -force
        foreach($file in $files)
        {
            if ($file -eq $null)
            {
                continue
            }
            $answer = Ask "Do you want to install $($file.FullName)? (y or n)?" "y"
            if ($answer -ne "y") { continue } 
            set-color "yellow"
            write-host "`tInstalling $file..."
            set-color
            InstallFile $($file.FullName)
        }
    }
    
    function InstallFile($file)
    {
        if ($file.EndsWith(".msi"))
        {
            execute-command -wait -command msiexec -parameters "/i $file /passive /norestart"
        }
        elseif ($file.EndsWith(".exe"))
        {
            execute-command -wait -command "$file"
        }
        elseif ($file.EndsWith(".iso"))
        {
            execute-command -command "$programFiles32\Elaborate Bytes\VirtualCloneDrive\VCDMount.exe" -parameters "/u"
            execute-command -command "$programFiles32\Elaborate Bytes\VirtualCloneDrive\VCDMount.exe" -parameters "$file"
            Pause "`tPress any key when finished with this installation."
            execute-command -command "$programFiles32\Elaborate Bytes\VirtualCloneDrive\VCDMount.exe" -parameters "/u"
        }
    }

#Main
    #Define ProgramFiles* variables
    $is64 = $(if([IntPtr]::Size -eq 8) { $true } else { $false })
    if ($is64)
    {
        $programFiles32 =  $(get-item "env:ProgramFiles(x86)").Value
        $programFiles64 = $env:ProgramFiles
    }
    else
    {
        $programFiles32 = $env:ProgramFiles
        $programFiles64 = $env:ProgramFiles
    }

    write-host "Installing $path..."
    InstallFolder $path

This is just one of my scripts that I use daily. I will describe more of them in later posts.

Attachments:

PowerShell

Connecting to SharePoint embedded database

10. March 2009

As a new member in Windows SharePoint team I am trying to learn as much as possible. I have installed SharePoint (WSS) version 3.0 on my home computer. I wanted to see what is the database schema in SharePoint. Since I had SQL Server 2008 installed on my machine I started SQL Server Management Studio.

 

Connecting to SharePoint SSEE database

 

But when I click Connect I only get the following error:

 

ConnectingToSharePointDb_Error

 

 

 

Bummer. After a while of searching on Internet I found the following Wikipedia article - Windows Internal Database. Let me quote some of the text from the article:

 

Windows Internal Database (codenamed WYukon, sometimes referred to as SQL Server Embedded Edition) is a variant of SQL Server Express 2005 that is included with Windows Server 2008, and is included with other free Microsoft products released after 2007 that require an SQL Server database backend. Windows SharePoint Services 3.0 and Windows Server Update Services 3.0 both include Windows Internal Database, which can be used as an alternative to using a retail edition of SQL Server.

 

Ok, now I know that I am not connecting to a SQL Server database but instead to Windows Internal database. How do I do that? Again Wikipedia has the answer:

 

SQL Server Management Studio Express can be used to connect to an instance of Windows Internal Database using \\.\pipe\mssql$microsoft##ssee\sql\query as server name.

 

That means, that I should be able to connect even from SQL Server 2008 Management Studio.

 

ConnectingToSharePointDb_EmbeddedPipeString

 

That did the trick. I have now SQL Server Management Studio fully connected to the content database and I can execute queries against the database (I can use the same connection with SQL Profiler).

 

ConnectingToSharePointDb_Connected

 

More information can be found at:

SharePoint ,

Windows SharePoint Services

6. March 2009

Ok, it’s not even a month since my first post on this blog and things are different already. I have changed teams and now I work in SharePoint Server organization, in particular in Windows SharePoint Services Storage and Perf team. Since I am new to the team I cannot say for sure what is my primary feature but I hope I will find out soon.

I’m now also on Twitter, you can follow me. I have also added a special side bar with my latest updates from Twitter.

Blog ,

Welcome

22. January 2009

After almost two years of being idle I have decided to start blogging again. My old blog is still available at http://blogs.msdn.com/dpokluda but new posts will only appear on this new blog at http:// .

Q: So what did you do over the last two years?
A: I have changed groups. I don’t work in Microsoft Dynamics AX any more. I still enjoy working on business applications. When I heard about a new team called Office for Sales in Office organization that was supposed to be working on a first version of a platform bringing sales data from LOB into Office I was interested.

I am not sure how much I can say at this moment, so I will only link to other sources:

Q: Why the new location for the blog?
A: It’s not as much about the location but the engine. The previous blog was powered by Community Server that was administered by Microsoft so I didn’t have a chance to tweak the system as much. I was also overwhelmed by the amount of spam that I received daily and I didn’t have a way to control that. That’s why I have decided to move to a new blog engine and a new location.

Here are extra information:

Q: What will you blog about?
A: I would like to continue to blog about various things. I love building software, I love business applications, I love agile development and doing test driven development, I love using .NET framework, I love using 3rd party software/tools and I love supporting them. So the blog will mainly be about all these things.

Q: How often do you expect to post new posts?
A: That’s a tricky question. I would like to post one post a week.

This is it for now. I am still tweaking the blog theme. I hope I will be done with that in a week or two and then I will start posting actively. Stay tuned.

Blog