SPSWakeUP.ps1
<#PSScriptInfo .VERSION 4.0.1 .GUID 1fc873b1-5854-46cb-8632-29cee879bb55 .AUTHOR luigilink (Jean-Cyril DROUHIN) .COPYRIGHT .TAGS script powershell sharepoint warmup .LICENSEURI https://github.com/luigilink/SPSWakeUp/blob/main/LICENSE .PROJECTURI https://github.com/luigilink/SPSWakeUp .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .PRIVATEDATA #> <# .SYNOPSIS SPSWakeUP script for SharePoint OnPremises .DESCRIPTION SPSWakeUp is a PowerShell script tool to warm up all site collection in your SharePoint environment. It's compatible with all supported versions for SharePoint (2016 to Subscription Edition). Use WebRequest object in multi-thread to download JS, CSS and Pictures files. Log script results in log file, Configure automatically prerequisites for a best warm-up. .PARAMETER Action Use the Action parameter equal to Install if you want to add the warmup script in taskscheduler InstallAccount parameter need to be set PS D:\> E:\SCRIPT\SPSWakeUP.ps1 -Action Install -InstallAccount (Get-Credential) Use the Action parameter equal to Uninstall if you want to remove the warmup script from taskscheduler PS D:\> E:\SCRIPT\SPSWakeUP.ps1 -Action Uninstall Use the Action parameter equal to AdminSitesOnly if you want to warmup the Central Administration Site collection PS D:\> E:\SCRIPT\SPSWakeUP.ps1 -Action AdminSitesOnly .PARAMETER InstallAccount Need parameter InstallAccount whent you use the Action parameter equal to Install PS D:\> E:\SCRIPT\SPSWakeUP.ps1 -Install -InstallAccount (Get-Credential) .PARAMETER Transcript Use the boolean Transcript parameter if you want to start Transcrit PowerShell Feature. PS D:\> E:\SCRIPT\SPSWakeUP.ps1 -Transcript:$True .EXAMPLE SPSWakeUP.ps1 -Action Install -InstallAccount (Get-Credential) SPSWakeUP.ps1 -Action Uninstall SPSWakeUP.ps1 -Action AdminSitesOnly SPSWakeUP.ps1 -Transcript:$True .NOTES FileName: SPSWakeUP.ps1 Authors: luigilink (Jean-Cyril DROUHIN) Nutsoft (Des Finkenzeller) Date: July 03, 2025 Version: 4.0.1 Licence: MIT License .LINK https://spjc.fr/ https://github.com/luigilink/spswakeup #> param ( [Parameter(Position = 1)] [validateSet('Install', 'Uninstall', 'Default', 'AdminSitesOnly', IgnoreCase = $true)] [System.String] $Action = 'Default', [Parameter(Position = 2)] [System.Management.Automation.PSCredential] $InstallAccount, [Parameter(Position = 3)] [System.Boolean] $Transcript = $false ) #region Initialization # Define variables $spsWakeupVersion = '4.0.1' $currentUser = ([Security.Principal.WindowsIdentity]::GetCurrent()).Name $scriptRootPath = Split-Path -Parent $MyInvocation.MyCommand.Definition # Clear the host console Clear-Host # Set the window title $Host.UI.RawUI.WindowTitle = "SPSWakeUP script running on $env:COMPUTERNAME" # Ensure the script is running with administrator privileges if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { Throw "Administrator rights are required. Please re-run this script as an Administrator." } # Setting power management plan to High Performance" Start-Process -FilePath "$env:SystemRoot\system32\powercfg.exe" -ArgumentList '/s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c' -NoNewWindow # Start Transcript parameter is equal to True if ($Transcript) { # Initialize the log file full path $pathLogFile = Join-Path -Path $scriptRootPath -ChildPath ('SPSWakeUP_script_' + (Get-Date -Format yyyy-MM-dd_H-mm) + '.log') # Clean the folder of log files Clear-SPSLog -path $scriptRootPath # Start Transcript with the log file Start-Transcript -Path $pathLogFile -IncludeInvocationHeader } #endregion #region functions function Add-SPSWakeUpEvent { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Message, [Parameter(Mandatory = $true)] [System.String] $Source, [Parameter()] [ValidateSet('Error', 'Information', 'FailureAudit', 'SuccessAudit', 'Warning')] [System.String] $EntryType = 'Information', [Parameter()] [System.UInt32] $EventID = 1 ) $LogName = 'SPSWakeUp' if ([System.Diagnostics.EventLog]::SourceExists($Source)) { $sourceLogName = [System.Diagnostics.EventLog]::LogNameFromSourceName($Source, ".") if ($LogName -ne $sourceLogName) { Write-Verbose -Message "[ERROR] Specified source {$Source} already exists on log {$sourceLogName}" return } } else { if ([System.Diagnostics.EventLog]::Exists($LogName) -eq $false) { #Create event log $null = New-EventLog -LogName $LogName -Source $Source } else { [System.Diagnostics.EventLog]::CreateEventSource($Source, $LogName) } } try { $headerMessage = @" SPSWakeUp Script Version: $spsWakeupVersion User: $currentUser ComputerName: $($env:COMPUTERNAME) -------------------------------------------------------------- "@ Write-EventLog -LogName $LogName -Source $Source -EventId $EventID -Message ($headerMessage + "`r`n" + $Message) -EntryType $EntryType } catch { Write-Error -Message @" SPSWakeUp Script Version: $spsWakeupVersion An error occurred while adding Event Log in Source: $Source User: $currentUser ComputerName: $($env:COMPUTERNAME) Exception: $_ "@ } } function Get-SPSInstalledProductVersion { [OutputType([System.Version])] param () $pathToSearch = 'C:\Program Files\Common Files\microsoft shared\Web Server Extensions\*\ISAPI\Microsoft.SharePoint.dll' $fullPath = Get-Item $pathToSearch -ErrorAction SilentlyContinue | Sort-Object { $_.Directory } -Descending | Select-Object -First 1 if ($null -eq $fullPath) { throw 'SharePoint path {C:\Program Files\Common Files\microsoft shared\Web Server Extensions} does not exist' } else { return (Get-Command $fullPath).FileVersionInfo } } function Add-SPSSheduledTask { param ( [Parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $ExecuteAsCredential, # Credentials for Task Schedule [Parameter(Mandatory = $true)] [System.String] $ActionArguments, # Arguments for the task action [Parameter()] [System.String] $TaskName = 'SPSWakeUP', # Name of the scheduled task to be added [Parameter()] [System.String] $TaskPath = 'SharePoint' # Path of the task folder ) # Initialize variables $TaskDate = Get-Date -Format yyyy-MM-dd # Current date in yyyy-MM-dd format $TaskCmd = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' # Path to PowerShell executable $UserName = $ExecuteAsCredential.UserName $Password = $ExecuteAsCredential.GetNetworkCredential().Password # Connect to the local TaskScheduler Service $TaskSvc = New-Object -ComObject ('Schedule.service') $TaskSvc.Connect($env:COMPUTERNAME) # Check if the folder exists, if not, create it try { $TaskFolder = $TaskSvc.GetFolder($TaskPath) # Attempt to get the task folder } catch { Write-Output "Task folder '$TaskPath' does not exist. Creating folder..." $RootFolder = $TaskSvc.GetFolder('\') # Get the root folder $RootFolder.CreateFolder($TaskPath) # Create the missing task folder $TaskFolder = $TaskSvc.GetFolder($TaskPath) # Get the newly created folder Write-Output "Successfully created task folder '$TaskPath'" } # Retrieve the scheduled task $getScheduledTask = $TaskFolder.GetTasks(0) | Where-Object -FilterScript { $_.Name -eq $TaskName } if ($getScheduledTask) { Write-Warning -Message 'Scheduled Task already exists - skipping.' # Task already exists } else { Write-Output '--------------------------------------------------------------' Write-Output "Adding '$TaskName' script in Task Scheduler Service ..." # Get credentials for Task Schedule $TaskAuthor = ([Security.Principal.WindowsIdentity]::GetCurrent()).Name # Author of the task $TaskUser = $UserName # Username for task registration $TaskUserPwd = $Password # Password for task registration # Add a new Task Schedule $TaskSchd = $TaskSvc.NewTask(0) $TaskSchd.RegistrationInfo.Description = "$($TaskName) Task - Start at 6:00 daily" # Task description $TaskSchd.RegistrationInfo.Author = $TaskAuthor # Task author $TaskSchd.Principal.RunLevel = 1 # Task run level (1 = Highest) # Task Schedule - Modify Settings Section $TaskSettings = $TaskSchd.Settings $TaskSettings.AllowDemandStart = $true $TaskSettings.Enabled = $true $TaskSettings.Hidden = $false $TaskSettings.StartWhenAvailable = $true # Task Schedule - Trigger Section $TaskTriggers = $TaskSchd.Triggers # Add Trigger Type 2 OnSchedule Daily Start at 6:00 AM $TaskTrigger1 = $TaskTriggers.Create(2) $TaskTrigger1.StartBoundary = $TaskDate + 'T06:00:00' $TaskTrigger1.DaysInterval = 1 $TaskTrigger1.Enabled = $true # Add Trigger Type 8 At StartUp Delay 10M $TaskTrigger2 = $TaskTriggers.Create(8) $TaskTrigger2.Delay = 'PT10M' $TaskTrigger2.Enabled = $true # Add Trigger Type 0 OnEvent IISReset $TrigSubscription = @" <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-IIS-IISReset'] and EventID=3201]]</Select></Query></QueryList> "@ $TaskTrigger3 = $TaskTriggers.Create(0) $TaskTrigger3.Delay = 'PT20S' $TaskTrigger3.Subscription = $TrigSubscription $TaskTrigger3.Enabled = $true # Define the task action $TaskAction = $TaskSchd.Actions.Create(0) # 0 = Executable action $TaskAction.Path = $TaskCmd # Path to the executable $TaskAction.Arguments = $ActionArguments # Arguments for the executable try { # Register the task $TaskFolder.RegisterTaskDefinition($TaskName, $TaskSchd, 6, $TaskUser, $TaskUserPwd, 1) Write-Output "Successfully added '$TaskName' script in Task Scheduler Service" Add-SPSWakeUpEvent -Message "Successfully added '$TaskName' script in Task Scheduler Service" -Source 'Add-SPSSheduledTask' -EntryType 'Information' } catch { $catchMessage = @" An error occurred while adding the script in scheduled task: $($TaskName) ActionArguments: $($ActionArguments) Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage # Handle any errors during task registration Add-SPSWakeUpEvent -Message $catchMessage -Source 'Add-SPSSheduledTask' -EntryType 'Error' } } } function Remove-SPSSheduledTask { param ( [Parameter(Mandatory = $true)] [System.String] $TaskName, # Name of the scheduled task to be removed [Parameter()] [System.String] $TaskPath = 'SharePoint' # Path of the task folder ) # Connect to the local TaskScheduler Service $TaskSvc = New-Object -ComObject ('Schedule.service') $TaskSvc.Connect($env:COMPUTERNAME) # Check if the folder exists try { $TaskFolder = $TaskSvc.GetFolder($TaskPath) # Attempt to get the task folder } catch { Write-Output "Task folder '$TaskPath' does not exist." } # Retrieve the scheduled task $getScheduledTask = $TaskFolder.GetTasks(0) | Where-Object -FilterScript { $_.Name -eq $TaskName } if ($null -eq $getScheduledTask) { Write-Warning -Message 'Scheduled Task already removed - skipping.' # Task not found } else { Write-Output '--------------------------------------------------------------' Write-Output "Removing $($TaskName) script in Task Scheduler Service ..." try { $TaskFolder.DeleteTask($TaskName, $null) # Remove the task Write-Output "Successfully removed $($TaskName) script from Task Scheduler Service" Add-SPSWakeUpEvent -Message "Successfully removed '$TaskName' script from Task Scheduler Service" -Source 'Remove-SPSSheduledTask' -EntryType 'Information' } catch { $catchMessage = @" An error occurred while removing the script in scheduled task: $($TaskName) Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage # Handle any errors during task removal Add-SPSWakeUpEvent -Message $catchMessage -Source 'Remove-SPSSheduledTask' -EntryType 'Error' } } } function Get-SPSVersion { process { try { # location in registry to get info about installed software $regLoc = Get-ChildItem HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall # Get SharePoint Products and language packs $programs = $regLoc | Where-Object -FilterScript { $_.PsPath -like '*\Office*' } | ForEach-Object -Process { Get-ItemProperty $_.PsPath } # output the info about Products and Language Packs $spsVersion = $programs | Where-Object -FilterScript { $_.DisplayName -like '*SharePoint Server*' } # Return SharePoint version $spsVersion.DisplayVersion } catch { Write-Warning -Message $_ } } } function Install-SPSWakeUP { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Path, [Parameter(Position = 2)] [System.Management.Automation.PSCredential] $InstallAccount ) # Initialize variables $ActionArguments = "-ExecutionPolicy Bypass -File $Path" $UserName = $InstallAccount.UserName $Password = $InstallAccount.GetNetworkCredential().Password $currentDomain = 'LDAP://' + ([ADSI]'').distinguishedName Write-Output "Checking Account `"$UserName`" ..." $dom = New-Object System.DirectoryServices.DirectoryEntry($currentDomain, $UserName, $Password) if ($null -eq $dom.Path) { Write-Warning -Message "Password Invalid for user:`"$UserName`"" Add-SPSWakeUpEvent -Message "Password Invalid for user:`"$UserName`"" -Source 'SPSWakeUP' -EntryType 'Error' Break } else { Write-Output "Account `"$UserName`" is valid. Adding SPSWakeUp script in Task Scheduler Service ..." # 1. Add SPSWakeup script in a new scheduled Task Add-SPSSheduledTask -ExecuteAsCredential $InstallAccount -ActionArguments $ActionArguments # 2. Get All Web Applications Urls $getSPWebApps = Get-SPSWebAppUrl # 3. Add read access for Warmup User account in User Policies settings if ($null -ne $getSPWebApps) { Add-SPSUserPolicy -Urls $getSPWebApps -UserName $UserName } } } function Clear-SPSLog { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $path, [Parameter()] [System.UInt32] $days = 30 ) if (Test-Path $path) { # Get the current date $Now = Get-Date # Definie the extension of log files $Extension = '*.log' # Define LastWriteTime parameter based on $days $LastWrite = $Now.AddDays(-$days) # Get files based on lastwrite filter and specified folder $files = Get-Childitem -Path "$path\*.*" -Include $Extension | Where-Object -FilterScript { $_.LastWriteTime -le "$LastWrite" } if ($files) { Write-Output "Cleaning log files in $path ..." foreach ($file in $files) { if ($null -ne $file) { Write-Output "Deleting file $file ..." Remove-Item $file.FullName | Out-Null } } } } } function Get-SPSThrottleLimit { # Get Number Of Throttle Limit process { try { $cimInstanceProc = @(Get-CimInstance -ClassName Win32_Processor) $cimInstanceSocket = $cimInstanceProc.count $numLogicalCpu = $cimInstanceProc[0].NumberOfLogicalProcessors * $cimInstanceSocket $NumThrottle = @{ $true = 10; $false = 2 * $numLogicalCpu }[$numLogicalCpu -ge 8] return $NumThrottle } catch { Write-Warning -Message $_ } } } function Add-SPSHostEntry { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $url ) $url = $url -replace 'https://', '' $url = $url -replace 'http://', '' $hostNameEntry = $url.split('/')[0] [void]$hostEntries.Add($hostNameEntry) } function Get-SPSAdminUrl { try { # Initialize ArrayList Object $tbCASitesURL = New-Object -TypeName System.Collections.ArrayList # Get Central Administration Url and add it in ArrayList Object $webAppADM = Get-SPWebApplication -IncludeCentralAdministration | Where-Object -FilterScript { $_.IsAdministrationWebApplication } [void]$tbCASitesURL.Add("$($webAppADM.Url)") # List of the most useful administration pages and Quick launch top links $urlsAdmin = @('Lists/HealthReports/AllItems.aspx', ` '_admin/FarmServers.aspx', ` '_admin/Server.aspx', ` '_admin/WebApplicationList.aspx', ` '_admin/ServiceApplications.aspx', ` 'applications.aspx', ` 'systemsettings.aspx', ` 'monitoring.aspx', ` 'backups.aspx', ` 'security.aspx', ` 'upgradeandmigration.aspx', ` 'apps.aspx', ` 'generalapplicationsettings.aspx') foreach ($urlAdmin in $urlsAdmin) { [void]$tbCASitesURL.Add("$($webAppADM.Url)$($urlAdmin)") } # Get Service Application Urls and then in ArrayList Object $sa = Get-SPServiceApplication $linkUrls = $sa | ForEach-Object { $_.ManageLink.Url } | Select-Object -Unique foreach ($linkUrl in $linkUrls) { $siteADMSA = $linkUrl.TrimStart('/') [void]$tbCASitesURL.Add("$($webAppADM.Url)$($siteADMSA)") } return $tbCASitesURL } catch { Write-Warning -Message $_ } } function Get-SPSSitesUrl { try { # Initialize ArrayList Object $tbSitesURL = New-Object -TypeName System.Collections.ArrayList # Get Url of all site collection $webApps = Get-SPWebApplication -ErrorAction SilentlyContinue if ($null -ne $webApps) { foreach ($webApp in $webApps) { $sites = $webApp.sites foreach ($site in $sites) { if ($AllSites) { $webs = (Get-SPWeb -Site $site -Limit ALL -ErrorAction SilentlyContinue) if ($null -ne $webs) { foreach ($web in $webs) { if ($web.Url -notmatch 'sitemaster-') { [void]$tbSitesURL.Add("$($web.Url)") } } } } else { if ($site.RootWeb.Url -notmatch 'sitemaster-') { [void]$tbSitesURL.Add("$($site.RootWeb.Url)") } } $site.Dispose() } } } return $tbSitesURL } catch { Write-Warning -Message $_ } } function Get-SPSWebAppUrl { try { # Initialize ArrayList Object $webAppURL = New-Object -TypeName System.Collections.ArrayList # Get SPwebApplication Object $webApps = Get-SPWebApplication -ErrorAction SilentlyContinue if ($null -ne $webApps) { foreach ($webapp in $webApps) { [void]$webAppURL.Add($webapp.GetResponseUri('Default').AbsoluteUri) $spSrvIsInUri = Get-SPServer | Where-Object -FilterScript { $webapp.GetResponseUri('Default').AbsoluteUri -match $_.Name } if ($null -eq $spSrvIsInUri) { Add-SPSHostEntry -Url $webapp.GetResponseUri('Default').AbsoluteUri } } $sites = $webApps | ForEach-Object -Process { $_.sites } $HSNCs = $sites | Where-Object -FilterScript { $_.HostHeaderIsSiteName -eq $true } foreach ($HSNC in $HSNCs) { if ($HSNC.Url -notmatch 'sitemaster-') { [void]$webAppURL.Add($HSNC.Url) Add-SPSHostEntry -Url $HSNC.Url } $HSNC.Dispose() } } else { $webAppURL = $null } return $webAppURL } catch { Write-Warning -Message $_ } } function Invoke-SPSWebRequest { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String[]] $Urls, [Parameter(Mandatory = $true)] $throttleLimit ) $ScriptBlock = { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Uri, [Parameter()] $Useragent = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome, [Parameter()] $SessionWeb ) Process { try { $startProcess = Get-Date if ($null -ne $sessionWeb) { $webResponse = Invoke-WebRequest -Uri $uri ` -WebSession $sessionWeb ` -TimeoutSec 90 ` -UserAgent $useragent ` -UseBasicParsing } else { $webResponse = Invoke-WebRequest -Uri $uri ` -UseDefaultCredentials ` -TimeoutSec 90 ` -UserAgent $useragent ` -UseBasicParsing } $timeExec = '{0:N2}' -f (((Get-Date) - $startProcess).TotalSeconds) $Response = "$([System.int32]$webResponse.StatusCode) - $($webResponse.StatusDescription)" } catch { $Response = $_.Exception.Message } finally { if ($webResponse) { $webResponse.Close() Remove-Variable webResponse } } $RunResult = New-Object PSObject $RunResult | Add-Member -MemberType NoteProperty -Name Url -Value $uri $RunResult | Add-Member -MemberType NoteProperty -Name 'Time(s)' -Value $TimeExec $RunResult | Add-Member -MemberType NoteProperty -Name Status -Value $Response $RunResult } } try { # Initialize variables and runpsace for Multi-Threading Request $psUserAgent = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome $Jobs = @() $iss = [system.management.automation.runspaces.initialsessionstate]::CreateDefault() $Pool = [runspacefactory]::CreateRunspacePool(1, $throttleLimit, $iss, $Host) $Pool.Open() } catch { $catchMessage = @" An error occurred invoking CreateRunspacePool Method Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage # Handle any errors during task removal Add-SPSWakeUpEvent -Message $catchMessage -Source 'CreateRunspacePool' -EntryType 'Error' } try { # Initialize WebSession from First SPWebApplication object $webApp = Get-SpWebApplication | Select-Object -first 1 $authentUrl = ("$($webapp.GetResponseUri('Default').AbsoluteUri)" + '_windows/default.aspx?ReturnUrl=/_layouts/15/Authenticate.aspx?Source=%2f') Write-Output "Getting webSession by opening $($authentUrl) with Invoke-WebRequest" Invoke-WebRequest -Uri $authentUrl ` -SessionVariable webSession ` -UseDefaultCredentials ` -UseBasicParsing ` -TimeoutSec 90 ` -UserAgent $psUserAgent } catch { $catchMessage = @" An error occurred invoking Invoke-WebRequest Function Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage # Handle any errors during task removal Add-SPSWakeUpEvent -Message $catchMessage -Source 'Invoke-WebRequest' -EntryType 'Error' } foreach ($Url in $Urls) { try { if (-not([string]::IsNullOrEmpty($Url))) { $Job = [powershell]::Create().AddScript($ScriptBlock).AddParameter('Uri', $Url).AddParameter('UserAgent', $psUserAgent).AddParameter('SessionWeb', $webSession) $Job.RunspacePool = $Pool $Jobs += New-Object PSObject -Property @{ Url = $Url Pipe = $Job Result = $Job.BeginInvoke() } } } catch { $catchMessage = @" An error occurred invoking Create Method for Url: $($Url) Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage # Handle any errors during task removal Add-SPSWakeUpEvent -Message $catchMessage -Source 'Invoke-SPSWebRequest' -EntryType 'Error' } } While ($Jobs.Result.IsCompleted -contains $false) { Start-Sleep -S 1 } $Results = @() foreach ($Job in $Jobs) { try { $Results += $Job.Pipe.EndInvoke($Job.Result) } catch { $catchMessage = @" An error occurred invoking Job Pipe EndInvoke Method for Url: $($Job.Url) Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage # Handle any errors during task removal Add-SPSWakeUpEvent -Message $catchMessage -Source 'Invoke-SPSWebRequest' -EntryType 'Error' } Finally { $Pool.Dispose() } } $Results } function Invoke-SPSAdminSites { $serviceInstance = Get-SPServiceInstance -Server $env:COMPUTERNAME if ($null -eq $serviceInstance) { $domain = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain $fqdn = "$($env:COMPUTERNAME).$domain" $serviceInstance = Get-SPServiceInstance -Server $fqdn } if ($null -ne $serviceInstance) { $serviceInstance = $serviceInstance | Where-Object -FilterScript { $_.GetType().Name -eq "SPWebServiceInstance" -and $_.Name -eq "WSS_Administration" } } if ($null -ne $serviceInstance) { Write-Output 'Opening All Central Admin Urls with Invoke-WebRequest, Please Wait...' $getSPADMSites = Get-SPSAdminUrl foreach ($spADMUrl in $getSPADMSites) { try { $startInvoke = Get-Date $webResponse = Invoke-WebRequest -Uri $spADMUrl -UseDefaultCredentials -TimeoutSec 90 -UseBasicParsing $TimeExec = '{0:N2}' -f (((Get-Date) - $startInvoke).TotalSeconds) Write-Output '-----------------------------------' Write-Output "| Url : $spADMUrl" Write-Output "| Time : $TimeExec" Write-Output "| Status : $($webResponse.StatusCode) - $($webResponse.StatusDescription)" } catch { $catchMessage = @" An error occurred with Invoke-WebRequest CMDLet Url: $($spADMUrl) Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage Add-SPSWakeUpEvent -Message $catchMessage -Source 'Invoke-SPSAdminSites' -EntryType 'Error' } } } else { Write-Warning -Message "No Central Admin Service Instance running on $env:COMPUTERNAME" Add-SPSWakeUpEvent -Message "No Central Admin Service Instance running on $env:COMPUTERNAME" -Source 'Invoke-SPSAdminSites' -EntryType 'Warning' } } function Invoke-SPSAllSites { # Initialize variables $DateStarted = Get-Date $hostEntries = New-Object -TypeName System.Collections.Generic.List[string] $hostsFile = "$env:windir\System32\drivers\etc\HOSTS" $hostsFileCopy = $hostsFile + '.' + (Get-Date -UFormat "%y%m%d%H%M%S").ToString() + '.copy' # Get All Web Applications Urls, Host Named Site Collection and Site Collections Write-Output '--------------------------------------------------------------' Write-Output 'Get URLs of All Web Applications ...' $getSPWebApps = Get-SPSWebAppUrl Write-Output '--------------------------------------------------------------' Write-Output 'Get URLs of All Site Collection ...' $getSPSites = Get-SPSSitesUrl if ($null -ne $getSPWebApps -and $null -ne $getSPSites) { if ($hostEntries) { # Disable LoopBack Check Disable-LoopbackCheck # Remove Duplicate Entries $hostEntries = $hostEntries | Get-Unique # Initialize variables $hostFileNeedsBackup = $true $hostIPV4Addr = '127.0.0.1' # Make backup copy of the Hosts file with today's date Add Web Application and Host Named Site Collection Urls in HOSTS system File Write-Output '--------------------------------------------------------------' Write-Output 'Add Urls of All Web Applications or HSNC in HOSTS File ...' foreach ($hostEntry in $hostEntries) { $hostEntryIsPresent = Select-String -Path $hostsFile -Pattern $hostEntry if ($null -eq $hostEntryIsPresent) { if ($hostFileNeedsBackup) { Write-Verbose -Message "Backing up $hostsFile file to: $hostsFileCopy" Copy-Item -Path $hostsFile -Destination $hostsFileCopy -Force $hostFileNeedsBackup = $false } # Remove http or https information to keep only HostName or FQDN if ($hostEntry.Contains(':')) { Write-Warning -Message "$hostEntry cannot be added in HOSTS File, only web applications with 80 or 443 port are added." } else { Write-Output "Adding $($hostEntry) in HOSTS file" Add-Content -Path $hostsFile -value "$hostIPV4Addr `t $hostEntry" } } } } # Request Url with Invoke-WebRequest CmdLet for All Urls Write-Output '--------------------------------------------------------------' Write-Output 'Opening All sites Urls with Invoke-WebRequest, Please Wait...' $InvokeResults = Invoke-SPSWebRequest -Urls $getSPSites -throttleLimit (Get-SPSThrottleLimit) # Show the results foreach ($InvokeResult in $InvokeResults) { if ($null -ne $InvokeResult.Url) { Write-Output '-----------------------------------' Write-Output "| Url : $($InvokeResult.Url)" Write-Output "| Time : $($InvokeResult.'Time(s)') seconds" Write-Output "| Status : $($InvokeResult.Status)" } } $DateEnded = Get-Date $totalUrls = $getSPSites.Count $totalDuration = ($DateEnded - $DateStarted).TotalSeconds $outputMessage = @" ------------------------------------- | SPSWakeUp Script - Invoke-SPSAllSites | Started on : $DateStarted | Completed on : $DateEnded | SPSWakeUp waked up $totalUrls urls in $totalDuration seconds -------------------------------------------------------------- | REPORTING: Memory Usage for each worker process (W3WP.EXE) | Process Creation Date | Memory | Application Pool Name -------------------------------------------------------------- "@ $w3wpProcess = Get-CimInstance Win32_Process -Filter "name = 'w3wp.exe'" | Select-Object WorkingSetSize, CommandLine, CreationDate | Sort-Object CommandLine foreach ($w3wpProc in $w3wpProcess) { $w3wpProcCmdLine = $w3wpProc.CommandLine.Replace('c:\windows\system32\inetsrv\w3wp.exe -ap "', '') $pos = $w3wpProcCmdLine.IndexOf('"') $appPoolName = $w3wpProcCmdLine.Substring(0, $pos) $w3wpMemoryUsage = [Math]::Round($w3wpProc.WorkingSetSize / 1MB) $outputMessage += ("`r`n" + "| $($w3wpProc.CreationDate) | $($w3wpMemoryUsage) MB | $($appPoolName)") } Write-Output $outputMessage Add-SPSWakeUpEvent -Message $outputMessage -Source 'Invoke-SPSAllSites'-EntryType Information # Clean the copy files of system HOSTS folder Clear-HostsFileCopy -hostsFilePath $hostsFile } } function Disable-LoopbackCheck { $lsaPath = 'HKLM:\System\CurrentControlSet\Control\Lsa' $lsaPathValue = Get-ItemProperty -path $lsaPath if (-not ($lsaPathValue.DisableLoopbackCheck -eq '1')) { Write-Output 'Disabling Loopback Check...' New-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\Lsa -Name 'DisableLoopbackCheck' -value '1' -PropertyType dword -Force | Out-Null } else { Write-Output 'Loopback Check already Disabled - skipping.' } } function Clear-HostsFileCopy { Param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $hostsFilePath, [Parameter()] [System.UInt32] $numberFiles = 10 ) $hostsFolderPath = Split-Path $hostsFilePath if (Test-Path $hostsFolderPath) { # Definie the extension of log files $extension = '*.copy' # Get files with .copy extension, sort them by name, from most recent to oldest and skip the first numberFiles variable $copyFiles = Get-ChildItem -Path "$hostsFolderPath\*.*" -Include $extension | Sort-Object -Descending -Property Name | Select-Object -Skip $numberFiles if ($copyFiles) { Write-Output '--------------------------------------------------------------' Write-Output "Cleaning backup HOSTS files in $hostsFolderPath ..." foreach ($copyFile in $copyFiles) { if ($null -ne $copyFile) { Write-Output " * Deleting File $copyFile ..." Remove-Item $copyFile.FullName | Out-Null } } } } } function Add-SPSUserPolicy { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $Urls, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $UserName ) Write-Output '--------------------------------------------------------------' Write-Output "Add Read Access to $UserName for All Web Applications ..." foreach ($url in $Urls) { try { $webapp = [Microsoft.SharePoint.Administration.SPWebApplication]::Lookup("$url") $displayName = 'SPSWakeUP Account' # If the web app is not Central Administration if ($webapp.IsAdministrationWebApplication -eq $false) { # If the web app is using Claims auth, change the user accounts to the proper syntax if ($webapp.UseClaimsAuthentication -eq $true) { $user = (New-SPClaimsPrincipal -identity $UserName -identitytype 1).ToEncodedString() } else { $user = $UserName } Write-Output "Checking Read access for $user account to $url..." [Microsoft.SharePoint.Administration.SPPolicyCollection]$policies = $webapp.Policies $policyExist = $policies | Where-Object -FilterScript { $_.Displayname -eq 'SPSWakeUP Account' } if (-not ($policyExist)) { Write-Output "Applying Read access for $user account to $url..." [Microsoft.SharePoint.Administration.SPPolicy]$policy = $policies.Add($user, $displayName) $policyRole = $webApp.PolicyRoles.GetSpecialRole([Microsoft.SharePoint.Administration.SPPolicyRoleType]::FullRead) if ($null -ne $policyRole) { $policy.PolicyRoleBindings.Add($policyRole) } $webapp.Update() Write-Output "Done Applying Read access for `"$user`" account to `"$url`"" } } } catch { $catchMessage = @" An error occurred while adding SPWebApp Policy for UserName: $UserName Url: $($url) Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage Add-SPSWakeUpEvent -Message $catchMessage -Source 'SPSWakeUP' -EntryType 'Error' } } } #endregion #region initialize SharePoint Context # Load SharePoint Powershell Snapin or Import-Module try { $installedVersion = Get-SPSInstalledProductVersion if ($installedVersion.ProductMajorPart -eq 15 -or $installedVersion.ProductBuildPart -le 12999) { if ($null -eq (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue)) { Add-PSSnapin Microsoft.SharePoint.PowerShell } } else { Import-Module SharePointServer -Verbose:$false -WarningAction SilentlyContinue -DisableNameChecking } } catch { # Handle errors during retrieval of Installed Product Version $catchMessage = @" Failed to get installed Product Version for $($env:COMPUTERNAME) Exception: $($_.Exception.Message) "@ Write-Error -Message $catchMessage Add-SPSWakeUpEvent -Message $catchMessage -Source 'Initialize Module' -EntryType 'Error' } # From SharePoint 2016, check if MinRole equal to Search try { $currentSPServer = Get-SPServer | Where-Object -FilterScript { $_.Address -eq $env:COMPUTERNAME } if ($null -ne $currentSPServer -and (Get-SPFarm).buildversion.major -ge 16) { if ($currentSPServer.Role -eq 'Search') { Write-Warning -Message 'You run this script on server with Search MinRole' Add-SPSWakeUpEvent -Message 'Search MinRole is not supported in SPSWakeUp' -Source 'Server MinRole' -EntryType 'Warning' Break } } } catch { Write-Error -Message @" An error occurred while checking the SharePoint Server Role Exception: $($_.Exception.Message) "@ } #endregion #region main switch ($Action) { 'Uninstall' { # Remove SPSWakeup script from scheduled Task Remove-SPSSheduledTask -TaskName 'SPSWakeUP' } 'Install' { if ($null -eq $InstallAccount) { Write-Warning -Message ('SPSWakeUp: Install parameter is set. Please set also InstallAccount ' + ` "parameter. `nSee https://github.com/luigilink/SPSWakeUp/wiki for details.") Break } else { # Initialize variables $scriptFullPath = Join-Path -Path $scriptRootPath -ChildPath 'SPSWakeUP.ps1' # Add SPSWakeup script in a new scheduled Task Install-SPSWakeUP -Path $scriptFullPath -InstallAccount $InstallAccount } } 'AdminSitesOnly' { # Invoke-WebRequest on Central Admin if Action parameter equal to AdminSitesOnly Invoke-SPSAdminSites } Default { # Invoke-WebRequest on Central Admin if Action parameter equal to Default Invoke-SPSAdminSites # Invoke-WebRequest on All Web Applications Urls, Host Named Site Collection and Site Collections Invoke-SPSAllSites } } # Stop Transcript parameter is equal to True if ($Transcript) { Stop-Transcript } Exit #endregion |