#SharePoint Farm management script #Written By: Tim Wheeler (https://codemonkeysoftware.atlassian.net) #.\SPManage.ps1 -Help (for information on this script) param ( [Parameter(Position=0)] [ValidateSet('All','APP','WFE')] [System.String]$ServerType = "All", $Command, $UserName, $Password, $configFile, $Credential, [switch] $Ping, [switch] $IISReset, [switch] $ClearSessions, [switch] $QueryDiskSpace, [switch] $RestartSPServices, [switch] $StopSPServices, [switch] $StartSPServices, [switch] $VerifyRemoting, [switch] $NoSP, [switch] $Silent, [switch] $QueryFarm, [switch] $QueryWebApps, [switch] $Help ) if($userName -ne $null) { $plainTextPassword = $Password $secureStringPassword = ConvertTo-SecureString $plainTextPassword -AsPlainText -force $Credential = New-Object System.Management.Automation.PSCredential ($UserName, $secureStringPassword) } $version = "1.1" #region Help and Feedback function Message([string] $computerName, $message) { Write-Host "$computerName : $message" } function Help() { Write-Host Write-Host ("SPManage v" + $version +" - Script Help") Write-Host Write-Host "This script uses powershell remoting which must be enabled on the servers" Write-Host "" Write-Host "Parameters:" Write-Host " -ServerType ('All', 'APP', 'WFE' - Default is ALL) This is only usable if you are on a local SP 2010 Farm otherwise use -configFile" Write-Host " -UserName (If on a different domain or if you require a different user account set this value eg; MyDomain\MyUser" Write-Host " -Password (The password if specifying a UserName)" Write-Host " -Command (An optional command to execute remotely. eg; {Get-Process})" Write-Host " -ConfigFile (An xml file that contains a list of servers)" Write-Host " -Credential (Pass in Get-Credential if you don't want to type in a plain text password)" Write-Host " -Ping (ICMP echo executed to all servers)" Write-Host " -VerifyRemoting (Creates a remote session to confirm remoting is available)" Write-Host " -QueryDiskSpace (Enumerates logical drives on each server)" Write-Host " -QueryWebApps (Enumerates SP2010 Web Applications and Application Pools (Local Query))" Write-Host " -QueryFarm (Enumerates servers within the SP2010 farm (Local Query))" Write-Host " -RestartSPServices (Restarts all SP2010 Services including User Code Host. Security note: If you don't want user code host running do not call this command.)" Write-Host " -StopSPServices (Stops all SP2010 Services including User Code Host and Search." Write-Host " -StartSPServices (Starts all SP2010 Services including User Code Host. Security note: If you don't want user code host running do not call this command.)" Write-Host " -IISReset (Runs IISReset on all servers)" Write-Host " -ClearSessions (Clears any existing remote sessions with the servers - not normally needed as script cleans up)" Write-Host " -NoSP (If the command being issued does not require the SharePoint snappin, use this switch to improve performance)" Write-Host " -Silent (Don't prompt for Un Safe Commands)" Write-Host " -Help (This output)" Write-Host Write-Host "Server configuration:" Write-Host "Each server in the farm must allow PS Remoting." Write-Host "run: winrm quickconfig (This will allow remote management on HTTP)" Write-Host "run: winrm quickconfig -transport:SSL (This will allow remote management on HTTPS)" Write-Host "run: winrm s winrm/config/client '@{TrustedHosts=`"RemoteComputer`"}' (Adds a trusted computer to the server config)" Write-Host "" Write-Host "Cross Domain:" Write-Host "If your client computer is on another domain, you may need to run an additional command on the remote server(s) to allow." Write-Host "run: " write-host "New-Itemproperty -name LocalAccountTokenFilterPolicy -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -propertyType DWord -value 1" Write-Host "(This will allow the remote computer to be controlled from a machine on another domain)" Write-Host Write-Host "Examples:" Write-Host ".\SPManage.ps1 -Ping #Simple ping test" Write-Host ".\SPManage.ps1 -VerifyRemoting #Creates a remote session on each server" Write-Host ".\SPManage.ps1 -ServerType:App -VerifyRemoting #Creates a remote session on each Application server" Write-Host ".\SPManage.ps1 -UserName `"DOM\User`" -Password `"MyPass`" -VerifyRemoting #Uses a username and password to connect to the remote servers" Write-Host ".\SPManage.ps1 -configFile devfarm.xml -Credential (Get-Credential) -VerifyRemoting #Uses a config file to connect to servers" Write-Host ".\SPManage.ps1 -Command {get-process} #Gets the running processes for all servers in the farm" Write-Host ".\SPManage.ps1 -IISReset #Issues IISReset using remote powershell session" Write-Host ".\SPManage.ps1 -RestartSPServices #Restarts all SP2010 Services on each server " Write-Host ".\SPManage.ps1 -ServerType App -RestartSPServices #Restarts all SP2010 Services on the application servers" Write-Host Write-Host "Notes:" Write-Host "Sessions are cleaned up at the end of the script." Write-Host "If a session already exists with the remote computer the script will attempt to use it." Write-Host "You don't need to specify a username and password if your account has correct permissions" Write-Host "This script must be run from an elevated PowerShell session." Write-Host "Be aware of the double hop issue when trying to execute SharePoint commands remotely. If the database is not on the same server the call will fail." Write-Host Write-Host "Example Config File (For use when you are not running on a server in the SP Farm. Specify -configFile parameter)" Write-Host "" Write-Host "" Write-Host "" Write-Host " " Write-Host " " Write-Host "" Write-Host "" } if($Help) { Help; return; } #endregion #region SharePoint Commands function GetFarmServers() { $farm = Get-SPFarm if($farm -eq $null) { throw "Cannot connect to local farm" } if($ServerType -ieq "All") { return $farm.Servers | where-object {$_.Role -ne "Invalid" } } if($ServerType -ieq "APP") { return $farm.Servers | where-object {$_.Role -ieq "Application" } } if($ServerType -ieq "WFE") { return $farm.Servers | where-object {$_.Role -ieq "WebFrontEnd" } } } function QueryFarmCommand() { $farm = Get-SPFarm if($farm -eq $null) { throw "Cannot connect to local farm" } Write-Host Write-Host "Farm Details" Write-Host $farm.Servers | sort $_.Role | Ft -AutoSize:$true @{Expression={$_.Role};Label="Role"}, @{Expression={$_.Address};Label="Address";}, @{Expression={$_.Status};Label="Status"} } #endregion #region SharePoint Windows Services function RestartSharePointServices() { StopSharePointServices StartSharePointServices } function StartSharePointServices() { $serviceRestart = { net start "SharePoint 2010 User Code Host"; net start "SharePoint 2010 Timer"; net start "SharePoint 2010 Administration"; net start "SharePoint Server Search 14";} RemoteExecuteCommand $servers $serviceRestart $false $true "Stopping SharePoint Services (2010)" } function StopSharePointServices() { $serviceRestart = { net stop "SharePoint 2010 User Code Host"; net stop "SharePoint 2010 Timer"; net stop "SharePoint 2010 Administration"; net stop "SharePoint Server Search 14";} RemoteExecuteCommand $servers $serviceRestart $false $true "Stopping SharePoint Services (2010)" } #endregion #region IO Functions and Config function LoadSPSnappin($session) { if($NoSP -eq $false) { RemoteExecute $session {Add-PSSnapin "Microsoft.SharePoint.PowerShell"} "Loading SharePoint Snappin" } } function Get-ScriptDirectory { $Invocation = (Get-Variable MyInvocation -Scope 1).Value $dir = Split-Path $Invocation.MyCommand.Path return $dir } function Set-Directory($location) { [IO.Directory]::SetCurrentDirectory((Convert-Path($location))) } function LoadConfiguration($configFile) { if(![IO.File]::Exists($configFile)) { throw "Could not configuration file: $configFile. Script Terminated." } write-host "Found configuration file: $configFile" try { $config = [xml] (get-content -Path $configFile) write-host -ForegroundColor Green "Configuration Loaded Successfully" return $config.Config } catch { Write-Error "Configuration file $config found but could not be loaded. May not be valid XML" throw } } #endregion #region Ping / Verify Command function VerifyRemotingCommand($servers) { [bool] $failure = $false; foreach($server in $servers) { Write-Host "Attempting to create a remote PowerShell session in" $server.Address $session = $null $session = GetRemoteSession $server if($session -ne $null -and $session.Availability -eq "Available") { Write-Host -NoNewline ($server.Address + " : ") Write-Host -ForegroundColor Green "Success" } else { $failure = $true throw "Failed" } trap { Write-Host -ForegroundColor Red ($server.Address + " : Remote PowerShell command failed. " + $_.Exception.Message) continue; } } if($failure) { throw "One or more servers would not except a remote PowerShell session" } } function PingCommand($servers) { [bool] $failure = $false; foreach($server in $servers) { if(!(Ping $server.Address $Credential)) { $failure = $true; } # trap # { # Write-Host -ForegroundColor Red ($server.Address + ": Ping command failed. " + $_.Exception.Message) # continue; # } } if($failure) { throw "One or more PING commands failed" } } function Ping([string] $computerName) { Write-Host -NoNewline "PING: $computerName" $result = Test-Connection -ComputerName $computerName -ErrorAction:Stop -Count 2 -Quiet; if($result) { Write-Host " Connection Successful" } else { Write-Host -ForegroundColor Red " Connection Failed" } return $result; } #endregion #region Remoting Session Managment #clears any sessions it can find between this computer and the remote computer function ClearSessions([string] $computerName) { $session = Get-PSSession -ComputerName $computerName -ErrorAction:SilentlyContinue while($session -ne $null) { Message $session.ComputerName "Removing session" Remove-PSSession $session.Id $session = Get-PSSession -ComputerName $computerName -ErrorAction:SilentlyContinue } } #Only clears sessions in the sessionWatch variable function ClearCurrentSessions() { #ensures PowerShell Session Cleanup foreach($key in $sessionWatch.Keys) { $session = $sessionWatch[$key] if($session -ne $null) { Write-Host "Exiting remote session " $session.Id Remove-PSSession $session.Id } } $sessionWatch.Clear(); } function GetRemoteSession($server) { $computerName = $server.Address; if($sessionWatch.ContainsKey($computerName)) { return $sessionWatch[$computerName]; } $session = Get-PSSession -ComputerName $computerName -ErrorAction:SilentlyContinue if($session -ne $null -and $session.Availability -eq "Available") { $sessionWatch.Add($computerName, $session); return $session; } if($Credential -ne $null) { $session = New-PSSession -ComputerName $computerName -Credential $Credential -ErrorAction:Stop } else { $session = New-PSSession -ComputerName $computerName -ErrorAction:Stop } $sessionWatch.Add($computerName, $session); return $session } #endregion #region Command Execution function RemoteExecuteCommand($servers, $command, [bool] $job, [bool] $requiresSP, [string] $comment) { $jobWatch = @{}; $jobWatch.Clear(); foreach($server in $servers) { $session = GetRemoteSession $server if($requiresSP) { LoadSPSnappin $session } if($job) { $pendingJob = RemoteExecuteJob $session $command $comment $jobWatch.Add($session.ComputerName,$pendingJob) } else { RemoteExecute $session $command $comment } trap { Write-Host -ForegroundColor Red ($computerName + ": $Command : " + $_.Exception.Message) break; } } if($job) { foreach($id in $jobWatch.Keys) { Message $id "Receiving Job" $pendingJob = $jobWatch[$id] while($pendingJob.State -eq "Running") { Sleep 1 Receive-Job -InstanceId $pendingJob.InstanceId } Write-Host $pendingJob | Wait-Job | Receive-Job Remove-Job $pendingJob.InstanceId # | Out-Null } $jobWatch.Clear() } } function RemoteExecute($session, $script, $comment) { Message $session.ComputerName $comment Invoke-Command -Session $session -ScriptBlock $script } function RemoteExecuteJob($session, $script, $comment) { Message ($session.ComputerName + " [Job]") $comment return Invoke-Command -AsJob -Session $session -ScriptBlock $script } function LocalExecute($script, $comment) { Message $env:ComputerName $comment Invoke-Command -ScriptBlock $script } #Provides a confirmation prompt function SafeFunction($question) { if(!$Silent) { if((Read-Host $question) -ine "Y") { Write-Host "Cancelled" return $false; } } return $true; } #endregion $sessionWatch = @{}; $sessionWatch.Clear(); try { cls $servers = $null; if($configFile -eq $null) { #Gets the servers depending on ServerType (All, App, WFE) $servers = GetFarmServers } else { $scriptDir = Get-ScriptDirectory $config = LoadConfiguration ([System.IO.Path]::Combine($scriptDir,$configFile)) $servers = $config.Servers.Server if($servers -eq $null -or $servers.Count -eq 0) { throw "No servers to process, please check config file." } } if($ClearSessions) { $servers | ForEach-Object { ClearSessions $_.Address } } if($Ping) { PingCommand $servers } if($VerifyRemoting) { VerifyRemotingCommand $servers } if($QueryFarm) { $query = {QueryFarmCommand} LocalExecute $query "Query Farm (Local)" } if($QueryWebApps) { $query = {Get-SPWebApplication | Ft -AutoSize:$true @{Expression={$_.DisplayName};Label="Name"}, @{Expression={$_.Url};Label="Url";}, @{Expression={$_.ApplicationPool.Name};Label="App Pool"}, @{Expression={$_.ApplicationPool.Status};Label="Status"}} #RemoteExecuteCommand $servers $query $true $true "Query Web Applications" LocalExecute $query "Query Web Applications (Local)" } if($IISReset) { $q = "Are you sure you want to execute IISReset on selected servers? Press Y to confirm." if(SafeFunction($q)) { RemoteExecuteCommand $servers {& IISReset} $true $false "IISRESET" } } if($Command -ne $null) { $q = "Are you sure you want to execute a custom command on all servers in config $configFile ? Press Y to confirm." if(SafeFunction($q)) { RemoteExecuteCommand $servers $Command $true (!$NoSP) $Command.ToString().Trim() } } if($QueryDiskSpace) { $query = { gwmi win32_logicaldisk -filter "drivetype=3" | Ft -AutoSize:$true @{Expression={$_.deviceid};Label="Device"},@{Expression={"{0:0.0}" -f ($_.size/1gb)};Label="Size (GB)"}, @{Expression={"{0:0.0}" -f ($_.freespace/1gb)};Label="Free Space (GB)"}, @{Expression={"{0:0.0}" -f (([int64]$_.size - [int64]$_.freespace)/1gb)};Label="Used (GB)"}, @{Expression={"{0:0}%" -f ((([int64]$_.size - [int64]$_.freespace) * 100.0)/$_.size)};Label="Used Percentage"}} RemoteExecuteCommand $servers $query $false $false "Query Disk Space" } if($RestartSPServices) { RestartSharePointServices } if($StopSPServices) { StopSharePointServices } if($StartSPServices) { StartSharePointServices } Write-Host "Complete (Use switch -Help for script information)" } finally { ClearCurrentSessions }