Using invoke-command with a timeout

Welcome Forums General PowerShell Q&A Using invoke-command with a timeout

Viewing 3 reply threads
  • Author
    Posts
    • #74662
      Participant
      Topics: 22
      Replies: 15
      Points: 17
      Rank: Member

      I use invoke-command to execute a scriptblock on a remote host.
      My code looks like:

      $my_remote_command = invoke-Command -Session $my_session -ScriptBlock {
      #
      Get-ChildItem c:\ *.* -Recurse | select name > c:\myfolder\myfile.txt
      #
      } 
      

      I need to stop the script block after 30 second, even if myfile.txt is not yet complete.
      How can I set a timeout?
      Regards
      marius

    • #74666
      Participant
      Topics: 2
      Replies: 234
      Points: 189
      Rank: Participant

      You could kick it as a job.

      $Job = Start-Job -ScriptBlock {
      
          Invoke-Command -ComputerName localhost -ScriptBlock {
          #
              Get-ChildItem c:\scripts\*.* -Recurse | select name | Out-File 'C:\Scripts\myfile.txt'
          #
          } 
      
      
      }
      
      $Job | Wait-Job -Timeout 10
      $Job | Stop-Job
      
      • #77980
        js
        Participant
        Topics: 27
        Replies: 739
        Points: 2,014
        Helping Hand
        Rank: Community Hero

        Is there a way to make resolve-dnsname timeout in 1 second? stop-job (or remove-job -force) takes about 4 seconds.

        $job = start-job -scriptblock { Resolve-DnsName 1.1.1.1 }
        $job | wait-job -timeout 1
        measure-command { $job | stop-job }
        
        Days              : 0
        Hours             : 0
        Minutes           : 0
        Seconds           : 4
        Milliseconds      : 148
        Ticks             : 41484183
        TotalDays         : 4.80141006944444E-05
        TotalHours        : 0.00115233841666667
        TotalMinutes      : 0.069140305
        TotalSeconds      : 4.1484183
        TotalMilliseconds : 4148.4183
        
    • #77988
      Participant
      Topics: 6
      Replies: 658
      Points: 47
      Rank: Member

      You can try using the -quicktimeout Parameter of the Resolve-DNSName cmdlet. It does not let you specify a timeout value, but it's quicker than the default.

      • #78016
        js
        Participant
        Topics: 27
        Replies: 739
        Points: 2,014
        Helping Hand
        Rank: Community Hero

        -quicktimeout has no effect in this case. Just try any ip without a hostname.

        I think if the first post here isn't updated, it doesn't appear on the main page.

      • #78022
        js
        Participant
        Topics: 27
        Replies: 739
        Points: 2,014
        Helping Hand
        Rank: Community Hero

        Oh well. In this case, -dnsonly fixes it.

    • #104366
      Participant
      Topics: 0
      Replies: 1
      Points: 5
      Rank: Member

      OK so from what I can see above the solution is to run Invoke-Command within a job, downside is that it will time out the whole lot when the job is stopped.

      So in my example I had an actual problem where I wanted to retrieve information about services running on several hundred servers. I didn't want the Invoke-Command to halt after a specific time but, if there was a server that wasn't responding, it would timeout for that particular computer so that the batch wouldn't be waiting for it.

      In my case I had one duff server that was making the query take 30 mins to run, with the code below the whole lot runs in about 2.5 mins, less then a tenth of the time.

      Basically I've just put the start-job within the ICM.

      Hopefully someone may find this useful

      #Array of Servers
      $ComputerName = @('Server01','Server02','Server03')
      
      # service to check
      $svcName="sql*"
      
      $out=Invoke-Command -computername $ComputerName -ArgumentList $svcName{
          param($svcName)
      
          $jbID=Start-Job -ArgumentList $svcName{
              param($svcName)
      
              $objArray=@();
      
              get-service  -DisplayName "$svcName"| foreach-object{
                  
                  # get service startmode and time
                  $filter="Name='"+$_.ServiceName+"'"; # can't put filter variable into function filter!
                  $s=Get-WmiObject -Class Win32_Service -Filter $filter
                   
                  $ProcessInfo=Get-WmiObject -Class Win32_Process -Filter "ProcessID='$($s.processid)'" -ea 0
                  if($_.Status -eq 'Running'){
                      $lastStarted=$s.ConvertToDateTime($ProcessInfo.CreationDate)
                  }else{
                      $lastStarted='N/A'
                  }
                  
                  $ht = @{
                      DisplayName=$_.DisplayName;
                      ServiceName=$_.ServiceName;
                      Status=$_.Status;
                      ServiceType=$_.ServiceType;
                      StartMode=$s.startmode;    
                      lastStarted=$lastStarted;
                      LogOnAs=$s.StartName;
                  };
      
                  $Obj=New-Object -TypeName PSObject -Property $ht
      
      		    $objArray+=$Obj
      	    }
      
              $objArray;
          }
      
          for ($i2=1; $i2 -le 30; $i2++){
              if((Get-Job $jbID.id).State -notmatch 'Running'){
                  BREAK;
              }
              sleep -Seconds 1
          }
          $out=Get-Job $jbID.id | Receive-Job
          Stop-Job $jbID.id
          remove-Job $jbID.id
          Write-Output $out
      }
      
      $out|select pscomputername,DisplayName,lastStarted,ServiceName,LogOnAs,StartMode,ServiceType,Status |ft -a
      
    • #178482
      js
      Participant
      Topics: 27
      Replies: 739
      Points: 2,014
      Helping Hand
      Rank: Community Hero

      Just to add to that other thread https://powershell.org/forums/topic/using-invoke-command-with-a-timeout/, which is closed for some reason. Invoke-command has an -asjob parameter already. And with multiple computers, they get run as child jobs that you can check on.

      invoke-command comp1,comp2,comp3 { pwd } -asjob
      
      get-job -Includechildjob
      

      And you can see which ones aren't completed...

Viewing 3 reply threads
  • The topic ‘Using invoke-command with a timeout’ is closed to new replies.