Posey's Tips & Tricks
Using PowerShell to Restore Backup Data
Use this simple code to apply your air gapped backups to your system.
A while back, I developed a PowerShell script that I use for creating air gapped backups of my file data. Even though I put a lot of effort into building this script and testing it over and over again to make sure that it worked perfectly, I had never bothered to build a companion script that could be used for restoring data. There had just never been a need since I use my air gapped backups as a secondary backup mechanism.
Recently though, I found myself in a situation in which I ran low on storage space within a data volume and in order to extend that volume, I was forced to reformat it. My easiest option for restoring all of the data was to do a direct copy from my air gapped backups to the newly formatted data volume. While I could have used File Explorer for the copy process, I decided instead to build a PowerShell script that works similarly to the one that I use to back up my data.
Before I show you how my restoration script works, let me show you an excerpt from my backup script. My backup script is designed to back up multiple volumes. However, for the sake of simplicity, I have modified the code below so that it only backs up one volume. Here is the code:
net use q: \\192.168.0.1\h
# Switch to the backup drive
E:
#Backup Q:
$H=Mount-VHD h.vhdx -passthru | Get-Disk | Get-Partition | Get-Volume
$HLetter=$H.DriveLetter + ':'
Robocopy Q: $HLetter /e /xo /purge
Dismount-VHD H.vhdx
# Switch to the C: Drive
C:
The modified script shown above is really simple. The first line of code maps the Q: drive letter to a network share. This share is tied to the network volume that I am backing up. The next line of code causes PowerShell to switch to the E: drive. E: is the drive letter that has been assigned to my backup drive.
From there, the script mounts a virtual hard disk file that is stored on the backup drive. In this case, the drive letter H: is being assigned to the mounted virtual hard disk.
The Robocopy command is the command that actually performs the file backup. In this case, data is being copied from the Q: drive, which is my network drive. The data is being copied to my backup drive. That data is written to the drive letter corresponding to the virtual hard disk that I mounted. Once again, that drive letter is H:.
I am using three command line parameters in conjunction with Robocopy. The first of these parameters is /e, which indicates that I want to copy all subdirectories, including the empty ones. The second parameter is /xo, which tells Robocopy to that if it encounters any files that are older than what has already been backed up, those should be skipped. Finally, the /purge switch tells Robocopy that if a file exists on the backup, but not on the source, then the file should be removed from the backup. The reason why I included this particular option is that if I move a file into an archive folder then I don’t want my backup drive to continue to maintain a copy of the file in its original location. I want the backup to reflect the fact that the file has been moved.
So now that I have shown you how my backup script works, let’s take a look at the restore script that I created.
net use q: \\192.168.0.1\h
# Switch to the backup drive
E:
#Perform a Full Restore of Q:
$H=Mount-VHD h.vhdx -passthru | Get-Disk | Get-Partition | Get-Volume
$HLetter=$H.DriveLetter + ':'
Robocopy $HLetter Q: /e
Dismount-VHD H.vhdx
# Switch to the C: Drive
C:
As you can see, the restoration script is extremely similar to the backup script. The only real differences exist within the Robocopy command. First, rather than copying data from Q: to H:, I am now copying data from H: to Q:.
The second difference is that the only Robocopy parameter that I am using is /e. As you will recall, the /e parameter tells Robocopy to copy all subdirectories, including the empty ones. The reason why I omitted the /xo and /purge parameters is because I intended this script to be used in a situation in which the Q: drive is completely empty. As such, there were no existing files to check the age of or to purge. It probably would not have hurt anything to leave those parameters in place, but I decided to omit them purely for the sake of simplicity.
About the Author
Brien Posey is a 22-time Microsoft MVP with decades of IT experience. As a freelance writer, Posey has written thousands of articles and contributed to several dozen books on a wide variety of IT topics. Prior to going freelance, Posey was a CIO for a national chain of hospitals and health care facilities. He has also served as a network administrator for some of the country's largest insurance companies and for the Department of Defense at Fort Knox. In addition to his continued work in IT, Posey has spent the last several years actively training as a commercial scientist-astronaut candidate in preparation to fly on a mission to study polar mesospheric clouds from space. You can follow his spaceflight training on his Web site.