Skip to content

Commit c875134

Browse files
Test-DbaAgPolicyState - Implement all 13 policies, fix Issue property, add Refresh
- Add 6 remaining policies: Availability Replica Role/Connection/Joined State and Availability Database Synchronization/Suspension/Join State - Fix Issue property to be null when IsHealthy is true (only populate on failure) - Add Replica and Database output properties for replica/database-level policies - Add $ag.Refresh() to ensure current state is evaluated - Update .OUTPUTS documentation to reflect new properties (do Test-DbaAgPolicyState) Co-authored-by: Andreas Jordan <andreasjordan@users.noreply.github.com>
1 parent 629ef3f commit c875134

1 file changed

Lines changed: 203 additions & 8 deletions

File tree

public/Test-DbaAgPolicyState.ps1

Lines changed: 203 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ function Test-DbaAgPolicyState {
5959
- InstanceName: The SQL Server instance name (string)
6060
- SqlInstance: The full SQL Server instance name (string)
6161
- AvailabilityGroup: Name of the availability group (string)
62+
- Replica: Name of the availability replica for replica-level policies, $null for server/AG-level policies (string)
63+
- Database: Name of the availability database for database-level policies, $null for server/AG/replica-level policies (string)
6264
- PolicyName: The name of the Always On policy being evaluated (string)
6365
- Category: The severity of the policy - Critical or Warning (string)
6466
- Facet: The object type the policy applies to (string)
6567
- IsHealthy: Boolean indicating whether the policy check passed (bool)
66-
- Issue: Description of the issue when the policy is not healthy (string)
68+
- Issue: Description of the issue when the policy is not healthy, $null when healthy (string)
6769
- Details: Additional detail about the current state (string)
6870
6971
.EXAMPLE
@@ -104,6 +106,7 @@ function Test-DbaAgPolicyState {
104106

105107
foreach ($ag in $InputObject) {
106108
$server = $ag.Parent
109+
$ag.Refresh()
107110

108111
<#
109112
WSFC cluster service is offline
@@ -124,11 +127,13 @@ function Test-DbaAgPolicyState {
124127
InstanceName = $ag.InstanceName
125128
SqlInstance = $ag.SqlInstance
126129
AvailabilityGroup = $ag.Name
130+
Replica = $null
131+
Database = $null
127132
PolicyName = "WSFC Cluster State"
128133
Category = "Critical"
129134
Facet = "Instance of SQL Server"
130135
IsHealthy = $isHealthy
131-
Issue = "WSFC cluster service is offline."
136+
Issue = if ($isHealthy) { $null } else { "WSFC cluster service is offline." }
132137
Details = "ClusterQuorumState is $($server.ClusterQuorumState)"
133138
}
134139

@@ -152,11 +157,13 @@ function Test-DbaAgPolicyState {
152157
InstanceName = $ag.InstanceName
153158
SqlInstance = $ag.SqlInstance
154159
AvailabilityGroup = $ag.Name
160+
Replica = $null
161+
Database = $null
155162
PolicyName = "Availability Group Online State"
156163
Category = "Critical"
157164
Facet = "Availability group"
158165
IsHealthy = $agState.IsOnline
159-
Issue = "Availability group is offline."
166+
Issue = if ($agState.IsOnline) { $null } else { "Availability group is offline." }
160167
Details = "IsOnline is $($agState.IsOnline)"
161168
}
162169

@@ -179,11 +186,13 @@ function Test-DbaAgPolicyState {
179186
InstanceName = $ag.InstanceName
180187
SqlInstance = $ag.SqlInstance
181188
AvailabilityGroup = $ag.Name
189+
Replica = $null
190+
Database = $null
182191
PolicyName = "Availability Group Automatic Failover Readiness"
183192
Category = "Critical"
184193
Facet = "Availability group"
185194
IsHealthy = $isHealthy
186-
Issue = "Availability group is not ready for automatic failover."
195+
Issue = if ($isHealthy) { $null } else { "Availability group is not ready for automatic failover." }
187196
Details = "IsAutoFailover is $($agState.IsAutoFailover), NumberOfSynchronizedSecondaryReplicas is $($agState.NumberOfSynchronizedSecondaryReplicas)"
188197
}
189198

@@ -206,11 +215,13 @@ function Test-DbaAgPolicyState {
206215
InstanceName = $ag.InstanceName
207216
SqlInstance = $ag.SqlInstance
208217
AvailabilityGroup = $ag.Name
218+
Replica = $null
219+
Database = $null
209220
PolicyName = "Availability Replicas Connection State"
210221
Category = "Warning"
211222
Facet = "Availability group"
212223
IsHealthy = $isHealthy
213-
Issue = "Some availability replicas are disconnected."
224+
Issue = if ($isHealthy) { $null } else { "Some availability replicas are disconnected." }
214225
Details = "NumberOfDisconnectedReplicas is $($agState.NumberOfDisconnectedReplicas)"
215226
}
216227

@@ -233,11 +244,13 @@ function Test-DbaAgPolicyState {
233244
InstanceName = $ag.InstanceName
234245
SqlInstance = $ag.SqlInstance
235246
AvailabilityGroup = $ag.Name
247+
Replica = $null
248+
Database = $null
236249
PolicyName = "Availability Replicas Data Synchronization State"
237250
Category = "Warning"
238251
Facet = "Availability group"
239252
IsHealthy = $isHealthy
240-
Issue = "Some availability replicas are not synchronizing data."
253+
Issue = if ($isHealthy) { $null } else { "Some availability replicas are not synchronizing data." }
241254
Details = "NumberOfNotSynchronizingReplicas is $($agState.NumberOfNotSynchronizingReplicas)"
242255
}
243256

@@ -260,11 +273,13 @@ function Test-DbaAgPolicyState {
260273
InstanceName = $ag.InstanceName
261274
SqlInstance = $ag.SqlInstance
262275
AvailabilityGroup = $ag.Name
276+
Replica = $null
277+
Database = $null
263278
PolicyName = "Availability Replicas Role State"
264279
Category = "Warning"
265280
Facet = "Availability group"
266281
IsHealthy = $isHealthy
267-
Issue = "Some availability replicas do not have a healthy role."
282+
Issue = if ($isHealthy) { $null } else { "Some availability replicas do not have a healthy role." }
268283
Details = "NumberOfReplicasWithUnhealthyRole is $($agState.NumberOfReplicasWithUnhealthyRole)"
269284
}
270285

@@ -287,13 +302,193 @@ function Test-DbaAgPolicyState {
287302
InstanceName = $ag.InstanceName
288303
SqlInstance = $ag.SqlInstance
289304
AvailabilityGroup = $ag.Name
305+
Replica = $null
306+
Database = $null
290307
PolicyName = "Synchronous Replicas Data Synchronization State"
291308
Category = "Warning"
292309
Facet = "Availability group"
293310
IsHealthy = $isHealthy
294-
Issue = "Some synchronous replicas are not synchronized."
311+
Issue = if ($isHealthy) { $null } else { "Some synchronous replicas are not synchronized." }
295312
Details = "NumberOfNotSynchronizedReplicas is $($agState.NumberOfNotSynchronizedReplicas)"
296313
}
314+
315+
foreach ($replica in $ag.AvailabilityReplicas) {
316+
<#
317+
Availability replica does not have a healthy role
318+
Documentation: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/availability-replica-does-not-have-a-healthy-role
319+
Policy Name: Availability Replica Role State
320+
Issue: Availability replica does not have a healthy role.
321+
Category: Warning
322+
Facet: Availability replica
323+
324+
Name : AlwaysOnArReplicaRoleHealthCondition
325+
Facet : IAvailabilityReplicaState
326+
ExpressionNode : @Role = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRole', 'Primary') OR @Role = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRole', 'Secondary')
327+
#>
328+
329+
$isHealthy = $replica.Role -in "Primary", "Secondary"
330+
[PSCustomObject]@{
331+
ComputerName = $ag.ComputerName
332+
InstanceName = $ag.InstanceName
333+
SqlInstance = $ag.SqlInstance
334+
AvailabilityGroup = $ag.Name
335+
Replica = $replica.Name
336+
Database = $null
337+
PolicyName = "Availability Replica Role State"
338+
Category = "Warning"
339+
Facet = "Availability replica"
340+
IsHealthy = $isHealthy
341+
Issue = if ($isHealthy) { $null } else { "Availability replica does not have a healthy role." }
342+
Details = "Role is $($replica.Role)"
343+
}
344+
345+
<#
346+
Availability replica is disconnected
347+
Documentation: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/availability-replica-is-disconnected
348+
Policy Name: Availability Replica Connection State
349+
Issue: Availability replica is disconnected.
350+
Category: Warning
351+
Facet: Availability replica
352+
353+
Name : AlwaysOnArReplicaConnectionHealthCondition
354+
Facet : IAvailabilityReplicaState
355+
ExpressionNode : @ConnectionState = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityReplicaConnectionState', 'Connected') OR @Role = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityReplicaRole', 'Primary')
356+
#>
357+
358+
$isHealthy = $replica.ConnectionState -eq "Connected" -or $replica.Role -eq "Primary"
359+
[PSCustomObject]@{
360+
ComputerName = $ag.ComputerName
361+
InstanceName = $ag.InstanceName
362+
SqlInstance = $ag.SqlInstance
363+
AvailabilityGroup = $ag.Name
364+
Replica = $replica.Name
365+
Database = $null
366+
PolicyName = "Availability Replica Connection State"
367+
Category = "Warning"
368+
Facet = "Availability replica"
369+
IsHealthy = $isHealthy
370+
Issue = if ($isHealthy) { $null } else { "Availability replica is disconnected." }
371+
Details = "ConnectionState is $($replica.ConnectionState), Role is $($replica.Role)"
372+
}
373+
374+
<#
375+
Availability replica is not joined
376+
Documentation: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/availability-replica-is-not-joined
377+
Policy Name: Availability Replica Joined State
378+
Issue: Availability replica is not joined.
379+
Category: Warning
380+
Facet: Availability replica
381+
382+
Name : AlwaysOnArReplicaJoinedHealthCondition
383+
Facet : IAvailabilityReplicaState
384+
ExpressionNode : @JoinState = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityReplicaJoinState', 'JoinedStandaloneInstance') OR @JoinState = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityReplicaJoinState', 'JoinedWindowsServerFailoverCluster')
385+
#>
386+
387+
$isHealthy = $replica.JoinState -in "JoinedStandaloneInstance", "JoinedWindowsServerFailoverCluster"
388+
[PSCustomObject]@{
389+
ComputerName = $ag.ComputerName
390+
InstanceName = $ag.InstanceName
391+
SqlInstance = $ag.SqlInstance
392+
AvailabilityGroup = $ag.Name
393+
Replica = $replica.Name
394+
Database = $null
395+
PolicyName = "Availability Replica Joined State"
396+
Category = "Warning"
397+
Facet = "Availability replica"
398+
IsHealthy = $isHealthy
399+
Issue = if ($isHealthy) { $null } else { "Availability replica is not joined." }
400+
Details = "JoinState is $($replica.JoinState)"
401+
}
402+
}
403+
404+
foreach ($databaseReplicaState in $ag.DatabaseReplicaStates) {
405+
<#
406+
Data synchronization state of availability database is not healthy
407+
Documentation: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/data-synchronization-state-of-availability-database-is-not-healthy
408+
Policy Name: Availability Database Synchronization State
409+
Issue: Data synchronization state of availability database is not healthy.
410+
Category: Critical
411+
Facet: Availability database
412+
413+
Name : AlwaysOnDbDataSynchronizationHealthCondition
414+
Facet : IAvailabilityDatabaseState
415+
ExpressionNode : @SynchronizationState = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityDatabaseSynchronizationState', 'Synchronized') OR @SynchronizationState = Enum('Microsoft.SqlServer.Management.Smo.AvailabilityDatabaseSynchronizationState', 'Synchronizing')
416+
#>
417+
418+
$isHealthy = $databaseReplicaState.SynchronizationState -in "Synchronized", "Synchronizing"
419+
[PSCustomObject]@{
420+
ComputerName = $ag.ComputerName
421+
InstanceName = $ag.InstanceName
422+
SqlInstance = $ag.SqlInstance
423+
AvailabilityGroup = $ag.Name
424+
Replica = $databaseReplicaState.AvailabilityReplicaServerName
425+
Database = $databaseReplicaState.AvailabilityDatabaseName
426+
PolicyName = "Availability Database Synchronization State"
427+
Category = "Critical"
428+
Facet = "Availability database"
429+
IsHealthy = $isHealthy
430+
Issue = if ($isHealthy) { $null } else { "Data synchronization state of availability database is not healthy." }
431+
Details = "SynchronizationState is $($databaseReplicaState.SynchronizationState)"
432+
}
433+
434+
<#
435+
Availability database is suspended
436+
Documentation: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/availability-database-is-suspended
437+
Policy Name: Availability Database Suspension State
438+
Issue: Availability database is suspended.
439+
Category: Warning
440+
Facet: Availability database
441+
442+
Name : AlwaysOnDbSuspendedHealthCondition
443+
Facet : IAvailabilityDatabaseState
444+
ExpressionNode : @IsSuspended = False()
445+
#>
446+
447+
$isHealthy = -not $databaseReplicaState.IsSuspended
448+
[PSCustomObject]@{
449+
ComputerName = $ag.ComputerName
450+
InstanceName = $ag.InstanceName
451+
SqlInstance = $ag.SqlInstance
452+
AvailabilityGroup = $ag.Name
453+
Replica = $databaseReplicaState.AvailabilityReplicaServerName
454+
Database = $databaseReplicaState.AvailabilityDatabaseName
455+
PolicyName = "Availability Database Suspension State"
456+
Category = "Warning"
457+
Facet = "Availability database"
458+
IsHealthy = $isHealthy
459+
Issue = if ($isHealthy) { $null } else { "Availability database is suspended." }
460+
Details = "IsSuspended is $($databaseReplicaState.IsSuspended)"
461+
}
462+
463+
<#
464+
Availability database is not joined to the availability group
465+
Documentation: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/availability-database-is-not-joined
466+
Policy Name: Availability Database Join State
467+
Issue: Availability database is not joined to the availability group.
468+
Category: Warning
469+
Facet: Availability database
470+
471+
Name : AlwaysOnDbJoinedHealthCondition
472+
Facet : IAvailabilityDatabaseState
473+
ExpressionNode : @IsJoined = True()
474+
#>
475+
476+
$isHealthy = $databaseReplicaState.IsJoined
477+
[PSCustomObject]@{
478+
ComputerName = $ag.ComputerName
479+
InstanceName = $ag.InstanceName
480+
SqlInstance = $ag.SqlInstance
481+
AvailabilityGroup = $ag.Name
482+
Replica = $databaseReplicaState.AvailabilityReplicaServerName
483+
Database = $databaseReplicaState.AvailabilityDatabaseName
484+
PolicyName = "Availability Database Join State"
485+
Category = "Warning"
486+
Facet = "Availability database"
487+
IsHealthy = $isHealthy
488+
Issue = if ($isHealthy) { $null } else { "Availability database is not joined to the availability group." }
489+
Details = "IsJoined is $($databaseReplicaState.IsJoined)"
490+
}
491+
}
297492
}
298493
}
299494
}

0 commit comments

Comments
 (0)