Using dbatools for automated restore and CHECKDB

OK, so if you haven’t heard of the dbatools.io project run by Chrissy LeMaire and company…you’ve likely been living under a rock. I strongly encourage you to check it out ASAP. What they’re doing will make your life as a DBA easier…immediately. Here’s an example…

One of the things I like to do as a DBA is backup my databases, restore them to another server and run CHECKDB on them. There are some cmdlets in the dbatools project, in particular the Snowball release, that really make this easy. In this post I’m going to outline a quick solution I had to throw together this week to help me achieve this goal. We’ve all likely written code to do this using any number of technologies and techniques…wait until you see how easy it is using the dbatools project.

Requirements

  1. Automation Complete autopilot, no human interaction.
  2. Report job status Accurate reporting in the event the job failed, the CHECKDB failed or the restore failed.

Solution

  1. Use dbatools cmdlets for restore and CHECKDB operations
  2. Use SQL Agent Job automation, logging and alerting

So let’s walk through this implementation together.

Up first, here’s the PowerShell script used to restore and CHECKDB the database. Save this code into a file named restore_databses.ps1

$ErrorActionPreference = "Stop"

$dataDir = "D:\DATA"
$logDir = "L:\LOGS"

$backupPath = "\\server1\SQLBackups\TESTDB1"
Restore-DbaDatabase -SqlServer localhost -Path $backupPath -DestinationDataDirectory $dataDir -DestinationLogDirectory $logDir -WithReplace -IgnoreLogBackup
Invoke-Sqlcmd2 -ServerInstance localhost -Query "ALTER DATABASE [TESTDB1]  SET RECOVERY SIMPLE;"
Invoke-Sqlcmd2 -ServerInstance localhost -Query "USE [TESTDB1]; DBCC SHRINKFILE (N'testdb1_log' , 100);"

Invoke-Sqlcmd2 -ServerInstance localhost -Query “DBCC CHECKDB([TESTDB1]) WITH NO_INFOMSGS, ALL_ERRORMSGS" -QueryTimeout 0

Let’s walk through what’s going on here. First the line with $ErrorActionPreference = “Stop” that’s crucial because it will tell our script to stop when it encounters and error. Yes, that’s what I want. The job stops and the error from the cmdlets will reach the SQL Agent job we have driving the process. Using this, the job will fail, and I’ll have a nice log telling me exactly what happened.

Next we have some variables set, including the backup path and the location of the data and log files on the destination system.

Now, here’s the Restore-DbaDatabase cmdlet from the dbatools project, this cmdlet will traverse the backup path defined in -Path parameter, find all the backups and build the restore sequence for you. Yes…really! If you don’t define a parameter defining a point in time it will build a restore sequence using the most recent backups available in the share. The next few parameters define the destination data and log directories and tell the restore to overwrite the database if the database exists on the destination server. That next parameter tells the job to ignore using log backups. This is sufficient in my implementation because I’m running full backups daily, I don’t need the point in time recovery. You might, so give it a try. CHECKDB can take a long time…the final parameter, tells Invoke-SqlCmd2 not to timeout while running its query.

Now, I need to run some T-SQL to clean up the databases, for example, I change the recovery model, then shrink the log. This is so I don’t have a bunch of production sized log files laying around on the destination system I do this after each restore, this way I can save a little space. And finally, I run CHECKDB against the database.

If you want to do this for more than one database, you could easily parameterize this code and drive the process with a loop. You’re creative…give it a try.

Now, I take all this and wrap it up in a SQL Agent job.

SQL Agent Job Step

 Figure 1: SQL Agent Job Step Definition

Using a SQL Agent job, we get automation, reporting and alerting. I’ll know average run times, if the job fails and have a log of why and it sends me an email with the job’s results.

The SQL Agent job type is set to Operating system (CmdExec), rather than PowerShell. We run the job this way because we want to use the latest version of PowerShell installed on our system. In this case its version 5.1. The SQL Agent PowerShell job step on SQL 2012 I believe uses version 4 and when I used it, it wasn’t able to load the dbatools modules.

We need to ensure we install the dbatools as administrator. This way the module is available to everyone on the system, including the SQL Agent user, not just the user installing the module. Simply run a PowerShell session as administrator and use Install-Module dbatools. If you need more assistance check out this for help.

From a testing standpoint I confirmed the following things…

  1. When a restore fails, it’s logged to the SQL Agent job’s log, I get an alert.
  2. When one of the Invoke-SqlCmd2 calls fails, it’s logged to the SQL Agent job’s log and I get an alert.
  3. When CHECKDB finds a corruption in a database, it’s logged to the SQL Agent job’s log, the SQL Server Error Log and I get an alert. For testing this I used Paul Randal’s corrupt databases which he has available here.

So in this post, we discussed a solution to common DBA problem, backup, restore and CHECKDB a set of databases. Using dbatools, you can do this with a very simple solution like I described here. I like simple. Simple is easier to maintain. Certainly there are some features I want to add to this. Specifically, I’d like to write some more verbose information into the SQL Agent job’s log or use the job step’s ability to log to a file. Using those logs I can easily review the exact runtimes of each restore and CHECKDB.

Give dbatools a try. You won’t be disappointed…really go there now!