Remote install of SharePoint 2010 prerequisites

I’ve published a free book on PowerShell 2.0 remoting. You can download it at:

Layman's guide to PowerShell 2.0 remoting 31009 downloads

Did you ever try installing SharePoint 2010 prerequisites remotely? If so, you would know by now that it is not easy. Essentially because of the Windows update packages in the prerequisites. It is a known issue that Wusa.exe cannot be invoked in a remote session. If you try to install SharePoint 2010 prerequisites remotely — either using PowerShell remoting or WMI — it fails with an error code 5.

We cannot really claim to have a fully unattended SharePoint deployment process until we have a solution to the prerequisite install process. So, with that background, I started working on the script for remotely installing SharePoint 2010 prerequisites. In today’s post, I will show you how to do that.

To achieve this, I used a combination of PsExec.exe and PowerShell remoting. Here are the prerequisites for this script to work.

  1. PowerShell remoting must be enabled the remote system where you are deploying SharePoint prerequisites.
  2. On the remote system, CredSSP must be enabled with Role set to Server.
  3. You need to download/copy PsExec.Exe to the same folder as the script in this post.
  4. Copy SharePoint 2010 install bits to a UNC share accessible from the remote computer and the prerequisiteinstaller.Arguments.txt file to install required prerequisites.
  5. Of course, you need to run this script with elevated privileges.

With no further delay, here is the script.

$global:ScriptPath = Split-Path $MyInvocation.MyCommand.Path            

#Check if remote computer has PS remoting enabled or not
#This function is by Lee Homles
#http://www.leeholmes.com/blog/2009/11/20/testing-for-powershell-remoting-test-psremoting/
function Test-PsRemoting
{
    param(
        [Parameter(Mandatory = $true)]
        $computername
    )            

    try
    {
        $errorActionPreference = "Stop"
        $result = Invoke-Command -ComputerName $computername { 1 }
    }
    catch
    {
        Write-Verbose $_
        return $false
    }             

    ## I've never seen this happen, but if you want to be 
    ## thorough.... 
    if($result -ne 1)
    {
        Write-Verbose "Remoting to $computerName returned an unexpected result."
        return $false
    }
    $true
}            

Function Get-PSCredential {
 Param(
 [string]$userName,
 [string]$password
 )
 $PsCred = New-Object System.Management.Automation.PSCredential -ArgumentList @($userName,(ConvertTo-SecureString -String $password -AsPlainText -Force))
 Return $PsCred
}            

Function Restart-RemoteComputer {
 Param (
  [Parameter(Mandatory=$true)]
  $computerName
 )
 $timeOut = 300
 $timeInSeconds = 0
 try {
  Restart-Computer -ComputerName $computerName -Force
 }
 catch {
  Write-Host $_
  return $false
 }
    #we just want to wait for 10 seconds before monitoring the system
 Write-Host "`nWaiting while the remote computer restarts"
 Start-Sleep 10
 #start the test-connection loop here
 While (-not (Test-Connection -ComputerName $computerName -Quiet -Count 1)) {
  #We need to time this out after a while. Otherwise, we will be looking for it forever
  if ($timeInSeconds -eq $timeOut) {
  Write-Error "Remote computer did not come back within the timeout period"
  return $false
  } else {
   $timeInSeconds += 1
  }
 }
 return $true
}            

Function Install-SPPrerequisites {
 Param (
  [Parameter(Mandatory=$true)]
  $path,
  [Parameter(Mandatory=$true)]
  $computerName,
  [Parameter(Mandatory=$true)]
  $userName,
  [Parameter(Mandatory=$true)]
  $password,
  [switch]$restart
  )            

 Write-Verbose "Verifying if PowerShell remoting is enabled on remote computer"
 if (Test-PSRemoting -ComputerName $computername) {
  Write-Verbose "Creating a persistent session"
  $session = New-PSSession -ComputerName $computername -Authentication Credssp -Credential (Get-PSCredential -userName $userName -Password $password)            

  Write-Verbose "Copying SharePoint prerequisite files to remote computer"
  $cmd ="`$tmpFolder = (md `"`$(`$env:Temp`)\SPreReq`") `
Copy-Item -Path `"$($path)\PrerequisiteInstallerFiles`" -Destination `$tmpFolder -Recurse
Copy-Item -Path `"$($path)\PrerequisiteInstaller.Arguments.txt`" -Destination `$tmpFolder
Copy-Item -Path `"$($path)\PrerequisiteInstaller.exe`" -Destination `$tmpFolder
Copy-Item -Path `"$($path)\msvcr90.dll`" -Destination `$tmpFolder
Copy-Item -Path `"$($path)\Microsoft.VC90.CRT.manifest`" -Destination `$tmpFolder
`$tmpFolder"
  Write-Verbose $cmd
  $scriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock($cmd)            

        #start the remote install process
  try {
   $remoteFolder = Invoke-Command -Session $session -ScriptBlock $scriptBlock -HideComputerName
   Write-Host "`nCopied files to $($remoteFolder.FullName) on $computerName"            

   if (Get-Item "$($global:ScriptPath)\psexec.exe" -ErrorAction SilentlyContinue) {
    $args = "\\$($computerName) -u $userName -p $password -d -w $($remoteFolder.FullName) `"$($remoteFolder.FullName)\PrerequisiteInstaller.exe`""
    Start-Process -FilePath "$($global:ScriptPath)\psexec.exe" -ArgumentList "$($args)" -Wait | Out-Null            

                #This is how we monitor the install process
    $procMonitor = "`$proc = Get-Process -Name PrerequisiteInstaller
Register-ObjectEvent `$proc Exited -SourceIdentifier ProcessExited -Forward
Wait-Event ProcessExited"
    Write-Verbose $procMonitor
    $scriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock($procMonitor)
    Invoke-Command -Session $session -ScriptBlock $scriptBlock -AsJob | Out-Null            

    Write-Host "`nWaiting for Prerequisites installer to complete.."
    $event = Wait-Event ProcessExited -Timeout 600
    if (($event.Sender.ExitCode -eq 3010) -or ($event.Sender.ExitCode -eq 0)) {
     if ($restart) {
      Write-Host "`nPrerequisite install complete. Restarting the remote computer"
      Write-Verbose "Cleaning up PS Session"
      Remove-PSSession -Session $session
      if (Restart-RemoteComputer $computername) {
       Write-Host "`nRemote computer restart is complete"
       Remove-Variable Event
       Return $true
      } else {
       Remove-Variable Event
       return $false
      }
     } else {
      Write-Host "`nPrerequisite Install completed successfully. Restart the remote computer"
      Remove-Variable Event
      Return $true
     }
    } else {
     Write-Error "Prerequisite installer failed with an error code: $($event.Sender.ExitCode)"
     Write-Error "Check the log files on the remote computer"
     Remove-Variable Event
     return $false
    }
   }
  }
  catch {
   Write-Verbose $_
   return $false
   }
  } else {
   Write-Host "PS remoting is not enabled on remote computer"
   return $false
  }
}

Save this to a .psm1 file. We can now run this script on the local system as shown here:

import-module SP-Deploy.psm1
Install-SPPrerequisites -path \\Server\Share\SharePointInstallFolder -ComputerName remoteComputer -UserName Domain\UserName -Password Password -Restart

As you see above, I have used PsExec.exe to invoke a remote process. PsExec is unique in this case because it invokes the installer process on the remote machine as if it was invoked locally. Hence, the issues pertaining to remote invocation of Wusa.exe don’t bother us. What the script does is:

  1. Copy the prerequisite files to a temp folder on the remote computer
  2. Invoke prerequisiteinstaller.exe using PsExec.exe
  3. Wait for the prerequisiteinstaller.exe process on remote machine to exit
  4. Finally, verify the exit code and restart the remote computer, if required.

CredSSP is required because the script tries to access the UNC path where SharePoint 2010 install bits are stored. Also, observe how the script monitors the remote process.

#This is how we monitor the install process
    $procMonitor = "`$proc = Get-Process -Name PrerequisiteInstaller
                Register-ObjectEvent `$proc Exited -SourceIdentifier ProcessExited -Forward
                Wait-Event ProcessExited"            

    $scriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock($procMonitor)
    Invoke-Command -Session $session -ScriptBlock $scriptBlock -AsJob | Out-Null            

    Write-Host "`nWaiting for Prerequisites installer to complete.."
    $event = Wait-Event ProcessExited -Timeout 600

Thanks to Oisin (@Oising) for this idea. I was struggling here to get the hold of remote process and using eventing made it really easy. Here is a screen capture of this script in action:

Prerequisite Deployment

Prerequisite Deployment

Finally, you can use the -restart parameter to automatically restart the system after prerequisite installation.

Once again, this is a very basic form of the script. Not even a complete advanced function. There is still some work to do. Things like,

  1. Complete comment based help
  2. Making it an advanced function
  3. Making this a part of PSSP2010Utils module

I will share the updates as I make some progress. Stay tuned.!