How to loop in powershell until successful?
Asked Answered
G

3

5
%WINDIR%\system32\inetsrv\appcmd.exe set site /site.name:"WebRole_IN_0_CB" /[Path='/'].applicationPool:"ASP.NET v4.0" >>CBLog.log
powershell.exe -command Set-ExecutionPolicy Unrestricted
powershell.exe .\CheckIfSuccessful.ps1

I would like to run the script above and if appcmd.exe can't execute it due the WebSite not yet being up and ready I would get the following error message in the CBLog.log file.

ERROR ( message:Cannot find SITE object with identifier "WebRole_IN_0_CB". )

In the CheckIfSuccessful.ps1 I would like to create a Powershell script to loop, which runs the appcmd command and checks the error again. Then sleeps for 5 seconds and then retries, until it succeeds.

How would I do this in Powershell?

highly appreciated,

UPDATE:

Ok, many thanks for the tip with $lastexitcode, while it looks promising. It seems I have problems with converting single quoates in Powershell: (The below is part of my initial appcmd command)

/[Path='/'].applicationPool:"ASP.NET v4.0"

How do I quote this in powershell? I tried to simplify it so that powershell has less trouble with it, but it seems my command is now wrong, as it says now:

ERROR ( message:Cannot find SITE object with identifier "v4.0". )

 & $Env:WinDir\system32\inetsrv\appcmd.exe set site '/site.name:"WebRole_IN_0_CB"' "/[Path='/'].applicationPool:`"ASP.NET v4.0`"" >CBLog.log
if($lastexitcode -ne '0')
{
    while($lastexitcode -ne '0')
    {
        Start-Sleep -s 5
        & $Env:WinDir\system32\inetsrv\appcmd.exe set site '/site.name:"WebRole_IN_0_CB"' "/[Path='/'].applicationPool:`"ASP.NET v4.0`"" >CBLog.log
    }
}

UPDATE II: I have escaped it properly. But still I get the error message. If I run the appcmd alone after creating that site, the log file is ok with it. Any suggestions please?

Grume answered 30/11, 2011 at 21:53 Comment(3)
Ok the escaping works now, but I still get the error message, any idea what this could be? The script loops forever. Even if I create a Site with that name. The logfile keeps repeating that initial errorGrume
For some reason PowerShell didn't like the space. I ended up creating my own AppPool and assign it to the Site.Grume
any full source code sample with final solution?Raiment
C
5

The special variable $lastexitcode gives you the exit code of the last native executable (i.e. not cmdlet) that was run. This will have different values depending on the results of the operation. For example, it will be 5 if appcmd encounters access denied. It should be 0 if the last command completed successfuolly.

Make sure you check this variable immediately after you run appcmd and in the same script. If not, you risk getting the exitcode from powershell.exe and not from appcmd.exe

Contact answered 1/12, 2011 at 1:21 Comment(1)
Sorry that still doesnt work. Powershell doesnt like to execute the command properly without any error message while the same command on appcmd runs fine, any ideas?Grume
T
3

I realize that the original issue has likely longs been resolved, but for the sake of Googlers hitting this question, I wrote a general purpose (advanced) function for retrying shell (cmd) commands. In addition to $LASTEXITCODE it can also parse the error stream (with some tools, the exit code can be a lie).

function Call-CommandWithRetries
{
 [CmdletBinding()]
 param( 
     [Parameter(Mandatory=$True)]
     [string]$Command,
     [Array]$Arguments,
     [bool]$TrustExitCode = $True,
     [int]$RetrySleepSeconds = 10,
     [int]$MaxAttempts = 10,
     [bool]$PrintCommand = $True
 )

 Process
 {
  $attempt = 0
  while ($true)
  {   
   Write-Host $(if ($PrintCommand) {"Executing: $Command $Arguments"} else {"Executing command..."}) 
   & $Command $Arguments 2>&1 | tee -Variable output | Write-Host

   $stderr = $output | where { $_ -is [System.Management.Automation.ErrorRecord] }
   if ( ($LASTEXITCODE -eq 0) -and ($TrustExitCode -or !($stderr)) )
   {
    Write-Host "Command executed successfully"
    return $output
   }

   Write-Host "Command failed with exit code ($LASTEXITCODE) and stderr: $stderr" -ForegroundColor Yellow
   if ($attempt -eq $MaxAttempts)
   {
    $ex = new-object System.Management.Automation.CmdletInvocationException "All retry attempts exhausted"
    $category = [System.Management.Automation.ErrorCategory]::LimitsExceeded
    $errRecord = new-object System.Management.Automation.ErrorRecord $ex, "CommandFailed", $category, $Command
    $psCmdlet.WriteError($errRecord)
    return $output
   }

   $attempt++;
   Write-Host "Retrying test execution [#$attempt/$MaxAttempts] in $RetrySleepSeconds seconds..."
   Start-Sleep -s $RetrySleepSeconds
  }
 }
}

For more information (including full documentation) see https://www.ohadsoft.com/2016/04/invoking-arbitrary-shell-cmd-commands-in-powershell.

In your case you would do something like

$appCmd = [io.path]::combine($env:WINDIR, 'system32', 'inetsrv', 'appcmd.exe')
$appCmdArg = @('set', 'site', '/site.name:"WebRole_IN_0_CB"', '/[Path=''/''].applicationPool:"ASP.NET v4.0"')
$output = Call-CommandWithRetries $appCmd $appCmdArgs
# write $output to log etc
Thromboembolism answered 18/4, 2016 at 16:7 Comment(0)
W
0

Simple version:

while ($true) { YOUR-COMMAND; if ($lastexitcode -eq 0) { break; } ; sleep 1 ; echo retry }

I'd note $lastexitcode is only available for applications. Powershell commands such as ls (get-childitem) do not set it, so you'd have to get the result from their output instead.

Whicker answered 17/7 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.