-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathTest-DBReadOnlyRouting.ps1
102 lines (84 loc) · 4.15 KB
/
Test-DBReadOnlyRouting.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
function Test-DBReadOnlyRouting {
<#
.SYNOPSIS
Tests read only routing for an availability group, and returns whether or not the routing is valid.
.DESCRIPTION
Tests read only routing for an availability group, and returns whether or not the routing is valid.
.PARAMETER ServerInstances
The sql server instances to connect to. This should be the listener name of the AG group.
.PARAMETER Database
The database. This database must be a synchronized database. If left empty, the the script will attempt to discover a synchronized database.
.PARAMETER Credentials
Specifies credentials to connect to the database with. If not supplied then a trusted connection will be used. This authentication will
be used for each server.
.OUTPUTS
.EXAMPLE
PS >Test-DBReadOnlyRouting -ServerInstances "listener1", "listener2" | format-table
.LINK
https://github.com/tcartwright/tcdbtools
.NOTES
Author: Tim Cartwright
#>
[OutputType([System.Collections.Generic.List[System.Object]])]
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateCount(1, 9999)]
[string[]]$ServerInstances,
[string]$Database,
[pscredential]$Credentials
)
begin {
$ret = New-Object 'System.Collections.Generic.List[System.Object]'
}
process {
foreach ($ServerInstance in $ServerInstances) {
try {
$serverTest = [TestReadonlyRoutingResults]::new()
$serverTest.ListenerName = $ServerInstance
$serverTest.ReadOnlyIsValid = $false
$ret.Add($serverTest) | Out-Null
$connection = New-DBSQLConnection -ServerInstance $ServerInstance -Database "master" -Credentials $Credentials
$connectionRO = New-DBSQLConnection -ServerInstance $ServerInstance -Database "master" -Credentials $Credentials -ApplicationIntent ReadOnly
$connection.Open()
if (-not $Database) {
$sql = (GetSQLFileContent -fileName "FindSynchronizedDB.sql") -f $Database
$Database = Invoke-DBScalarQuery -conn $connection -sql $sql
} else {
# doing this to block sql injection for the USE statement below. ensure their db name actually exists and to also ensure it is synchronized
$sql = "SELECT d.[name] FROM sys.databases d WHERE d.[name] = @name"
$parameters = @(
(New-DBSqlParameter -name "@name" -type NVarChar -size 256 -value $Database)
)
$Database = Invoke-DBScalarQuery -conn $connection -sql $sql -parameters $parameters
}
if ($Database) {
$connectionRO.Open()
$query = "USE [$Database]; SELECT @@SERVERNAME;"
Write-Information "TESTING Read Only Routing for: `r`n`tSERVER: $($ServerInstance.ToUpper())`r`n`tDATABASE: $($Database.ToUpper())`r`n"
Write-Information "Connecting using RW connection"
$server = (Invoke-DBScalarQuery -conn $connection -sql $query)
Write-Information "Connecting using RO connection"
$serverRO = (Invoke-DBScalarQuery -conn $connectionRO -sql $query)
$serverTest.ReadOnlyServer = $serverRO
$serverTest.ReadWriteServer = $server
$serverTest.ReadOnlyIsValid = $serverRO -ine $server
$serverTest.Database = $Database
if (-not $serverTest.ReadOnlyIsValid) {
$serverTest.Reason = "SERVERS ARE EQUAL"
}
} else {
$serverTest.Reason = "NO SYNCHRONIZED DBS"
}
} catch {
$serverTest.Reason = "EXCEPTION: $($_.Exception.GetBaseException().Message)"
} finally {
if ($connection) { $connection.Dispose() }
if ($connectionRO) { $connectionRO.Dispose() }
}
}
}
end {
return $ret
}
}