Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

Supervision – SQL – 3 scripts pour la supervision de Always On

 

Dans le cadre d’un portage de règle de supervision, les trois scripts ci-dessous ont été crées pour remonter l’état de:

– Un Availability Group

– Un Availability Replica

– Un Database Replica

 

En pratique ils utilisent a peu près la même requête SQL. Ils contiennent des éléments propre a l’api scom mais peuvent bien sur être adapté pour être utilisé indépendamment.

Ci-dessous les 3 scripts et l’exemple du code du premier, Check_SQL_AO_AvailabilityGroupStatus_Query_Version.ps1.

 

 

#######################################################################################
#         
#
         
#          Script: SQLAlwaysOnAvailGroupStatus.ps1

#          Purpose: Shows AlwaysOn Availability Group Status
#         
#          Parameters:

#           $DBServer: ShortName of DB Server
#          $InstanceFullName: Name of SQL Instance
#          $AvailGroupName: Availability Group Name
#         
#

########################################################################################

param ( 
           
$Arguments,

           
[string]$DBServer,
           
[string]$InstanceFullName,
       
[string]$AvailGroupName
           
                          
           
)

# Create local variables from override value
.([Scriptblock]::Create($Arguments))

$Scriptname = « SQLAlwaysOnAvailabilityReplicaStatus.ps1 »

# Name of Instance
$ServerInstance = $InstanceFullName.Split(‘\’)[1]

    #Determine TcpPort Used for Instance
    try
    {
    $TcpPort = Get-ItemProperty « HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL11.$ServerInstance\MSSQLServer\SuperSocketNetLib\Tcp\IpAll » | select TcpPort -exp tcpport
    }
    catch
    {
    $Message = « Error during Retrieve of TCP Port Used – Check the execution of the script »
    write-host -f Yellow $Message
    $PropertyBag.AddValue(« State », »WARNING »)
    $PropertyBag.AddValue(« Message »,$Message)
    $PropertyBag.AddValue(« ReplicaName »,$AvailabilityReplicaName)
    $PropertyBag
    Exit 1
    }

# Scom Object and Property Bag
$api = New-Object -comObject “MOM.ScriptAPI” 
$PropertyBag = $api.CreatePropertyBag()

# Function  GetAODBRepStatus
Function GetAOAvailGroupStatus
                        {
                        param(
                                    [string]$TargetComputer=$DBServer,
                                    $global:Source = « $DBServer\$ServerInstance »,
                                    [string]$sqlCommand =
                                            $(« 
                                            SELECT
                                           
                                            AO_AG.name as AvailGroupName
                                            ,HADR_AO_AGS.synchronization_health as AvailGroup_SyncHealth
                                            ,HADR_AO_AGS.synchronization_health_desc as AvailGroup_SyncHealth_Desc
                                            –,SYSDB.name as DBName
                                           
                                            –,AO_AVREP.replica_server_name
                                            –,HADR_AVAIL_REP_STATE.synchronization_health as AvailReplica_SyncHealth
                                           
                                            –,HADR_AVAIL_REP_STATE.synchronization_health_desc as AvailReplica_SyncHealth_Desc

                                            –,HADR_DB_REP_STATE.synchronization_state as DBReplica_SyncState
                                            –,HADR_DB_REP_STATE.synchronization_state_desc as DBReplica_SyncState_Desc
                                            –,HADR_DB_REP_STATE.synchronization_health_desc as DBReplica_SyncHealth_Desc

                                           
                                            FROM
                                            [sys].[availability_databases_cluster] AO_DB_CLUS
                                            –INNER JOIN sys.databases SYSDB on CAST(SYSDB.group_database_id AS VARCHAR(50)) =  CAST(AO_DB_CLUS.group_database_id AS VARCHAR(50))
                                            INNER JOIN sys.dm_hadr_database_replica_states HADR_DB_REP_STATE on CAST(HADR_DB_REP_STATE.group_database_id AS VARCHAR(50)) = CAST(AO_DB_CLUS.group_database_id AS VARCHAR(50))
                                            –INNER JOIN sys.dm_hadr_availability_replica_states HADR_AVAIL_REP_STATE on HADR_AVAIL_REP_STATE.group_id = HADR_DB_REP_STATE.group_id
                                            INNER JOIN sys.availability_groups AO_AG on AO_AG.group_id = HADR_DB_REP_STATE.group_id
                                            INNER JOIN sys.dm_hadr_availability_group_states HADR_AO_AGS on HADR_AO_AGS.group_id = AO_AG.group_id
                                            –INNER JOIN [sys].[availability_replicas] AO_AVREP on AO_AVREP.replica_id = HADR_DB_REP_STATE.replica_id
                                            WHERE AO_AG.name = ‘
$AvailGroupName

                                           
                                            GROUP BY
                                            AO_AG.name
                                            ,HADR_AO_AGS.synchronization_health
                                            ,HADR_AO_AGS.synchronization_health_desc
                                            « 
                                            )
                               )

                               Try
                               {

                                                                                           
                                               

                                            $global:connectionString = « Data Source=$Source,$TcpPort; » +
                                            « Integrated Security=SSPI;  » +
                                            « Initial Catalog=master »

                                           
                                            $connection = new-object system.data.SqlClient.SQLConnection($connectionString)
                                           
                               
                                                               
                                            $connection.Open()
                                                                                 
                                                                              

                                            $command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)

                                }
                                catch
                                {
                                write-host -F Red $(« Error during sql connection – check the credentials used »).ToUpper()
                                #exit 1
                                }

                                $adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
                                $set = New-Object System.Data.DataSet
                                $adapter.Fill($Set) | Out-Null

                                $connection.Close()
                                $Set.Tables

                               

                              }

# Execute function and get data
try
{
[array]$AvailGroup = GetAOAvailGroupStatus
}
catch
{
$Message = « WARNING – Error during Connection to master database or execution of sql query »
write-host -F Yellow $Message
$PropertyBag.AddValue(« State », »WARNING »)
$PropertyBag.AddValue(« Message »,$Message)
$PropertyBag.AddValue(« DBServer »,$DBServer)
$PropertyBag.AddValue(« connectstring »,$connectionString)
$PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
$PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
$PropertyBag
exit 1
}

if (!($AvailGroup))
{
$Message = « WARNING – Error – No Availability Group have been found »
write-host -F Yellow $Message
$PropertyBag.AddValue(« State », »WARNING »)
$PropertyBag.AddValue(« Message »,$Message)
$PropertyBag.AddValue(« DBServer »,$DBServer)
$PropertyBag.AddValue(« AvailGroupName », »no_data »)
$PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
$PropertyBag
exit 1
}

 

try
{
$AvailGroupState = $AvailGroup  | select AvailGroup_SyncHealth_Desc -ExpandProperty AvailGroup_SyncHealth_Desc
}
catch
{
$Message = « Error during Retrieve of Availability Group State »
Write-Host -ForegroundColor Yellow $Message
$PropertyBag.AddValue(« State », »WARNING »)
$PropertyBag.AddValue(« Message »,$Message)
$PropertyBag.AddValue(« DBServer »,$DBServer)
$PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
$PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
$PropertyBag
exit 1
}

« AVAILABILITY GROUP: $AvailGroupName »

If ($AvailGroupState -eq « Healthy »)

        {
        $Message = « OK – Status of $AvailGroupName Availability Group is Healthy »
        write-host -f Green $Message
        $PropertyBag.AddValue(« State », »OK »)
        $PropertyBag.AddValue(« Message »,$Message)
        $PropertyBag.AddValue(« DBServer »,$DBServer)
        $PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
        $PropertyBag.AddValue(« AvailGroupStatus », »Healthy »)
        $PropertyBag
        Exit 0
        }
       

ElseIf ($AvailGroupState -eq « Error »)

   
        {
        $Message = « CRITICAL – Status of $AvailGroupName Availability Group is Error »
        write-host -f Red $Message
        $PropertyBag.AddValue(« State », »CRITICAL »)
        $PropertyBag.AddValue(« Message »,$Message)
      $PropertyBag.AddValue(« DBServer »,$DBServer)
        $PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
        $PropertyBag.AddValue(« AvailGroupStatus », »Error »)
        $PropertyBag
        Exit 0
        }
       
Else   {
        $Message = « WARNING – Status of $AvailGroupName Availability Group cannot be determined »
        write-host -f yellow $Message
        $PropertyBag.AddValue(« State », »WARNING »)
        $PropertyBag.AddValue(« Message »,$Message)
      $PropertyBag.AddValue(« DBServer »,$DBServer)
        $PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
        $PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
        $PropertyBag
        Exit 1
        }
 
 

########################################################################################

Supervision – Script de corrélation a la seconde

 

Une demande m’a été faite récemment pour la détection de l’occurrence de deux events distinct a la même seconde, ce cas particulier traduisant un problème de sécurité spécifique.

Indépendamment de l’objectif final, il s’agit d’un cas intéressant auquel le script ci-dessous a répondu. Il contient des éléments propre a l’api scom mais peux bien sur être adapté pour être utilisé indépendamment.

 

 

  ##############################################################
### SCRIPT TO DETECT SPECIFIC TWO EVENTS OCCURING AT SAME TIME #####
##############################################################

# PARAMETERS:
### $EventLog: Event Log to look in
### $EventSource: Event Source to search for 

### $FirstEventId: First event to correlate
### $SecondEventId: second event to correlate

### $LastMinutes: Last Time Window to search in
### $DayOfWeekToExclude: Day Of Week To Exclude (Example: « (‘Saturday’,’Sunday’) » )

param(
$Arguments,
$EventLog,
$EventSource,
$FirstEventId,
$SecondEventId,
$LastMinutes,
$DayOfWeekToExclude
)

$ScriptName = « CorrelateTwoSpecEvent.ps1 »

#FUNCTIONS

#Check for the existence of an event source with script name in operation manager eventlog to log some events 
        
Function NewEventSource
 
        
{
 
        
if(!(Test-Path « HKLM:\SYSTEM\CurrentControlSet\services\eventlog\Operations Manager\$ScriptName »))
 
        
{
 
        
New-EventLog -LogName « Operations Manager » -Source $ScriptName
 
        
}

        
} 

#END FUNCTIONS

#Log of script execution 
NewEventSource
 
write-eventlog -logname « Operations Manager » -Source $ScriptName -EventID 1000 -Message « Execution du script $ScriptName » -EntryType Information
 

# Create local variables from override value
.([Scriptblock]::Create($Arguments))

# Determine the moment in the week
if ((Get-date).DayOfWeek -in $DayOfWeekToExclude)
{
# If the day is in $DayOfWeekToExclude -> NO ACTION  END OF SCRIPT
Write-Host « $((Get-date).DayOfWeek) : NO ACTION – END OF SCRIPT »
Exit 0

}

# Create the Scom property bag
$ScomAPI = New-Object -comObject « MOM.ScriptAPI »
$PropertyBag = $ScomAPI.CreatePropertyBag()

$Message =     « SEARCH CRITERIAS: Log: $EventLog – Source: $EventSource – EventId: $FirstEventId or $SecondEventId `n »
$Message

try
{
New-Variable -Name « $($FirstEventId)_Events » -Force -Value $(Get-WinEvent -ErrorAction SilentlyContinue -FilterHashtable @{logname=$EventLog;ProviderName=$EventSource;id=$FirstEventId;StartTime=$(get-date).AddMinutes(-$LastMinutes)})
New-Variable -Name « $($SecondEventId)_Events » -Force -Value $(Get-WinEvent -ErrorAction SilentlyContinue -FilterHashtable @{logname=$EventLog;ProviderName=$EventSource;id=$SecondEventId;StartTime=$(get-date).AddMinutes(-$LastMinutes)})
}
catch
{
$Message = « Error during retrieve of events in the $ScriptName script »
$Message

NewEventSource
Write-EventLog -LogName « operations manager » -Source $ScriptName -EventId 1001 -EntryType Warning -Message « $Message »
Exit 1

}

#If no one of the two events id have occurence no need to continue
if (!$(Get-Variable « $($FirstEventId)_Events »).Value -and !$(Get-Variable « $($SecondEventId)_Events »).Value)
   
{
   
$Message =  « No one of the two events id have occurences in last $LastMinutes minutes – END OF SCRIPT »
   
$Message

   
Write-EventLog -LogName « operations manager » -Source $ScriptName -EventId 1002 -EntryType Information -Message « $Message »

    Exit 0
   
}

#If Only one of the two events id have occurences no need to continue
if (!$(Get-Variable « $($FirstEventId)_Events »).Value -or !$(Get-Variable « $($SecondEventId)_Events »).Value)
   
{
   
$Message = « Only one of the two events id have occurences – END OF SCRIPT »
   
$Message

   
Write-EventLog -LogName « operations manager » -Source $ScriptName -EventId 1003 -EntryType Information -Message $Message

    Exit 0
   
}

$Message = « $($(Get-Variable « $($FirstEventId)_Events« ).Value.count) occurence of event $FirstEventId and $($(Get-Variable « $($SecondEventId)_Events« ).Value.count) occurence of event $SecondEventId in the last $LastMinutes minutes »
$Message +=
« `nSTART OF COMPARAISON…`n »
#$Message

#Compare DateTimes at second level
try
{
$CompareResult = Compare-Object -ReferenceObject $(Get-Variable -Name « $($FirstEventId)_Events »).Value.timecreated.second -DifferenceObject $(Get-Variable -Name « $($SecondEventId)_Events »).Value.timecreated.second -ExcludeDifferent -IncludeEqual -Verbose
}
catch
{
$Message += « Error during comparaison of Date Creation »
$Message

NewEventSource
Write-EventLog -LogName « operations manager » -Source $ScriptName -EventId 1004 -EntryType Warning -Message $Message
Exit 1
}

#If $CompareResult is null, Events have not occureat the same time
If (!($CompareResult))
   
{
   
$Message += « Events $FirstEventId and $SecondEventId have not occured at the same second – No correlation »
   
$Message

   
NewEventSource   
   
Write-EventLog -LogName « operations manager » -Source $ScriptName -EventId 1004 -EntryType Information -Message $Message
   
   
exit 0

   
}

Else
   
{
   
NewEventSource
   
#
   
$Message += « EVENT $FirstEventId and $SecondEventId have occured at same second $($CompareResult.count) times `n »
   
$Message +=
« `nEVENTS OF LAST $LastMinutes MINUTES:`n »
       
   
$Message += $(Get-Variable « $($FirstEventId)_Events »).value | foreach {$_} | Out-String
 
   
$Message += $(Get-Variable « $($SecondEventId)_Events »).value | foreach {$_} | Out-String

   
$Message
   
Write-EventLog -LogName « operations manager » -Source $ScriptName -EventId 1005 -EntryType Information -Message $Message
   
$PropertyBag.AddValue(« State »,« CRITICAL »)
   
$PropertyBag.AddValue(« Message »,$Message)
   
   
$PropertyBag
   
}

 
 


[Powershell] CredSSP : Credential Security Service Provider.

CredSSP qu’est ce que c’est ?

CredSSP : Credential Security Service Provider.

A quoi cela sert-il ?

A déléguer des identifiants pour une session distante, plus précisément, CredSSP vous permet de fournir une authentification de bout en bout au travers de plusieurs sessions distantes.

Exemple :

Depuis un Serveur A un script Powershell ouvre une session distante sur un Serveur B, ce dernier traite ses instructions puis ouvre une session distante pour requêter un Serveur C et y déposer ses résultats sur un partage.

Lors de l’ouverture de la session distante entre A et B Windows utilise les identifiants fournis, mais lors de l’ouverture de la session distante depuis B vers C, Windows considère qu’il s’agit d’une usurpation Kerberos (car par défaut WinRm n’autorise pas la délégation des identifiants).

Afin de palier  cela que ce soit pour un « Invoke-Command » ou un « New-PSSession » vous devrez ajouter « -Authentication CredSSP »; cet ajout vous permettra de déléguer les identifiants pour la seconde session distante.

Mise en oeuvre :

Et non, on ne peut pas utiliser l’option CredSSP sans prérequis, voici ceux à mettre en place.

Sur le Serveur A on va activer CredSSP « Client » via la commande suivante :

Enable-WSManCredSSP -Role Client -DelegateComputer ServerB.mondomaine.com -Force

On peut vérifier que cela à bien fonctionné à l’aide de la commande suivante :

Get-WSManCredSSP

Puis sur le Server B on va activer CredSSP « Server » via la commande suivante :

Enable-WSManCredSSP -Role Server -Force

vérifions.

Et voilà nous devrions donc pouvoir exécuter la commande initiale depuis le serveur A, vérifions cela sans oublier d’ajouter « -Authentication CredSSP ».

Enter-PSSession -ComputerName lab01-wsus1.LAB.ORG -Credential Lab\Adminmad -Authentication Credssp

Jusque la tout fonctionne, essayons donc maintenant de requêter vers le serveur C.

Invoke-Command -ComputerName Lab01-wsus2.lab.org -ScriptBlock {Get-WindowsFeature | Where-Object {$_.Name -like "UpdateServices"}}

C’est bien fonctionnel, bien entendu comme ce changement est lié à la sécurité, il est préconisé de désactiver cette option après usage.

On peut utiliser les commandes :

Disable-WSManCredSSP –Role Client # Sur le serveur A
Disable-WSManCredSSP –Role Server # Sur le serveur B

Ou directement depuis le serveur A :

Invoke-Command –ComputerName ServerB –ScriptBlock { Disable-WSManCredSSP –Role Server }
Disable-WSManCredSSP –Role Client

 

Annexes :

Vous pouvez aussi le faire sur plusieurs Serveurs : 

Enable-WSManCredSSP -Role "Client" -DelegateComputer "ServerB.mondomaine.com", "ServerC.mondomaine.com", "ServerD.mondomaine.com" -Force

Ou sur toutes les machines du domaine :

Enable-WSManCredSSP -Role "Client" -DelegateComputer "*.mondomaine.com" -Force


 Pour plus d’informations:

https://docs.microsoft.com/en-us/powershell/module/microsoft.wsman.management/enable-wsmancredssp?view=powershell-6 

https://msdn.microsoft.com/en-us/library/windows/desktop/bb931352(v=vs.85).aspx