Wednesday, 25 July 2012

Ghetto PowerShell fix via admin shares and an infinite loop

At the office, most of our work is done with one major government agency. To work with them, we have to use their in-house database system. It's slow, it's troublesome, and it tends to be down for 'unplanned maintenance' quite regularly.

The most annoying feature is that they will silently push out updates to the client software about once a week. It downloads the new version in the background, and then removes the old version when it's next launched. Lately this has been going wrong: about a third of the time, on any computer running windows 7 64-bit, it deletes the old version before it downloads the new version properly. This will continue on every computer multiple times a day until the next update is pushed about a week later, whereupon it will all play nice again.

There are two solutions to this:
  • Reinstall the software daily (it will correctly download the older version once, and then fail when it later tries to upgrade)
  • Manually copy over the files to the correct directory on each computer.

Option 2 is the much faster option, and so we have

The initial fix:

param([string]$targetName = "8200-01")
robocopy "C:\fixess\data" "\\$targetname\c$\Program Files (x86)\Common Files\Government\Program\201207030908" /MIR /E /V /R:2 /W:2 /fp /eta  /A-:SH
exit


When a computer wasn't working, I called that script (fixcomputer.ps1 computernamehere), it'd copy the files over to that computer with the magic of admin shares, and everybody was happy....until I wanted to take a few days off and had to come up with a slightly more robust option.


The updated fix (click here for a prettier version)

$computerlist="computer01","computer02","computer03","computer04","computer05","computer06","computer07","computer08","computer09","computer10","computer11","computer12","computer13","computer14","computer15" #list of computer names that need fixing
$sourcedir="C:\fixess\data"
$targetdir="c$\Program Files (x86)\Common Files\Government\Program\201207030908"
$count=0 #counting the number of loops for no useful reason
$wait=60 #how many seconds to wait between runs

Write-host "$(Get-Date) Script started"

While($true)
{
  foreach ($computer in $computerlist)
  {
  #if the computer is on and the directory doesn't exist, 

      #silently copy the files over
   if ((test-path "\\$computer\c$") -and -not (test-path "\\$computer\$targetdir"))
   {
      robocopy "$sourcedir" "\\$computer\$targetdir" /MIR /E /R:0 /W:1 /NJH /NJS /NDL /NC /NS /NP /NFL /A-:SH
      Write-host "$(Get-Date) Fixed: $computer on pass $count"
   }
  }
  $count++

  #Every 20 loops, post an update just so I know it's still running
  if ($count%20 -eq 0) { Write-host "$(Get-Date) Don't worry, the script is still running" }

  #Have a break before running the script again.
  Start-Sleep -s $wait

}



How it works:
  • We start an infinite while loop to wrap everything in
  • Cycle through each computer in the list provided at the start
  • Check whether they're accessible and the files have been deleted, and copy over the data if so
  • Every 20 loops, post a reassuring message just so I know the script hasn't died
  • Finally, wait a little while. There's no sense running this script constantly, but I've never been a big fan of task scheduler as an alternative to cron.
What it looks like:



In summary:
Let's be honest, this is a complete hack job. It's an ugly fix to get around a bug in one unpleasant piece of software. It's slow, inflexible, and wouldn't scale up to hundreds of computers without some major changes. 

Despite that it works, keeps users happy, and lets me take time off despite the best efforts of the government. I'm calling it a win.

Update:
And here's what a slightly-modified version looks like after two weeks chugging away on its own.