02 June, 2013

Edit scheduled task remotely - OS

The other day, I had a couple of 100 servers where I needed to edit a particular scheduled task. Fortunately or unfortunately all of them were Windows 2008 servers. Why am I saying that?
On Windows 2003, scheduled tasks are simple, there's one command or action, start time, run as credentials and some little other bits. So if you want to "edit" a task, you can just delete and recreate it with whatever change you wanted to make.
Unfortunately, on Windows 2008, it's not that simple. There are multiple triggers you can define, multiple actions a task can run...etc.:

Scheduled Task on Windows 2008

However, fortunately, you can export a scheduled task into an xml file, which is quite cool as the xml format is easy to handle in PowerShell.
You can just edit the xml and import it back (basically recreate the whole task with all the settings defined in the xml).

Easy to get xml representation of a Scheduled task in PowerShell

A couple of key things in the script below. Connect to the remote machine and get task details:
$schedSvc = New-object -ComObject Schedule.Service
$schedSvc.connect($srv
$schFolder = $schedSvc.GetFolder("\"
$schFolder = $schFolder.GetFolder($taskfolder
$schedTask = $schFolder.GetTask($taskName
$schedTask.xml | Out-File "$env:TEMP\$srv-$taskname.xml" -Force

Edit the xml and create a new file:
gc "$env:TEMP\$srv-$taskname.xml" | %{
....
....
} | Out-File "$env:TEMP\$srv-$taskname-new.xml" -Force 


Recreate the scheduled task remotely (for this I use schtasks.exe for the sake of simplicity, although the COM object mentioned above could be used as well):
$res = schtasks.exe /create /S $srv /RU SYSTEM /TN "$taskfolder\$taskName" /XML "$env:TEMP\$srv-$taskname-new.xml" /F

The script has Object type output which looks like this:
ComputerName oldValue Result
------------ -------- ------
R2D2              120 OK
JARJAR            120 OK
C3PO             3456 OK

The script (without log and error handling):
   
        
        
 param(     [string] $hosts = "",  
           [string] $taskName = "",  
           [string] $taskFolder = "",  
           [string] $newvalue = "")  
   
 #### Collate the host list.  
 $hostlist = @($Input)  
 if ($hosts) {  
      if($hosts -imatch " "){  
           $hostsArr = @($hosts.split(" "))  
           $hostlist += $hostsArr  
      }  
      else{  
           $hostlist += $hosts  
      }  
 }  
   
 foreach($srv in $hostlist){  
      $script:sObject = "" | select ComputerName,oldValue,Result  
      $script:sObject.ComputerName = $srv  
        
      # connect to remote host and export the scheduled task to an xml  
      $schedSvc = New-object -ComObject Schedule.Service  
      $schedSvc.connect($srv)  
      $schFolder = $schedSvc.GetFolder("\")  
      $schFolder = $schFolder.GetFolder($taskfolder)  
      $schedTask = $schFolder.GetTask($taskName)  
      $schedTask.xml | Out-File "$env:TEMP\$srv-$taskname.xml" -Force  
        
      # edit the given text in the xml  
      if(Test-Path "$env:TEMP\$srv_$taskname.xml"){  
           gc "$env:TEMP\$srv-$taskname.xml" | %{  
             
                if($_ -imatch "\\myapplication.exe\."){  
                     $script:sObject.oldValue = ([regex]::Match($_, "-parameter=\d")).Value -ireplace "-parameter=",""  
   
                     $newline = $_ -ireplace "parameter=\d+", "parameter=$newvalue"  
                       
                }  
                else{  
                     $newline = $_  
                }  
                $newline  
           } | Out-File "$env:TEMP\$srv-$taskname-new.xml" -Force  
             
             
           # import the new task xml  
           $res = schtasks.exe /create /S $srv /RU SYSTEM /TN "$taskfolder\$taskName" /XML "$env:TEMP\$srv-$taskname-new.xml" /F  
             
           if($res -imatch "Success"){  
                $script:sObject.Result = "OK"  
           }  
           else{  
                $script:sObject.Result = "Could not import task $taskname"  
           }  
      }  
      else{  
           $script:sObject.Result = "Could not export task $taskname"  
      }  
      $objColl += $script:sObject  
 }  
   
 $objColl  
   


t


2 comments:

  1. Powershell and XML, this is really cool. Just curious, what does schtasks.exe use to connect to the remote host, RPC? Thanks!

    ReplyDelete
  2. Yep, RPC, an it's unfortunately the quickest way to handle sched tasks. All the rest is slow and ugly, be that COM+ from PS, WMI..etc.

    ReplyDelete