As a DBA I used more and more the dbachecks at customer place to validate environments. It became part of our DBA toolkit at the same place than its famous brother the dbatools.
My colleague Steven Naudet did a blog-post some weeks ago about it and today I will show you how is it possible to add your own checks if you cannot find the one you need in the quite long list available for the moment and which is continuously growing.

Before to create your own checks you can read this wiki which is dedicated to dbachecks development and available on the dbachecks main page. It will give you some advice and standard development guidelines.

To create my new checks I will first create a new file which will contain those checks, lets name it SelfChecks.Tests.ps1.
I would like to add two checks:

  • The first one will check the health of my Availability Groups which have to be synchronized and healthy.
    I will tag this check DBinAG.
  • The second one will check that all the user databases of my instance are part of an Availability Group with exception of some specifics ones like our dbi_tools database for example.
    I will tag this one DBHealthyInAG.

The PowerShell script will be the following:

$filename = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")

$DBinAG = "IF (SERVERPROPERTY ('IsHadrEnabled') = 1)
BEGIN
	SELECT  name, state_desc
	FROM    sys.databases
	WHERE    name    NOT IN
		(   SELECT  DISTINCT
					drcs.database_name              AS name
			FROM                master.sys.availability_groups                      AS ag
				LEFT OUTER JOIN master.sys.dm_hadr_availability_group_states        AS ags
							ON    ag.group_id       = ags.group_id
				INNER JOIN      master.sys.availability_replicas                    AS ar
							ON   ag.group_id        = ar.group_id
				INNER JOIN      master.sys.dm_hadr_availability_replica_states      AS ars
							ON     ar.replica_id    = ars.replica_id
							AND    ars.is_local     = 1
				INNER JOIN      master.sys.dm_hadr_database_replica_cluster_states  AS drcs
							ON     ars.replica_id   = drcs.replica_id
				LEFT OUTER JOIN master.sys.dm_hadr_database_replica_states          AS dbrs
							ON      drcs.replica_id         = dbrs.replica_id
							AND     drcs.group_database_id  = dbrs.group_database_id
			WHERE   ISNULL(ars.role, 3)                 = 1
			OR      ISNULL(drcs.is_database_joined, 0)  = 1
		)
	--AND     dbid  
	AND     name    NOT IN  ('master'
							,'tempdb'
							,'model'
							,'msdb'
							-- add below your exceptions
							,'dbi_tools'
							)
	AND     state_desc  != 'OFFLINE'
	ORDER BY
			name    ASC
END";

(Get-Instance).ForEach{

    Describe –Name "All databases should be in an AAG" -Tags DBinAG, $filename {
        Context –Name "All databases should be in an AAG on $psitem" {
            It "should have 0 database outide of an AAG" {
                Invoke-DbaQuery -SqlInstance $psitem -Query $DBinAG |  
                Measure-Object |
                    Select-Object –ExpandProperty Count |
                    Should -Be 0 –Because "All databases should be in an AAG"
            }
        }
    }
}

$DBHealthyInAG = "IF (SERVERPROPERTY ('IsHadrEnabled') = 1)
BEGIN
SELECT  ag.name                             AS [AGName]
        ,adc.[database_name]                AS [DBName]
        ,ar.replica_server_name             AS [ReplicaServerName]
        ,drs.synchronization_state_desc     AS [SynchronizationState]
        ,drs.synchronization_health_desc    AS [SynchronizationHealth]
        ,drs.is_local                       AS [IsLocal]
        ,ar.availability_mode_desc          AS [AvailabilityMode]
FROM    sys.dm_hadr_database_replica_states                 AS drs WITH (NOLOCK)
        INNER JOIN sys.availability_databases_cluster       AS adc WITH (NOLOCK)
            ON  drs.group_id            = adc.group_id
            AND drs.group_database_id   = adc.group_database_id
        INNER JOIN sys.availability_groups                  AS ag WITH (NOLOCK)
            ON  ag.group_id             = drs.group_id
        INNER JOIN sys.availability_replicas                AS ar WITH (NOLOCK)
            ON  drs.group_id            = ar.group_id
            AND drs.replica_id          = ar.replica_id
WHERE   1=1
AND NOT (   drs.synchronization_state_desc  = 'SYNCHRONIZED'
        AND drs.synchronization_health_desc = 'HEALTHY'
        )
ORDER BY
        ag.name
        ,ar.replica_server_name
        ,adc.[database_name]        OPTION (RECOMPILE)
END";

(Get-Instance).ForEach{

    Describe –Name "All databases should be synchronized and healthy in an AAG" –Tags DBHealthyInAG, $filename {
        Context –Name "All databases should be synchronized and healthy in an AAG on $psitem" {
            It "should have 0 unsynchronized and unhealthy databases" {
                Invoke-DbaQuery -SqlInstance $psitem -Query $DBHealthyInAG |  
                Measure-Object |
                    Select-Object –ExpandProperty Count |
                    Should -Be 0 –Because "All databases need to be synchronized and healthy"
            }
        }
    }
}

When we install the dbachecks, Pester tests & checks are stored in a particular folder and to find where we can run the cmdlet:

Get-DbcConfig -Name app.checkrepos

Result is:

To add my checks to the list of the available checks I need to add the location of my file to this configuration value.
I can do it with the cmdlet:

Set-DbcConfig -Name app.checkrepos -Value 'C:\dbachecks\OwnChecks\' -Append

Now, my folder has been added to the check repo:

I’m able to call my two new tags as I would do for “existing” ones and the result is:

All my databases are part of an Always On Availability Group and my AAG are healthy & synchronized 😉

I was able to add my two new checks and if I need another ones, I can add them to my file.

Quite easy no?