├── README.md ├── SPBestWarmUp.ps1 ├── SPBestWarmUp.xml └── doc ├── 1.jpg ├── 2.jpg ├── 3.jpg └── download.png /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Tired of waiting for SharePoint pages to load? Want something easy to support? That works on all versions? This warmup script is for you! 3 | 4 | I used several different warmup scripts. All worked OK but each seemed to lack features so I decided to create a new one. Hope you find it useful too. 5 | 6 | [![](https://raw.githubusercontent.com/spjeff/spbestwarmup/master/doc/download.png)](https://github.com/spjeff/spbestwarmup/releases/download/v2.4.22/SPBestWarmUp.zip) 7 | 8 | ## Key Features 9 | * SharePoint 2010, 2013, and 2016 10 | * Custom page URLs 11 | * Detect SP Web Application URLs 12 | * Detect Host Named Site Collection URLs 13 | * Detects Service Application URLs 14 | * Detects Project Server PWA 15 | * Download full page resources (JS, images) not just HTML 16 | * Download with `Invoke-WebRequest` 17 | * Great for ECM websites to populate blob cache 18 | * Warm up Central Admin - home, health rules, web apps for a faster admin experience! 19 | * Warm up Central Admin - all Manage Service Application pages. 20 | * Display W3WP total #MB before and after 21 | * Excellent blog post by [@hd_ka](https://twitter.com/hd_ka) at [http://blog.greenbrain.de/2014/10/fire-up-those-caches.html](http://blog.greenbrain.de/2014/10/fire-up-those-caches.html) 22 | 23 | ## Quick Start 24 | * Download `SPBestWarmUp.ps1` 25 | * Copy `SPBestWarmUp.ps1` to one SharePoint web front end (WFE) 26 | * (Optionnally) Run the following powershell command to avoid error on the next command : Set-ExecutionPolicy -ExecutionPolicy ByPass 27 | * Run `SPBestWarmUp.ps1 -f` to install farm wide. Creates Task Scheduler job on every machine. 28 | * Run `SPBestWarmUp.ps1 -i` to install locally. Creates Task Scheduler job on the local machine. 29 | * Run `SPBestWarmUp.ps1 -u` to uninstall farm wide. Deletes any Task Scheduler job named "SPBestWarmup" 30 | * Sit back and enjoy! 31 | 32 | ## Screenshots 33 | 34 | * Install farm wide 35 | ![image](https://raw.githubusercontent.com/spjeff/spbestwarmup/master/doc/1.jpg) 36 | 37 | * Manual run 38 | ![image](https://raw.githubusercontent.com/spjeff/spbestwarmup/master/doc/2.jpg) 39 | 40 | * Manual run with custom URL parameter 41 | ![image](https://raw.githubusercontent.com/spjeff/spbestwarmup/master/doc/2.jpg) 42 | 43 | ## Admin Tip 44 | * After reboot run this command to manually trigger the job and warm up IIS 45 | `SCHTASKS /RUN /TN "SPBestWarmUp"` 46 | 47 | ## Custom URLs 48 | * Use the `-url` paramter to add custom URLs from the command line. 49 | * Rename Central Admin site title and edit lines `331-345` to add custom URLs within the script. Good for lifecycle (dev, test, prod). 50 | 51 | ## Permission 52 | * Running this with a different service account than farm might require you to first grant PowerShell access. This ensures the service account has access to run `Get-SPWebApplication` and `Get-SPServer` for detecting URLs to load. [http://technet.microsoft.com/en-us/magazine/gg490648.aspx 53 | ](http://technet.microsoft.com/en-us/magazine/gg490648.aspx) 54 | 55 | ## Contact 56 | Please drop a line to [@spjeff](https://twitter.com/spjeff) or [spjeff@spjeff.com](mailto:spjeff@spjeff.com) 57 | Thanks! =) 58 | 59 | ![image](http://img.shields.io/badge/first--timers--only-friendly-blue.svg?style=flat-square) 60 | 61 | 62 | ## License 63 | 64 | The MIT License (MIT) 65 | 66 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 67 | 68 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 71 | -------------------------------------------------------------------------------- /SPBestWarmUp.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Warm up SharePoint IIS W3WP memory cache by loading pages from WebRequest 4 | 5 | .DESCRIPTION 6 | Loads the full page so resources like CSS, JS, and images are included. Please modify lines 350-368 to suit your portal content design (popular URLs, custom pages, etc.) 7 | 8 | Comments and suggestions always welcome! Please, use the issues panel at the project page. 9 | 10 | .PARAMETER url 11 | A collection of url that will be added to the list of websites the script will fetch. 12 | 13 | .PARAMETER install 14 | Typing "SPBestWarmUp.ps1 -install" will create a local machine Task Scheduler job under credentials of the current user. Job runs every 60 minutes on the hour to help automatically populate cache. Keeps cache full even after IIS daily recycle, WSP deployment, reboot, or other system events. 15 | 16 | .PARAMETER installfarm 17 | Typing "SPBestWarmUp.ps1 -installfarm" will create a Task Scheduler job on all machines in the farm. 18 | 19 | .PARAMETER uninstall 20 | Typing "SPBestWarmUp.ps1 -uninstall" will remove Task Scheduler job from all machines in the farm. 21 | 22 | .PARAMETER user 23 | Typing "SPBestWarmUp.ps1 -user" provides the user name that will be used for the execution of the Task Scheduler job. If this parameter is missing it is assumed that the Task Scheduler job will be run with the current user. 24 | 25 | .PARAMETER allsites 26 | Typing "SPBestWarmUp.ps1 -allsites" will load every site and web URL. If the parameter skipsubwebs is used, only the root web of each site collection will be processed. 27 | 28 | .PARAMETER transcript 29 | Typing "SPBestWarmUp.ps1 -transcript" will generate a full PowerShell text transcript of the script exeuction. Helpful for debug. 30 | 31 | .PARAMETER skiplog 32 | Typing "SPBestWarmUp.ps1 -skiplog" will avoid writing to the EventLog 33 | 34 | .PARAMETER skipsubwebs 35 | Typing "SPBestWarmUp.ps1 -skipsubwebs" will skip the subwebs of each site collection and only process the root web of the site collection. 36 | 37 | .PARAMETER skipadmincheck 38 | Typing "SPBestWarmUp.ps1 -skipadmincheck" will skip checking of the current user is a local administrator. Local administrator rights are necessary for the installation of the Windows Task Scheduler but not necessary for simply running the warmup script. 39 | 40 | .EXAMPLE 41 | .\SPBestWarmUp.ps1 -url "http://domainA.tld","http://domainB.tld" 42 | 43 | .EXAMPLE 44 | .\SPBestWarmUp.ps1 -i 45 | .\SPBestWarmUp.ps1 -install 46 | 47 | .EXAMPLE 48 | .\SPBestWarmUp.ps1 -f 49 | .\SPBestWarmUp.ps1 -installfarm 50 | 51 | .EXAMPLE 52 | .\SPBestWarmUp.ps1 -f -user "Contoso\JaneDoe" 53 | .\SPBestWarmUp.ps1 -installfarm -user "Contoso\JaneDoe" 54 | 55 | .EXAMPLE 56 | .\SPBestWarmUp.ps1 -u 57 | .\SPBestWarmUp.ps1 -uninstall 58 | 59 | .NOTES 60 | File Name: SPBestWarmUp.ps1 61 | Author : Jeff Jones - @spjeff 62 | Author : Hagen Deike - @hd_ka 63 | Author : Lars Fernhomberg 64 | Author : Charles Crossan - @crossan007 65 | Author : Leon Lennaerts 66 | Author : Dan Rullo 67 | Modified : 2023-12-09 68 | 69 | .LINK 70 | https://github.com/spjeff/spbestwarmup 71 | #> 72 | 73 | 74 | [CmdletBinding()] 75 | param ( 76 | [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline = $false, HelpMessage = 'A collection of URLs that will be fetched too')] 77 | [Alias("url")] 78 | [ValidateNotNullOrEmpty()] 79 | [ValidatePattern("https?:\/\/\D+")] 80 | [string[]]$cmdurl, 81 | 82 | [Parameter(Mandatory = $False, Position = 1, ValueFromPipeline = $false, HelpMessage = 'Use -install -i parameter to add script to Windows Task Scheduler on local machine')] 83 | [Alias("i")] 84 | [switch]$install, 85 | 86 | [Parameter(Mandatory = $False, Position = 2, ValueFromPipeline = $false, HelpMessage = 'Use -installfarm -f parameter to add script to Windows Task Scheduler on all farm machines')] 87 | [Alias("f")] 88 | [switch]$installfarm, 89 | 90 | [Parameter(Mandatory = $False, Position = 3, ValueFromPipeline = $false, HelpMessage = 'Use -uninstall -u parameter to remove Windows Task Scheduler job')] 91 | [Alias("u")] 92 | [switch]$uninstall, 93 | 94 | [Parameter(Mandatory = $False, Position = 4, ValueFromPipeline = $false, HelpMessage = 'Use -user to provide the login of the user that will be used to run the script in the Windows Task Scheduler job')] 95 | [string]$user, 96 | 97 | [Parameter(Mandatory = $False, Position = 5, ValueFromPipeline = $false, HelpMessage = 'Use -skiplog -sl parameter to avoid writing to Event Log')] 98 | [Alias("sl")] 99 | [switch]$skiplog, 100 | 101 | [Parameter(Mandatory = $False, Position = 6, ValueFromPipeline = $false, HelpMessage = 'Use -allsites -all parameter to load every site and web (if skipsubwebs parameter is also given, only the root web will be processed)')] 102 | [Alias("all")] 103 | [switch]$allsites, 104 | 105 | [Parameter(Mandatory = $False, Position = 7, ValueFromPipeline = $false, HelpMessage = 'Use -skipsubwebs -sw parameter to skip subwebs of each site collection and to process only the root web')] 106 | [Alias("sw")] 107 | [switch]$skipsubwebs, 108 | 109 | [Parameter(Mandatory = $False, Position = 8, ValueFromPipeline = $false, HelpMessage = 'Use -skipadmincheck -sac parameter to skip checking if the current user is an administrator')] 110 | [Alias("sac")] 111 | [switch]$skipadmincheck, 112 | 113 | [Parameter(Mandatory = $False, Position = 9, ValueFromPipeline = $false, HelpMessage = 'Use -skipserviceapps -ssa parameter to skip warming up of Service Application Endpoints URLs')] 114 | [Alias("ssa")] 115 | [switch]$skipserviceapps, 116 | 117 | [Parameter(Mandatory = $False, Position = 10, ValueFromPipeline = $false, HelpMessage = 'Use -skipprogress -sp parameter to skip display of progres bar. Faster execution for background scheduling.')] 118 | [Alias("sp")] 119 | [switch]$skipprogress, 120 | 121 | [Parameter(Mandatory = $False, Position = 11, ValueFromPipeline = $false, HelpMessage = 'Use -transcript -t parameter to generate full PowerShell transcript. Helpful for debug.')] 122 | [Alias("t")] 123 | [switch]$transcript 124 | ) 125 | 126 | Function Installer() { 127 | # Add to Task Scheduler 128 | Write-Output " Installing to Task Scheduler..." 129 | if (!$user) { 130 | $user = $ENV:USERDOMAIN + "\" + $ENV:USERNAME 131 | } 132 | Write-Output " User for Task Scheduler job: $user" 133 | 134 | try { 135 | # Attempt to detect password from IIS Pool (if current user is local admin and farm account) 136 | $appPools = Get-WMIObject -Namespace "root/MicrosoftIISv2" -Class "IIsApplicationPoolSetting" -ErrorAction SilentlyContinue | Select-Object WAMUserName, WAMUserPass 137 | foreach ($pool in $appPools) { 138 | if ($pool.WAMUserName -like $user) { 139 | $pass = $pool.WAMUserPass 140 | if ($pass) { 141 | break 142 | } 143 | } 144 | } 145 | } 146 | catch { 147 | # Manual input if auto detect failed 148 | if (!$pass) { 149 | $pass = Read-Host "Enter password for $user " 150 | } 151 | } 152 | # Task Scheduler command 153 | $suffix += " -skipadmincheck" #We do not need administrative rights on local machines to check the farm 154 | if ($allsites) { $suffix += " -allsites" } 155 | if ($skipsubwebs) { $suffix += " -skipsubwebs" } 156 | if ($skiplog) { $suffix += " -skiplog" } 157 | if ($skipprogress) { $suffix += " -skipprogress" } 158 | $cmd = '-ExecutionPolicy Bypass -File "' + $cmdpath + '"' + $suffix 159 | 160 | # Target machines 161 | $machines = @() 162 | if ($installfarm -or $uninstall) { 163 | # Create farm wide on remote machines 164 | foreach ($srv in (Get-SPServer | Where-Object { $_.Role -ne "Invalid" })) { 165 | $machines += $srv.Address 166 | } 167 | } 168 | else { 169 | # Create local on current machine 170 | $machines += "localhost" 171 | } 172 | $machines | ForEach-Object { 173 | try { 174 | if ($uninstall) { 175 | # Delete task 176 | WriteLog "SCHTASKS DELETE on $_" 177 | schtasks /s $_ /delete /tn "SPBestWarmUp" /f 178 | WriteLog " [OK]" Green 179 | } 180 | else { 181 | $xmlCmdPath = $cmdpath.Replace(".ps1", ".xml") 182 | # Ensure that XML file is present 183 | if (!(Test-Path $xmlCmdPath)) { 184 | Write-Warning """$($xmlCmdPath)"" is missing. Cannot create timer job without missing file." 185 | $xmlTemplate = @" 186 | 187 | 188 | 189 | 190 | 191 | PT1H 192 | P1D 193 | false 194 | 195 | 2017-01-25T01:00:00 196 | true 197 | 198 | 1 199 | 200 | 201 | 202 | true 203 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-IIS-IISReset'] and EventID=3201]]</Select></Query></QueryList> 204 | PT1M 205 | 206 | 207 | true 208 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5074]]</Select></Query></QueryList> 209 | PT1M 210 | 211 | 212 | true 213 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5075]]</Select></Query></QueryList> 214 | PT1M 215 | 216 | 217 | true 218 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5076]]</Select></Query></QueryList> 219 | PT1M 220 | 221 | 222 | true 223 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5077]]</Select></Query></QueryList> 224 | PT1M 225 | 226 | 227 | true 228 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5078]]</Select></Query></QueryList> 229 | PT1M 230 | 231 | 232 | true 233 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5079]]</Select></Query></QueryList> 234 | PT1M 235 | 236 | 237 | true 238 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5080]]</Select></Query></QueryList> 239 | 240 | 241 | true 242 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5081]]</Select></Query></QueryList> 243 | PT1M 244 | 245 | 246 | true 247 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5117]]</Select></Query></QueryList> 248 | PT1M 249 | 250 | 251 | true 252 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5186]]</Select></Query></QueryList> 253 | PT1M 254 | 255 | 256 | true 257 | PT5M 258 | 259 | 260 | 261 | 262 | 263 | Password 264 | HighestAvailable 265 | 266 | 267 | 268 | IgnoreNew 269 | true 270 | true 271 | true 272 | false 273 | false 274 | 275 | true 276 | false 277 | 278 | true 279 | true 280 | false 281 | false 282 | false 283 | P3D 284 | 7 285 | 286 | 287 | 288 | PowerShell.exe 289 | 290 | 291 | 292 | 293 | 294 | "@ 295 | $xmlTemplate | Out-File $xmlCmdPath -Force 296 | } 297 | 298 | # Update xml file 299 | Write-Host "xmlCmdPath - $xmlCmdPath" 300 | $xml = [xml](Get-Content $xmlCmdPath) 301 | $xml.Task.Principals.Principal.UserId = $user 302 | $xml.Task.Actions.Exec.Arguments = $cmd 303 | $xml.Task.Actions.Exec.WorkingDirectory = (Split-Path ($xmlCmdPath)).ToString() 304 | $xml.Save($xmlCmdPath) 305 | 306 | # Copy local file to remote UNC path machine 307 | Write-Output "SCHTASKS CREATE on $_" 308 | if ($_ -ne "localhost" -and $_ -ne $ENV:COMPUTERNAME) { 309 | $dest = $cmdpath 310 | $drive = $dest.substring(0, 1) 311 | $match = Get-WMIObject -Class Win32_LogicalDisk | Where-Object { $_.DeviceID -eq ($drive + ":") -and $_.DriveType -eq 3 } 312 | if ($match) { 313 | $dest = "\\" + $_ + "\" + $drive + "$" + $dest.substring(2, $dest.length - 2) 314 | $xmlDest = $dest.Replace(".ps1", ".xml") 315 | mkdir (Split-Path $dest) -ErrorAction SilentlyContinue | Out-Null 316 | Write-Output $dest 317 | Copy-Item $cmdpath $dest -Confirm:$false 318 | Copy-Item $xmlCmdPath $xmlDest -Confirm:$false 319 | } 320 | } 321 | # Create task 322 | WriteLog "SCHTASKS CREATE on $_" 323 | schtasks /s $_ /create /tn "SPBestWarmUp" /ru $user /rp $pass /xml $xmlCmdPath 324 | WriteLog " [OK]" Green 325 | } 326 | } 327 | catch { 328 | $ErrorMessage = $_.Exception.Message 329 | $FailedItem = $_.Exception.ItemName 330 | WriteLog " [ERROR] $ErrorMessage $FailedItem" Yellow 331 | } 332 | } 333 | } 334 | 335 | Function WarmUp() { 336 | # Load plugin 337 | Add-PSSnapIn Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue 338 | 339 | # Warm up CMD parameter URLs 340 | $cmdurl | ForEach-Object { NavigateTo $_ } 341 | 342 | # Warm up SharePoint web applications 343 | Write-Output "Opening Web Applications..." 344 | 345 | # Accessing the Alternate URls to warm up all "extended webs" (i.e. multiple IIS websites exists for one SharePoint webapp) 346 | $was = Get-SPWebApplication -IncludeCentralAdministration 347 | foreach ($wa in $was) { 348 | foreach ($alt in $wa.AlternateUrls) { 349 | $url = $alt.PublicUrl 350 | if (!$url.EndsWith("/")) { 351 | $url = $url + "/" 352 | } 353 | 354 | # SharePoint Core 355 | NavigateTo $url 356 | NavigateTo $url"_api/web" 357 | NavigateTo $url"_api/_trust" 358 | NavigateTo $url"_layouts/viewlsts.aspx" 359 | NavigateTo $url"_layouts/settings.aspx" 360 | NavigateTo $url"_vti_bin/UserProfileService.asmx" 361 | NavigateTo $url"_vti_bin/sts/spsecuritytokenservice.svc" 362 | NavigateTo $url"_api/search/query?querytext='warmup'" 363 | 364 | # SharePoint 2016 365 | NavigateTo $url"_layouts/15/fonts/shellicons.eot" 366 | NavigateTo $url"_layouts/15/jsgrid.js" 367 | NavigateTo $url"_layouts/15/sp.js" 368 | NavigateTo $url"_layouts/15/sp.ribbon.js" 369 | NavigateTo $url"_layouts/15/core.js" 370 | NavigateTo $url"_layouts/15/init.js" 371 | NavigateTo $url"_layouts/15/cui.js" 372 | NavigateTo $url"_layouts/15/inplview.js" 373 | NavigateTo $url"_layouts/15/suitenav.js" 374 | } 375 | 376 | # Warm Up Individual Site Collections and Sites 377 | if ($allsites) { 378 | $sites = (Get-SPSite -WebApplication $wa -Limit ALL) 379 | foreach ($site in $sites) { 380 | if ($skipsubwebs) { 381 | $url = $site.RootWeb.Url 382 | NavigateTo $url 383 | } 384 | else { 385 | $webs = (Get-SPWeb -Site $site -Limit ALL) 386 | foreach ($web in $webs) { 387 | $url = $web.Url 388 | NavigateTo $url 389 | } 390 | } 391 | } 392 | } 393 | 394 | # Central Admin 395 | if ($wa.IsAdministrationWebApplication) { 396 | $url = $wa.Url 397 | # Specific pages 398 | NavigateTo $url"Lists/HealthReports/AllItems.aspx" 399 | NavigateTo $url"_admin/FarmServers.aspx" 400 | NavigateTo $url"_admin/Server.aspx" 401 | NavigateTo $url"_admin/WebApplicationList.aspx" 402 | NavigateTo $url"_admin/ServiceApplications.aspx" 403 | 404 | # Quick launch top links 405 | NavigateTo $url"applications.aspx" 406 | NavigateTo $url"systemsettings.aspx" 407 | NavigateTo $url"monitoring.aspx" 408 | NavigateTo $url"backups.aspx" 409 | NavigateTo $url"security.aspx" 410 | NavigateTo $url"security.aspx" 411 | NavigateTo $url"upgradeandmigration.aspx" 412 | NavigateTo $url"apps.aspx" 413 | NavigateTo $url"office365configuration.aspx" 414 | NavigateTo $url"generalapplicationsettings.aspx" 415 | 416 | # Manage Service Application 417 | $sa = Get-SPServiceApplication 418 | $links = $sa | ForEach-Object { $_.ManageLink.Url } | Select-Object -Unique 419 | foreach ($link in $links) { 420 | $ml = $link.TrimStart('/') 421 | NavigateTo "$url$ml" 422 | } 423 | } 424 | } 425 | 426 | # Warm up Service Applications 427 | if (!$skipserviceapps) { 428 | Get-SPServiceApplication | ForEach-Object { 429 | $_.EndPoints | ForEach-Object { 430 | $_.ListenUris | ForEach-Object { 431 | $uri = $_.AbsoluteUri -Replace "/https$", $null -Replace "/http$", $null -Replace "/secure$", $null -Replace "/optimized$", $null 432 | NavigateTo $uri 433 | } 434 | } 435 | } 436 | } 437 | 438 | # Warm up Project Server 439 | Write-Output "Opening Project Server PWAs..." 440 | if ((Get-Command Get-SPProjectWebInstance -ErrorAction SilentlyContinue).Count -gt 0) { 441 | Get-SPProjectWebInstance | ForEach-Object { 442 | # Thanks to Eugene Pavlikov for the snippet 443 | $url = ($_.Url).AbsoluteUri + "/" 444 | 445 | NavigateTo $url 446 | NavigateTo ($url + "_layouts/viewlsts.aspx") 447 | NavigateTo ($url + "_vti_bin/UserProfileService.asmx") 448 | NavigateTo ($url + "_vti_bin/sts/spsecuritytokenservice.svc") 449 | NavigateTo ($url + "Projects.aspx") 450 | NavigateTo ($url + "Approvals.aspx") 451 | NavigateTo ($url + "Tasks.aspx") 452 | NavigateTo ($url + "Resources.aspx") 453 | NavigateTo ($url + "ProjectBICenter/Pages/Default.aspx") 454 | NavigateTo ($url + "_layouts/15/pwa/Admin/Admin.aspx") 455 | } 456 | } 457 | 458 | # Warm up Topology 459 | NavigateTo "http://localhost:32843/Topology/topology.svc" 460 | 461 | # Custom URLs - Add your own below 462 | # NavigateTo "http://portal/popularPage.aspx" 463 | # NavigateTo "http://portal/popularPage2.aspx" 464 | # NavigateTo "http://portal/popularPage3.aspx 465 | 466 | # Warm up Host Name Site Collections (HNSC) 467 | Write-Output "Opening Host Name Site Collections (HNSC)..." 468 | $hnsc = Get-SPSite -Limit All | Where-Object { $_.HostHeaderIsSiteName -eq $true } | Select-Object Url 469 | foreach ($sc in $hnsc) { 470 | NavigateTo $sc.Url 471 | } 472 | 473 | # Warm up Office Online Server (OOS) 474 | $remoteuis = "m,o,oh,op,p,we,wv,x".Split(",") 475 | $services = "diskcache/DiskCache.svc,dss/DocumentSessionService.svc,ecs/ExcelService.asmx,farmstatemanager/FarmStateManager.svc,metb/BroadcastStateService.svc,pptc/Viewing.svc,ppte/Editing.svch,wdss/WordDocumentSessionService.svc,wess/WordSaveService.svc,wvc/Conversion.svc".Split(",") 476 | 477 | # Loop per WOPI 478 | $wopis = Get-SPWOPIBinding | Select-Object ServerName -Unique 479 | foreach ($w in $wopis.ServerName) { 480 | foreach ($r in $remoteuis) { 481 | NavigateTo "http://$w/$r/RemoteUIs.ashx" 482 | NavigateTo "https://$w/$r/RemoteUIs.ashx" 483 | } 484 | foreach ($s in $services) { 485 | NavigateTo ("http://$w" + ":809/$s") 486 | NavigateTo ("https://$w" + ":810/$s") 487 | } 488 | } 489 | } 490 | 491 | Function NavigateTo([string] $url) { 492 | if ($url.ToUpper().StartsWith("HTTP") -and !$url.EndsWith("/ProfileService.svc", "CurrentCultureIgnoreCase")) { 493 | WriteLog " $url" 494 | # WebRequest command line 495 | try { 496 | $wr = Invoke-WebRequest -Uri $url -UseBasicParsing -UseDefaultCredentials -TimeoutSec 120 497 | FetchResources $url $wr.Images 498 | FetchResources $url $wr.Scripts 499 | Write-Host "." 500 | } 501 | catch { 502 | $httpCode = $_.Exception.Response.StatusCode.Value__ 503 | if ($httpCode) { 504 | WriteLog " [$httpCode]" Yellow 505 | } 506 | else { 507 | Write-Host " " 508 | } 509 | } 510 | } 511 | } 512 | 513 | Function FetchResources($baseUrl, $resources) { 514 | # Download additional HTTP files 515 | [uri]$uri = $baseUrl 516 | $rootUrl = $uri.Scheme + "://" + $uri.Authority 517 | 518 | # Loop 519 | $counter = 0 520 | foreach ($res in $resources) { 521 | # Support both abosolute and relative URLs 522 | $resUrl = $res.src 523 | if ($resUrl.ToUpper().Contains("HTTP")) { 524 | $fetchUrl = $res.src 525 | } 526 | else { 527 | if (!$resUrl.StartsWith("/")) { 528 | $resUrl = "/" + $resUrl 529 | } 530 | $fetchUrl = $rootUrl + $resUrl 531 | } 532 | 533 | # Progress 534 | if (!$skipprogress) { 535 | Write-Progress -Activity "Opening " -Status $fetchUrl -PercentComplete (($counter / $resources.Count) * 100) 536 | $counter++ 537 | } 538 | 539 | # Execute 540 | Invoke-WebRequest -UseDefaultCredentials -UseBasicParsing -Uri $fetchUrl -TimeoutSec 120 | Out-Null 541 | Write-Host "." -NoNewLine 542 | } 543 | if (!$skipprogress) { 544 | Write-Progress -Activity "Completed" -Completed 545 | } 546 | } 547 | 548 | Function ShowW3WP() { 549 | # Total memory used by IIS worker processes 550 | $mb = [Math]::Round((Get-Process W3WP -ErrorAction SilentlyContinue | Select-Object workingset64 | Measure-Object workingset64 -Sum).Sum / 1MB) 551 | WriteLog "Total W3WP = $mb MB" "Green" 552 | } 553 | 554 | Function CreateLog() { 555 | # EventLog - create source if missing 556 | if (!(Get-EventLog -LogName Application -Source "SPBestWarmUp" -Newest 1 -ErrorAction SilentlyContinue)) { 557 | New-EventLog -LogName Application -Source "SPBestWarmUp" -ErrorAction SilentlyContinue | Out-Null 558 | } 559 | } 560 | 561 | Function WriteLog($text, $color) { 562 | $global:msg += "`n$text" 563 | if ($color) { 564 | Write-Host $text -Fore $color 565 | } 566 | else { 567 | Write-Output $text 568 | } 569 | } 570 | 571 | Function SaveLog($id, $txt, $exception) { 572 | # EventLog 573 | if (!$skiplog) { 574 | if (!$exception) { 575 | # Success 576 | $global:msg += $txt 577 | Write-EventLog -LogName Application -Source "SPBestWarmUp" -EntryType Information -EventId $id -Message $global:msg 578 | } 579 | else { 580 | # Error 581 | $global:msg += "ERROR`n" 582 | $global:msg += $exception.Message + "`n" + $exception.ItemName 583 | Write-EventLog -LogName Application -Source "SPBestWarmUp" -EntryType Warning -EventId $id -Message $global:msg 584 | } 585 | } 586 | } 587 | 588 | # Main 589 | 590 | # Transcript 591 | if ($transcript) { 592 | $startTime = (Get-Date) 593 | $datestamp = $startTime.ToString("yyyy-MM-dd-hh-mm-ss") 594 | Start-Transcript "SPBestWarmup-$datestamp.log" 595 | } 596 | 597 | # Log 598 | $global:msg = "" 599 | CreateLog 600 | $cmdpath = (Resolve-Path .\).Path 601 | $cmdpath += "\SPBestWarmUp.ps1" 602 | $ver = $PSVersionTable.PSVersion 603 | WriteLog "SPBestWarmUp v2.4.22 (last updated 2023-12-09)`n------`n" 604 | WriteLog "Path: $cmdpath" 605 | WriteLog "PowerShell Version: $ver" 606 | 607 | # Suppress progress bar display 608 | if ($skipprogress) { 609 | $ProgressPreference = 'SilentlyContinue' 610 | } 611 | 612 | # Check Permission Level 613 | if (!$skipadmincheck -and !([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { 614 | Write-Warning "You do not have elevated Administrator rights to run this script.`nPlease re-run as Administrator." 615 | break 616 | } 617 | else { 618 | try { 619 | # SharePoint cmdlets 620 | Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null 621 | 622 | # Task Scheduler 623 | $tasks = schtasks /query /fo csv | ConvertFrom-Csv 624 | $spb = $tasks | Where-Object { $_.TaskName -eq "\SPBestWarmUp" } 625 | if (!$spb -and !$install -and !$installfarm) { 626 | Write-Warning "Tip: to install on Task Scheduler run the command ""SPBestWarmUp.ps1 -install""" 627 | } 628 | if ($install -or $installfarm -or $uninstall) { 629 | Installer 630 | SaveLog 2 "Installed to Task Scheduler" 631 | break 632 | } 633 | if ($uninstall) { 634 | break 635 | } 636 | 637 | # Core 638 | ShowW3WP 639 | WarmUp 640 | ShowW3WP 641 | 642 | # Log 643 | SaveLog 1 "Operation completed successfully" 644 | } 645 | catch { 646 | SaveLog 101 "ERROR" $_.Exception 647 | } 648 | 649 | # Transcript 650 | if ($transcript) { 651 | Stop-Transcript 652 | } 653 | } 654 | -------------------------------------------------------------------------------- /SPBestWarmUp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PT1H 7 | P1D 8 | false 9 | 10 | 2017-01-25T01:00:00 11 | true 12 | 13 | 1 14 | 15 | 16 | 17 | true 18 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-IIS-IISReset'] and EventID=3201]]</Select></Query></QueryList> 19 | PT1M 20 | 21 | 22 | true 23 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5074]]</Select></Query></QueryList> 24 | PT1M 25 | 26 | 27 | true 28 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5075]]</Select></Query></QueryList> 29 | PT1M 30 | 31 | 32 | true 33 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5076]]</Select></Query></QueryList> 34 | PT1M 35 | 36 | 37 | true 38 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5077]]</Select></Query></QueryList> 39 | PT1M 40 | 41 | 42 | true 43 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5078]]</Select></Query></QueryList> 44 | PT1M 45 | 46 | 47 | true 48 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5079]]</Select></Query></QueryList> 49 | PT1M 50 | 51 | 52 | true 53 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5080]]</Select></Query></QueryList> 54 | 55 | 56 | true 57 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5081]]</Select></Query></QueryList> 58 | PT1M 59 | 60 | 61 | true 62 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5117]]</Select></Query></QueryList> 63 | PT1M 64 | 65 | 66 | true 67 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WAS'] and EventID=5186]]</Select></Query></QueryList> 68 | PT1M 69 | 70 | 71 | true 72 | PT5M 73 | 74 | 75 | 76 | 77 | 78 | Password 79 | HighestAvailable 80 | 81 | 82 | 83 | IgnoreNew 84 | true 85 | true 86 | true 87 | false 88 | false 89 | 90 | true 91 | false 92 | 93 | true 94 | true 95 | false 96 | false 97 | false 98 | P3D 99 | 7 100 | 101 | 102 | 103 | PowerShell.exe 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /doc/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spjeff/spbestwarmup/ef6f4a13f6fbe1794e8ef9bfc2ec08691767ce21/doc/1.jpg -------------------------------------------------------------------------------- /doc/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spjeff/spbestwarmup/ef6f4a13f6fbe1794e8ef9bfc2ec08691767ce21/doc/2.jpg -------------------------------------------------------------------------------- /doc/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spjeff/spbestwarmup/ef6f4a13f6fbe1794e8ef9bfc2ec08691767ce21/doc/3.jpg -------------------------------------------------------------------------------- /doc/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spjeff/spbestwarmup/ef6f4a13f6fbe1794e8ef9bfc2ec08691767ce21/doc/download.png --------------------------------------------------------------------------------