Creating scheduled PST backups using Python and VSS
Background:
A client of ours (an individual this time — let’s call him Bob) has two PC’s: primary & secondary. His Outlook collects mail from a POP3 server, and deletes mail off the server once it’s done. Bob wants regular (hourly) email backups, so that he still has all his email in the event of a failure on the primary PC. Unfortunately, he is strongly opposed to using cloud-based email (eg Gmail with IMAP, or Hosted Exchange, either of which would have otherwise solved the problem).
We decided upon setting a scheduled task to copy Bob’s Outlook.pst file over the network from primary to secondary PC every hour.
Difficulties:
Bob wanted the .pst file to be copied every hour, regardless of whether Outlook is open (and therefore using the file) or not, which means that we needed to use Microsoft’s Volume Shadow Copy Service (VSS) to make a shadow copy of the original. This led to further difficulties:
- VSS is awfully difficult to control
- Creating a VSS image requires administrative rights
- Even with UAC disabled, and “run with highest privileges”, and running as a local administrator, scheduled tasks in Windows 7 are not able to be automatically run with administrative rights.
Solutions:
First of all, I created a mapped drive (T:) to the secondary PC so that the backup could be directed to a specific location. I then wrote a python script which works like so:
- Python calls VSS to create a shadow copy
- Python finds the index for that shadow copy, and finds out the path to access it
- Python copies the file from VSS copy to T: drive
This works fine when run manually from the command line, but won’t run at all from Task Scheduler, due to the part in bold above. The solution was to run this as the first part of the Python script, which I found here:
if sys.argv[-1] != ASADMIN: print "Don't have privileges, trying to get them" script = os.path.abspath(sys.argv[0]) params = ''.join([script] + sys.argv[1:] + [ASADMIN]) shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params) else: print "Got privileges, running" main()
The above snippet literally does this: “If I’m not running as admin, run myself as admin.” The downside of this workaround is that it’s virtually impossible to debug, because the STDOUT data from the child process is not sent to the terminal window of the main process — so you can’t SEE ANYTHING.
Now the backups work as planned and Bob can rest easy that his email is safe and secure.