user1574808
user1574808

Reputation: 737

How to ensure IIS website is completely stopped in Powershell?

I've got a Powershell script that stops an IIS website and corresponding app pool and then deletes the app logs (log4net logs). Here is the script snippet:

stop-website -name "MyWebsite"
stop-webapppool -name "MyWebsite"
del c:\inetpub\MyWebsite\logs\*.*

The problem is stop-website and stop-webapppool seem to return before the website is completely shutdown which results in the delete failing saying the file is being used by another process:

del : Cannot remove item C:\inetpub\MyWebsite\logs\App.log: The process cannot access the file 'C:\inetpub\MyWebsite\logs\App.log' because it is being used by another process.

If I add a 10 second sleep between the stop commands and the del command then the logs are deleted successfully. This is very hackish though and not reliable. Is there a way to force the stop-website/stop-webapppool commands to not return until the website/apppool is completely stopped?

Thanks.


Implemented solution from the below link. I will wait ~60 seconds and then kill the IIS process if it hasn't stopped.

https://greenfinch.ie/blog/powershellscript.html

        "Stopping IIS site [$name]" >> $logFile
        stop-website -name $name

        "Stopping app pool [$name]" >> $logFile
        stop-webapppool -name $name

        $sleepTime = 5
        $processId = $TRUE
        while ($processId)
        {
            $processId = Get-WmiObject -Class win32_process -filter "name='w3wp.exe'" |
                            ?{ ($_.CommandLine).Split("`"")[1] -eq $name } |
                            %{ $_.ProcessId }

            if ($sleepTime -gt 60)
            {
                "Waited [$sleepTime] sec for process [$processId] to stop and it is still running. Killing it." >> $logFile
                Stop-Process $processId
                break
            }

            if ($processId)
            {
                "App pool [$name] is running with process ID: [$processId]. Sleeping for [$sleepTime] sec and then checking again." >> $logFile
                Start-Sleep -s $sleepTime
                $sleepTime = $sleepTime + 10
            }
        }

Upvotes: 16

Views: 16272

Answers (5)

user4531
user4531

Reputation: 2565

After you run 'Stop-WebAppPool', the state of the WebAppPool will be "Stopping" and it may take a few seconds before the state of the WebAppPool is actually "Stopped".
Here is a little function to help with the WebAppPoolState:

function Stop-AppPool ($webAppPoolName, [int]$secs) {
    $retvalue = $false
    $wsec = (get-date).AddSeconds($secs)
    Stop-WebAppPool -Name $webAppPoolName
    Write-Output "$(Get-Date) waiting up to $secs seconds for the WebAppPool '$webAppPoolName' to stop"
    $poolNotStopped = $true
    while (((get-date) -lt $wsec) -and $poolNotStopped) {
        $pstate = Get-WebAppPoolState -Name $webAppPoolName
        if ($pstate.Value -eq "Stopped") {
            Write-Output "$(Get-Date): WebAppPool '$webAppPoolName' is stopped"
            $poolNotStopped = $false
            $retvalue = $true
        }
    }
    return $retvalue
}

You can run this function using e.g.

Stop-AppPool "MyWebsite" 30

and check the return-value to see if the WebAppPool has stopped within the given seconds.

Upvotes: 4

Kai Zhao
Kai Zhao

Reputation: 1015

You can use these two commands to check the status of the website/app, say after 10 seconds, then use an If statement to delete logs only when the status returned is stopped

Get-WebsiteState -name "MyWebsite"
Get-WebAppPoolState -name "MyWebsite"

This loop should help you too

$currentRetry = 0
$success = $false
do {
    $status = Get-WebAppPoolState -name "MyWebsite"
    if ($status -eq "Stopped") {
        <....your code here....>
        $success = $true
    }
    Start-Sleep -s 10
    $currentRetry = $currentRetry + 1
}
while (!$success -and $currentRetry -le 4)

Updated Apr 24, 2019

Based on comment and current cmdlet document, it appears the return type is indeed an object. Thus presumably can be handled as commented or the line snippet below. Author no longer have access to Windows Server environment therefore did not directly modify original answer nor able to test the update

if ($status.Value -eq "Stopped")

Upvotes: 21

CodeNotFound
CodeNotFound

Reputation: 23190

The simplest way to stop the app pool and get it into Stopped state is to use appcmd.exe. It will return when the app pool is really stopped or you'll get an error

Just do this on PowerShell:

& $env:windir\system32\inetsrv\appcmd.exe stop apppool /apppool.name:"YourAppPoolName"

When your AppPool is correctly stooped you'll get this message:

"YourAppPoolName" successfully stopped

Upvotes: 5

Masoud DaneshPour
Masoud DaneshPour

Reputation: 126

I fix the @user4531 code It would be failed if the app pool is stopped before :

function Stop-AppPool ($webAppPoolName,[int]$secs) {
    $retvalue = $false
    $wsec = (get-date).AddSeconds($secs)

    $pstate =  Get-WebAppPoolState -Name $webAppPoolName

    if($pstate.Value -eq "Stopped") {
         Write-Output "WebAppPool '$webAppPoolName' is stopped already"
         return $true
    }

    Stop-WebAppPool -Name $webAppPoolName
    Write-Output "$(Get-Date) waiting up to $secs seconds for the WebAppPool '$webAppPoolName' to stop"
    $poolNotStopped = $true
    while (((get-date) -lt $wsec) -and $poolNotStopped) {
        $pstate =  Get-WebAppPoolState -Name $webAppPoolName
        if ($pstate.Value -eq "Stopped") {
            Write-Output "WebAppPool '$webAppPoolName' is stopped"
            $poolNotStopped = $false
            $retvalue = $true
        }
    }
    return $retvalue
}

It can use like this :

Stop-AppPool "SSO" 30

Upvotes: 2

yohosuff
yohosuff

Reputation: 1459

Here is how I did it with Get-IISServerManager.

$manager = Get-IISServerManager      
$site = $manager.Sites["mySiteName"]

if($site.State -ne "Stopped") {
  $site.Stop()
}

while ($site.State -ne "Stopped") {
  "waiting 1 second for site to stop..."
  Start-Sleep -s 1
}

"site stopped"

Upvotes: 1

Related Questions