There are many reasons you might want to warm-up a web application occasionally. It can be after a fresh deployment or on a regular basis after recycling application pools. Some times you might also need to warm-up SOAP services without going through front-end.
It might seems to be any easy task specially if you have PowerShell 3.0 or higher on your servers, but after Googling a while and reviewing some of the top hits I discovered that each solution is missing a part. Some only work in a single server scenario and some has forgotten that each HTTP response might contain links to scripts and images that we need to download and finally I could not find anything for SOAP services that just works. Long story short I decided to put together a simple script that just works and is easy to change to everyone’s needs.
Please note that my only assumption is you have PowerShell 3.0+ in your servers.
Currently the script takes care of the following tasks, but I will most likely improve it to cover other useful scenarios.
- Calling SOAP operations and sending parameters and custom headers
- Calling front-end URIs and downloading scripts and images that are local to the front-end
- Logging to a configurable folder and file name
- Cleaning up old log files
Currently, I have the following points in mind to improve the script.
- Put configuration in a different file.
- Improve function definitions (named and typed parameters and description).
- Default values for parameters when it makes sense (e.g. log folder can be the current folder).
- Support REST services.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Parameters | |
$logFolder = "C:\Temp\logs" | |
$logFile = "$logFolder\Warmup_$(Get-Date –Format "MM-dd-yyyy_hh-mm-ss").log" | |
$timeoutSec = 10 | |
$headers = @{ | |
SOAPAction= ""; | |
"Accept-Encoding" = "gzip,deflate"; | |
"User-Agent" = "PowerShell/Warmup-WebApp" | |
} | |
$webRequests = @( | |
@{ | |
SOAPAction = "http://tempuri.org/IService1/Operation1"; | |
Body = @" | |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"> | |
<soapenv:Body> | |
<tem:Operation1> | |
<tem:Parameter1>1</tem:Parameter1> | |
<tem:Parameter2>2</tem:Parameter2> | |
</tem:Operation1> | |
</soapenv:Body> | |
</soapenv:Envelope> | |
"@ | |
}, | |
@{ | |
SOAPAction = "http://tempuri.org/IService1/Operation2"; | |
Body = @" | |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"> | |
<soapenv:Body> | |
<tem:Operation1> | |
<tem:Parameter1>1</tem:Parameter1> | |
<tem:Parameter2>2</tem:Parameter2> | |
</tem:Operation1> | |
</soapenv:Body> | |
</soapenv:Envelope> | |
"@ | |
} | |
) | |
# Functions | |
function Write-Log([string]$level, [string]$text) { | |
$logTime = Get-Date –Format "yyyy-MM-dd HH:mm:ss.fff" | |
Write-Host "[$level] $text" | |
if ($logFile -ne $null) { | |
"$logTime [$level] $text" | Out-File –FilePath $Logfile –Append | |
} | |
} | |
function Cleanup-LogFiles() { | |
$expiryDate = (Get-Date).AddMinutes(-7) | |
[bool]$logFileDeleted = $false | |
$filesToDelete = Get-Childitem $logFolder –Include "*.log" –Recurse | ? {$_.LastWriteTime -le $expiryDate} | % { | |
Remove-Item $_.FullName | out-null | |
Write-Log "Information" "File deleted successfully – $_" | |
$logFileDeleted = $true | |
} | |
if (-not $logFileDeleted) { | |
Write-Log "Information" "No old log file was found in $logFolder" | |
} | |
} | |
function Warmup-WcfService($serviceUrl) { | |
foreach($webRequest in $webRequests) { | |
$headers["SOAPAction"] = $webRequest.SOAPAction | |
Invoke-WcfService $serviceUrl $headers $webRequest.Body | |
} | |
} | |
function Invoke-WcfService($uri, $headers, $body) { | |
$invocationStartTime = Get-Date | |
try { | |
$result = Invoke-WebRequest –Uri $uri –Method Post –UseDefaultCredentials –ContentType "text/xml; charset=utf-8" –Headers $headers –body $body –TimeoutSec $timeoutSec | |
$invocationDuration = (Get-Date).Subtract($invocationStartTime) | |
if ($result.StatusCode -eq 200) { | |
if ($result.Headers.ContainsKey("Content-Length")) { | |
$contentLength = $result.Headers["Content-Length"] | |
} else { | |
$contentLength = "$($result.RawContentLength) (raw)" | |
} | |
Write-Log "Information" "WCF operation invoked successfully – Operation: $($webRequest.SOAPAction)($variation), Duration: $($invocationDuration), Length: $contentLength" | |
} else { | |
Write-Log "Warning" "WCF operation failed – Operation: $($webRequest.SOAPAction)($variation), Status: $($result.StatusCode) $($result.StatusDescription), Length: $contentLength" | |
} | |
} catch { | |
Write-Log "Critical" "WCF operation failed – Operation: $($webRequest.SOAPAction)($variation), Exception: $_" | |
} | |
} | |
function Invoke-WebPage([string]$uri, [bool]$getImages, [bool]$getScripts) { | |
try { | |
$response = Invoke-WebRequest –Uri $uri –UseDefaultCredentials –TimeoutSec $timeoutSec –Proxy "http://localhost" –ProxyUseDefaultCredentials | |
if ($response.StatusCode -eq 200) { | |
Write-Log "Information" "Web page invoked successfully – URL: $uri, Images: $($response.Images.Count), Scripts: $($response.Scripts.Count), Links: $($response.Links.Count)" | |
} else { | |
Write-Log "Warning" "Web page invocation failed – URL: $uri, Status: $($response.StatusCode) $($response.StatusDescription)" | |
} | |
} catch { | |
Write-Log "Critical" "Web page invocation failed – URL: $uri, Exception: $_" | |
} | |
if ($getImages) { | |
Fetch–Resources $uri ($response.Images | ? { $_.src } | % { $_.src }) | |
} | |
if ($getScripts) { | |
Fetch–Resources $uri ($response.Scripts | ? { $_.src } | % { $_.src }) | |
} | |
} | |
Function Fetch-Resources($baseUrl, $resources) { | |
[uri]$uri = $baseUrl | |
$rootUrl = $uri.Scheme + "://" + $uri.Authority | |
$counter = 0 | |
foreach ($resUrl in $resources) { | |
if ($resUrl.StartsWith("http", [System.StringComparison]::OrdinalIgnoreCase)) { | |
$fetchUrl = $resUrl | |
} elseif (!$resUrl.StartsWith("/")) { | |
$fetchUrl = $rootUrl + "/" + $resUrl | |
} else { | |
$fetchUrl = $rootUrl + $resUrl | |
} | |
# Progress | |
Write-Progress –Activity "Opening " –Status $fetchUrl –PercentComplete (($counter/$resources.Length)*100) | |
$counter++ | |
# Execute | |
try { | |
$resp = Invoke-WebRequest –UseDefaultCredentials –UseBasicParsing –Uri $fetchUrl –TimeoutSec 120 | |
Write-Log "Information" "Resource has been downloaded successfully – URL: $fetchUrl" | |
} catch { | |
Write-Log "Warning" "Resource failed to download – URL: $fetchUrl, Exception: $_" | |
} | |
} | |
Write-Progress –Activity "Completed" –Completed | |
} | |
# Execution | |
Write-Log "Information" "My Web Application Warmup Script – $(Get-Date)" | |
Write-Log "Information" "Cleaning up old log files…" | |
Cleanup–LogFiles | |
Write-Log "Information" "Warming up WCF Services…" | |
Warmup–WcfServices | |
Write-Log "Information" "Warming up Front-End…" | |
Invoke-WebPage "http://domain.com/area1/Page1.aspx" $true $true | |
Invoke-WebPage "http://domain.com/area1/Page2.aspx" $true $true | |
Write-Log "Information" "My Web Application Warmup Script Completed." |
I’m open to any suggestion and feature request. Please let me know if you found it useful or if have got something wrong.
Leave a Reply