PI Services

Le blog des collaborateurs de PI Services

Supervision - Zabbix - Script - Utilisation des Regex pour rechercher des données numeriques dans une chaine de caractère

Le cas présenté ici, peut être indépendant de l'outil de monitoring. Il s'agit de devoir alerter et donc définir des seuil sur une donnée qui est présenté par défaut sous forme de texte.

Pour être un peu plus clair, dans le contexte, un équipement snmp renvoi l'information d'utilisation de son file system sous la forme suivante:

"/tmp=0, /var=26, /var/log=0, /=22"

il s'agit en effet de la valeur de l'OID atpDiskUsage dans la MIB ATP-SNMP-MIB (Symantec Advanced Threat Protection)

Pour pouvoir traiter les valeurs d'utilisation du file system comme des nombre, les expressions régulières sont d'un grand secours.

Supposons que l'on veuille alerter sur le fait que "/var" soit au dessus de 80.

Dans ce cas l'expression proposé est la suivante régulière sera la suivante:

^.*(\/var=)[8-9][0-9]|100,.*$

Explication --> ^ : début de chaine

                      .*(\/var=) : n caractère n fois, suivi de "/var="  (on doit échapper le caractère '/' avec '\')

                      [8-9][0-9] : le range de chiffre est de 80 a 99

                      |100 : ou bien sur 100.

                       ,.* : la virgule présente, suivi de n caractère n fois

                       $: fin de chaine

Ce qui, pour nos 4 points de montage du file system donnera:

^.*(\/var=)[8-9][0-9]|100,.*$

^.*(\/tmp=)[8-9][0-9]|100,.*$

^.*(\/var\/log=)[8-9][0-9]|100,.*$

^.*(\/=)[8-9][0-9]|100.*$

Dans le contexte de Zabbix, Les expressions régulières ci-dessus ont donc été intégrées chacune dans un trigger utilisant la fonction "regexp() - Regular expression matching last value in period (1- match, 0 - not match)".

Indépendamment de l'outil de supervision, n'hésitez pas a user et abuser d'un outil de construction de regex tel que https://regex101.com/

 

SCOM - SCRIPT - Fonction powershell pour lister toutes les instances d'une machine

Le script ci-dessous intègre la requête SQL de récupération des instances d'une machine, sous forme de fonction.

All_InstancesForOneAgent_vSQLQuery.ps1

 

# SCRIPT THAT QUERY SCOM DATABASE TO GET ALL INSTANCES OF ALL CLASSES FOR A GIVEN COMPUTER ($TargetAgent)
# $TargetComputer must be Short name of computer because we look for this specific string in name, displayname and path of instances.

param(
#Short name of computer
$TargetComputer = "MyComputer"
)


# Function  Invoke-InstancesFromSQL

Function Invoke-InstancesFromSQL
                            {
                                param(
                                    [string]$dataSource = "MyScomSQLServer\OPSMGR",
                                    [string]$database = "OperationsManager",
                                    [string]$TargetComputer,
                                    [string]$sqlCommand = 
                                            $("Use $Database
                                                SELECT 
                                                   MTV.DisplayName as ClassName
	                                               ,MEGV.Path as Instance_Path
	                                               ,MEGV.Id as Instance_Id
	                                               ,MEGV.[DisplayName] as 'Entity_DisplayName'
	                                               ,MEGV.[Name] as 'Entity_Name'
	                                               ,MEGV.[FullName] as Entity_FullName
                                                   ,[IsManaged]
                                                   ,[IsDeleted]
                                                   ,HealthState = 
													CASE WHEN InMaintenanceMode = '0'
														  THEN 
															CASE [HealthState]
															WHEN '0' THEN 'Not Monitored'
															WHEN '1' THEN 'OK'
															WHEN '2' THEN 'Warning'
															WHEN '3' THEN 'Critical'
															END
														WHEN InMaintenanceMode = '1'
														THEN 
															CASE [HealthState]
															WHEN '0' THEN 'In Maintenance Mode'
															WHEN '1' THEN 'OK'
															WHEN '2' THEN 'Warning'
															WHEN '3' THEN 'Critical'
															END
													END
                                                  ,Is_Available = CASE [IsAvailable]
			                                            WHEN '1' THEN 'YES'
			                                            WHEN '2' THEN 'NO'
			                                            END
                                                  
                                                  ,In_MaintenanceMode = CASE [InMaintenanceMode]
			                                            WHEN '0' THEN 'NO'
			                                            WHEN '1' THEN 'YES'
			                                            END
                                                  
                                                  ,Start_Of_Maintenance = CASE WHEN InMaintenanceMode = '0' 
			                                            THEN null
			                                            ELSE MMV.StartTime
			                                            END
	                                              ,End_Of_Maintenance = CASE WHEN InMaintenanceMode = '0'
			                                            THEN null
			                                            ELSE MMV.ScheduledEndTime
			                                            END
                                                  	                                              
                                                  ,Maintenance_RootCause = 
														CASE WHEN InMaintenanceMode = '0'
														  THEN null
															ELSE 
																CASE MMV.ReasonCode
																WHEN '0' THEN 'Other (Planned)'
																WHEN '1' THEN 'Other (Unplanned)'
																  WHEN '2' THEN 'Hardware: Maintenance (Planned)'
																  WHEN '3' THEN 'Hardware: Maintenance (Unplanned)'
																  WHEN '4' THEN 'Hardware: Installation (Planned)'
																  WHEN '5' THEN 'Hardware: Installation (Unplanned)'
																  WHEN '6' THEN 'Operating System: Reconfiguration (Planned)'
																  WHEN '7' THEN 'Operating System: Reconfiguration (Unplanned)'
																  WHEN '8' THEN 'Application: Maintenance (Planned)'
																  WHEN '9' THEN 'Application: Maintenance (Unplanned)'
																  WHEN '10' THEN 'Application: Installation (Planned)'
																  WHEN '11' THEN 'Application: Unresponsive'
																  WHEN '12' THEN 'Application:  Unstable'
																  WHEN '13' THEN 'Security Issue'
																  WHEN '14' THEN 'Loss of network connectivity (Unplanned)'
																END
															END
                                                  
		                                            ,Maintenance_Reason =   
														CASE WHEN InMaintenanceMode = '0' 
														    THEN null
															    ELSE MMV.Comments
														END
	  
      
                                              FROM [OperationsManager].[dbo].[ManagedEntityGenericView] MEGV

                                              INNER JOIN [dbo].[ManagedTypeView] MTV on MEGV.MonitoringClassId = MTV.Id
                                              INNER JOIN [OperationsManager].[dbo].[MaintenanceModeView] MMV on MEGV.id = MMV.BaseManagedEntityId
                                              WHERE (MEGV.Name  like '%$TargetComputer%' OR MEGV.DisplayName  like '%$TargetComputer%' OR MEGV.Path  like '%$TargetComputer%')
                                              and MTV.LanguageCode = 'ENU'
                                              and MEGV.HealthState is not null
                                              and MEGV.IsDeleted <> '1'
                                              ORDER BY MTV.DisplayName
                                            ")
                                           )


                                    $connectionString = "Data Source=$dataSource; " +
                                    "Integrated Security=SSPI; " +
                                    "Initial Catalog=$database"

                                $connection = new-object system.data.SqlClient.SQLConnection($connectionString)
                                $command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
                                
                                try
                                {
                                $connection.Open()
                                }
                                catch
                                {
                                write-host -F Red $("Error during sql connection - check the credentials used").ToUpper()
                                exit 1
                                }

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

                                $connection.Close()
                                $dataSet.Tables

                              }


[array]$table = Invoke-InstancesFromSQL -TargetComputer $TargetComputer


write-host "`nCOMPUTER: $TargetComputer"

Write-Host "`nNb Of Objects:"$table.count""

$table | ft -Property ClassName,Instance_Path,Entity_DisplayName,Entity_Name,Entity_FullName,HealthState,Is_Available,In_MaintenanceMode,Start_Of_Maintenance,End_Of_Maintenance,Maintenance_RootCause,Maintenance_Reason



# TO FILTER ON SPECIFIC CLASS
# $table | Where-Object {$_.CLASS_NAME -in ('Windows computer','Health Service Watcher','VMware vSphere Host','VMWare Virtual Machine','HPE ProLiant Server')} | ft -AutoSize



# SIMULATE START OF MAINTENANCE MODE FOR THE INSTANCES
#$table | foreach {Get-SCOMClassInstance -id $_.instance_id} | foreach {start-SCOMMaintenanceMode -instance $_ -WhatIf -EndTime $((Get-Date).AddHours(1))}

 

 

 

 

 

 

Script Powershell pour Zabbix - Gerer les items d'un host

L'exemple ci-dessous utilise le module PowerShell PSBBIX qui encapsule les commandes de l'API Zabbix.

Parmi les nombreuses commandes, pour exemple, le script propose de désactiver ou activer les items (points de supervision) d'un host spécifique.

N'hésitez pas pour plus de détails a aller voir le github de PSBBIX (https://github.com/yubu/psbbix-zabbix-api)

ZabbixManageHostItems.ps1

#########################################################
### ENABLE/DISABLED ALL SPECIFIC ITEMS FOR A ZABBIX HOST
### Params:
### $ZabbixSrv: Target Zabbix Server
### $TargetHost: Target host to treat
### $ItemPattern: Pattern of Items to check
### $Action: Disable or Enable
#########################################################


Param(
[Parameter(Mandatory=$true)] $ZabbixSrv,
[Parameter(Mandatory=$true)] $TargetHost,
[Parameter(Mandatory=$true)] $ItemPattern,
[Parameter(Mandatory=$true)] $Action
)


if ($PSBoundParameters.Count -lt 4)
    {
    write-host -F Red "Too few arguments!"
    write-host -F Yellow "ZabbixSrv: Target Zabbix Server`nTargetHost: Target host to treat`nItemPattern: Pattern of Items to check`nAction: Disable or Enable"
    exit 1
    } 

switch($Action)
{
"disable" {$status = 1}
"enable" {$status = 0}
default {Write-Host -F Red "Possible value for Action is 'enable' or 'disable' - END OF SCRIPT" ; exit 0}
}



# Check if psbbix module is loaded
if (!(get-module psbbix))
    {
    write-host -F Yellow "psbbix module is not loaded...search module to load it..."
    $modulepath = $env:PSModulePath -split ‘;’ | foreach {Get-ChildItem -Recurse -Path $_ | Where-Object {$_.name -eq "psbbix.psm1"}}
    if (!$modulepath)
        {
        write-host -F Yellow "unable to find psbbix.psm1...get it on https://github.com/yubu/psbbix-zabbix-api"
        exit 1
        }
    Else
        {
            try
            {
            Import-Module $($modulepath | select -Unique).fullname
            }
            catch
            {
            write-host -F error "Error during import of psbbix module"
            exit 1
            }
        }
    Write-Host -F Green "...Module loaded"

    }


$cred = Get-Credential -Credential "cjourdan_fr"


# Connect to zabbix server
try
{
Connect-Zabbix $ZabbixSrv -PSCredential $cred
}
catch
{
Write-Host -F Red "Error during connection to $ZabbixSrv"
exit 1
}


# Get all items that we want to disable
$items = Get-ZabbixItem -hostid $(Get-ZabbixHost -HostName $TargetHost).hostid | Where-Object {$_.name -like $ItemPattern}

if ($items.Count -eq 0)
    {
    Write-Host -F Yellow "No items found with '$ItemPattern' pattern for $TargetHost host"
    }

Else
    {
        try
        {
        $items | foreach {Set-ZabbixItem -itemid $_.itemid -status $status}
        }
        catch
        {
        write-host -F Red "Error during modification of Items status"
        exit 1
        }

    }


Write-host "`nFollowing items have been treated for $TargetHost`:`n"
$items = Get-ZabbixItem -hostid $(Get-ZabbixHost -HostName $TargetHost).hostid | Where-Object {$_.name -like $ItemPattern}
$items | foreach {$_.name +" -- "+  $(switch($_.status){0{"enabled"}1{"disabled"}})}


 

SCOM – Supprimer un management pack directement dans la base de données

Préambule : cette manipulation n’est pas supportée par Microsoft, vous ne devriez pas vous en servir sur une infrastructure de production en dehors des instructions que vous fournirait le support.

Ceci étant dit, revenons-en au sujet.
Il peut arriver que certains Management Packs aient écrit tellement de données dans la base que leur suppression via la console ou via Powershell échoue après 30 minutes de travail :

clip_image002

Remove-SCOMManagementPack : The requested operation timed out

On entre alors dans un cercle vicieux : plus on attend pour les supprimer, plus il y aura de données à supprimer ; sans compter que ces management packs ont la facheuse tendance à noyer la base de données et à provoquer le redouté événement 2115 (mais c’est une autre histoire…).

Heureusement, il existe une procédure stockée qui permet de supprimer un Management Pack et toutes les données qu’il a enregistrées dans la base, sans craindre de timeout : p_ManagementPackRemove.

Afin de l’exécuter, il est d’abord nécessaire de récupérer l’id du management pack à supprimer :

SELECT ManagementPackId,MPName
FROM [OperationsManager].[dbo].[ManagementPack]
where MPName like ‘%MP à supprimer%’

Puis la procédure s’appelle simplement avec la requête suivante :

exec [dbo].[p_ManagementPackRemove] ‘ManagementPackId‘
go

Il ne vous reste alors plus qu’à patienter… à titre d’exemple, la suppression du MP Dell (Detailed) sur un environnement contenant plusieurs centaines de serveurs physiques a nécessité plus de 4h !

SCOM – Utiliser une propriété issue du workflow dans une Recovery

Au programme de cet article, une nouvelle astuce méconnue : comment utiliser une propriété issue du changement d’état d’un moniteur dans une tâche de remédiation (Recovery).

Prenons pour exemple un moniteur de service tout ce qu’il y a de plus classique :

 clip_image002

Imaginons maintenant que nous ayons besoin de déclencher une Recovery lorsque ce moniteur repasse à l’état Healthy (oui, c’est parfaitement réalisable en XML même si impossible directement dans la console), par exemple pour aller écrire le Process ID de l’exécutable sous-jacent au service dans un fichier de log.

Le PID fait partie des informations contenues dans le PropertyBag qui entraine le passage du moniteur en Healthy :

clip_image004

Il faudrait donc pouvoir passer la valeur de cette propriété en entrée de notre Recovery.

On pense alors immédiatement à la notation couramment utilisée pour afficher ces valeurs dans les alertes :
$Data/Context/Property[@Name=’ProcessId’]$
Malheureusement, elle ne fonctionne pas dans le cadre d’une Recovery…

La syntaxe correcte, très peu utilisée et documentée, est en réalité la suivante :

$Data/StateChange/DataItem/Context/DataItem/Property[@Name=’ProcessId’]$

Elle résulte de la structure du MonitorTaskDataType qui encapsule un PropertyBag dans un autre lors de la circulation des données dans un workflow de moniteur, tel que le montre cet exemple sur MSDN : https://docs.microsoft.com/en-us/previous-versions/system-center/developer/ee533651(v=msdn.10)

Cette astuce ne vous servira probablement pas tous les jours, mais elle m’a déjà été bien utile… peut-être vous le sera-t-elle aussi !

SCOM - La variable $Data$ ou comment utiliser Powershell n’importe où dans un module

L’article d’aujourd’hui s’adresse en priorité aux développeurs de management packs aguerris puisqu’il nécessite que vous maitrisiez déjà des notions d’authoring avancé comme la composition de modules personnalisés, de data source, de probe et donc les concepts de property bags et de circulation des données au sein d’un module.

Si ce n’est pas votre cas, ne partez pas tout de suite : cela pourrait quand même vous donner envie d’en savoir plus sur ces sujets !

Revenons-en donc au sujet qui nous intéresse : ne vous êtes-vous jamais retrouvé frustré par l’impossibilité de récupérer un Property Bag complet dans une probe Powershell afin de le traiter dans son ensemble avant de le réinjecter dans le workflow d’un module personnalisé ?
Par exemple pour travailler sur l’ensemble des données retournées par un SnmpWalk (ma bête noire !), d’une requête SQL issue de la Datasource OleDB native ou plus généralement lorsqu’une datasource renvoie un résultat dont les propriétés sont en nombre variable et imprévisible ; pour produire à partir de ces derniers des PropertyBags formatés à votre guise afin de les réutiliser plus loin dans votre module ?

Si ce n’est pas le cas, tant mieux ! Mais cela m’est arrivé plus d’une fois… Jusqu’à ce que je me souvienne d’un article de l’illustre Daniele Grandini qui, je dois bien l’admettre, ne m’avait pas interpelé plus que ça lors de ma première lecture. Ces préoccupations étaient alors bien éloignées de mes besoins en authoring, encore modestes !
Il y met en lumière la problématique que j’exposais plus haut, et propose pour la résoudre un module de type ConditionDetection de sa propre conception qui permet de « reconstruire » un property bag complet afin de le passer dans le workflow.

S’il s’agit assurément d’un grand pas en avant, une solution encore largement supérieure proposée par « bobgreen84 » (alias Validimir Zelenov) se cache dans les commentaires de l’article : en décompilant la ProbeAction powershell native de SCOM (Microsoft.EnterpriseManagement.Modules.PowerShell.dll – SCOM n’utilise pas directement l’exécutable original de powershell présent dans toute installation de Windows lors de l’utilisation de ProbeAction powershell), il a remarqué que celle-ci disposait d’une possibilité à ma connaissance non documentée ni utilisée dans aucun management pack.

Mais avant d’aller plus loin sur cette découverte, revenons rapidement sur l’aspect d’un PropertyBag et le fonctionnement d’une Probe powershell.

Voici un PropertyBag classique, issu d’une Datasource quelconque.
Il s’agit d’un bloc de code XML formaté d’une façon bien spécifique qui permet au moteur de SCOM de faire circuler les données entre les différents composants d’un module.
Celui-ci contient les propriétés nommées Value1 et Value2, dont les valeurs sont respectivement 10 et 5 :

image

Voici maintenant une Probe powershell qui exécute un script dont le but est simplement d’additionner deux valeurs qui lui sont passées en paramètre, puis de retourner le résultat de cette addition dans un PropertyBag.
Les valeurs de ces paramètres proviennent du PropertyBag précédent et sont récupérées à l’aide de la notation habituelle $Data/Property[@Name=’nomdelapropriété’]$ .

clip_image004

Le PropertyBag résultant de l’exécution de cette probe ressemblera donc à ceci :

clip_image006

Rappelons par ailleurs que la notation  $Data/Property[@Name=’nomdelapropriété’]$ est héritée de xQuery, une notation standard permettant de naviguer et de lire des données dans un fichier xml.

Votre mémoire étant maintenant rafraichie, revenons-en au code découvert par Vladimir Zelenov :

clip_image008

Ce que dit ce code, c’est que si vous passez la variable $Data$ en paramètre de votre ProbeAction et non pas une variable utilisant la notation habituelle vue ci-dessus, ce ne seront plus les valeurs des propriétés du PropertyBag mais bien le PropertyBag complet qui sera envoyé au script !

Prenons un exemple concret :

clip_image010

Intégrée dans un module personnalisé, cette probe très simple vous permettra de copier l’intégralité du Propertybag qui y entre dans un fichier texte.
Cela peut s’avérer extrêmement utile lors du debug d’un module, lorsqu’on ne comprend pas pourquoi l’on n’obtient pas les résultats attendus !

Il est évidemment possible de créer des scripts beaucoup plus complexe, d’autant plus que Powershell permet nativement de typer un objet en tant que XML et donc de naviguer dans son arborescence.
L’exemple suivant (fictif) récupère le résultat de la requête SnmpWalk d’un tableau snmp contenant des métriques systèmes et en réorganise le contenu afin de créer des PropertyBags plus lisibles :

clip_image012

Bref, les possibilités sont infinies…A vous de jouer !

Script Powershell - Archive log files to Zip

Le script ci-dessous archive des fichiers de log plus ancien que $daysthreshold vers une archive zip existante ($zipfilepath). Il crée le sous dossier et l'archive zip horodatée si elle n'existe pas.

ArchiveLogToZip.ps1

 

###############################################################
### ArchiveLogs.ps1                                    ###
### Add Log Files older than $days to existing zip archive. ###

### Params
### $logpath: Log Folder
### $archpath: Archive Folder
### $zipfilePath: zip file path
### $daysthreshold: Treat log older than $daysthreshold days
###############################################################
 
Param(
$logpath = "C:\MyLogFolder", 
$archpath = "C:\MyLogFolder\Archive\",
$zipfilePath =  "$archpath"+"*.zip",
[int]$daysthreshold = 0 
)
 



# Test if $archpath exist
If ( -not (Test-Path $archpath)) {New-Item $archpath -type directory} 
 
# Get items to archive
$ItemsToArc = Get-Childitem -Path $logpath -recurse | Where-Object {$_.extension -eq ".log" -and $_.LastWriteTime -lt (get-date).AddDays(-$daysthreshold)}

# Log and exit if  No items to archive
If (! $ItemsToArc)
    {
    Write-Host -F Yellow "No items to archive - Check the existence of the logs"
    exit 0
    }



# If the zip file not exist, create it
if (!(Get-Item $zipfile -ErrorAction SilentlyContinue))
{
$date = $(Get-Date).ToString("MM-dd-yyyy_HH-mm-ss")
New-Item -Path "$archpath$date.zip"
$zipfile = $(Get-Item "$archpath$date.zip").FullName
}
Else
{
$zipfile = $(Get-Item $zipfilePath).FullName
}


# Create a com object that will represent the zip file
$Zip = New-Object -ComObject Shell.Application

write-progress -activity "Archiving Data" -status "Progress..." 

try
{
$ItemsToArc | foreach {$Zip.namespace($zipfile).Movehere($_.fullname,8) ; start-sleep -Seconds 3}
}
catch
{
Write-Host "Error during File add to Zip"
}

 

Script Powershell - Fonction de test de ports reseaux

Ci-dessous une fonction dont le besoin est récurrent, pour tester la disponibilité d'un ou plusieurs ports réseaux sur une ou plusieurs machines.

 test-port.ps1 (10,05 kb)

# TEST-PORT
<#
.SYNOPSIS
         CHECK AVAILABILITY OF NETWORK PORT CONNECTION ON REMOTE COMPUTERS
.OUTPUTS 
         Return Open Status and details
.PARAMETER 
        $computer: One or more target computers
        $port: One or more port to test
        $TCPtimeout: Timeout for TCP, in ms
        $UDPtimeout: Timeout for UDP, in ms
        $TCP: Precise TCP connection
        $UDP: Precise UDP connection
                 

.USAGE:
#Test-Port -computer (get-content .\servers.txt) -port 3389
#Test-Port -computer (get-content .\servers.txt) -port 80,443,23000,17990 -TCP -TCPtimeout 1000
#Test-Port -computer myserver -port 8080,8443,10443,3389,443,80,22 -TCP -UDP
#>



function Test-Port{  




 
[cmdletbinding(  
    DefaultParameterSetName = '',  
    ConfirmImpact = 'low'  
)]  
    Param(  
        [Parameter(  
            Mandatory = $True,  
            Position = 0,  
            ParameterSetName = '',  
            ValueFromPipeline = $True)]  
            [array]$computer,  
        [Parameter(  
            Position = 1,  
            Mandatory = $True,  
            ParameterSetName = '')]  
            [array]$port,  
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [int]$TCPtimeout=500,  
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [int]$UDPtimeout=500,             
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [switch]$TCP,  
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [switch]$UDP                                    
        )  
    Begin {  
        If (!$tcp -AND !$udp) {$tcp = $True}  
        
        $ErrorActionPreference = "SilentlyContinue"  
        $report = @()  
    }  
    Process {     
        ForEach ($c in $computer) {  
            ForEach ($p in $port) {  
                If ($tcp) {    
                    #Create temporary holder   
                    $temp = "" | Select hostname,Server, Port, TypePort, Open, Notes  
                    #Create object for connecting to port on computer  
                    $tcpobject = new-Object system.Net.Sockets.TcpClient  
                    #Connect to remote machine's port                
                    $connect = $tcpobject.BeginConnect($c,$p,$null,$null)  
                    #Configure a timeout before quitting  
                    $wait = $connect.AsyncWaitHandle.WaitOne($TCPtimeout,$false)  
                    #If timeout  
                    If(!$wait) {  
                        #Close connection  
                        $tcpobject.Close()  
                        Write-Verbose "Connection Timeout"  
                        #Build report  
                        $temp.hostname= [System.Net.dns]::Resolve($c).hostname
                        $temp.Server = $c  
                        $temp.Port = $p  
                        $temp.TypePort = "TCP"  
                        $temp.Open = "False"  
                        $temp.Notes = "Connection to Port Timed Out"  
                    } Else {  
                        $error.Clear()  
                        $tcpobject.EndConnect($connect) | out-Null  
                        #If error  
                        If($error[0]){  
                            #Begin making error more readable in report  
                            [string]$string = ($error[0].exception).message  
                            $message = (($string.split(":")[1]).replace('"',"")).TrimStart()  
                            $failed = $true  
                        }  
                        #Close connection      
                        $tcpobject.Close()  
                        #If unable to query port to due failure  
                        If($failed){  
                            #Build report 
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname 
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "TCP"  
                            $temp.Open = "False"  
                            $temp.Notes = "$message"  
                        } Else{  
                            #Build report
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "TCP"  
                            $temp.Open = "True"    
                            $temp.Notes = ""  
                        }  
                    }     
                    #Reset failed value  
                    $failed = $Null      
                    #Merge temp array with report              
                    $report += $temp  
                }      
                If ($udp) {  
                    #Create temporary holder   
                    $temp = "" | Select Server, Port, TypePort, Open, Notes                                     
                    #Create object for connecting to port on computer  
                    $udpobject = new-Object system.Net.Sockets.Udpclient
                    #Set a timeout on receiving message 
                    $udpobject.client.ReceiveTimeout = $UDPTimeout 
                    #Connect to remote machine's port                
                    Write-Verbose "Making UDP connection to remote server" 
                    $udpobject.Connect("$c",$p) 
                    #Sends a message to the host to which you have connected. 
                    Write-Verbose "Sending message to remote host" 
                    $a = new-object system.text.asciiencoding 
                    $byte = $a.GetBytes("$(Get-Date)") 
                    [void]$udpobject.Send($byte,$byte.length) 
                    #IPEndPoint object will allow us to read datagrams sent from any source.  
                    Write-Verbose "Creating remote endpoint" 
                    $remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any,0) 
                    Try { 
                        #Blocks until a message returns on this socket from a remote host. 
                        Write-Verbose "Waiting for message return" 
                        $receivebytes = $udpobject.Receive([ref]$remoteendpoint) 
                        [string]$returndata = $a.GetString($receivebytes)
                        If ($returndata) {
                           Write-Verbose "Connection Successful"  
                            #Build report
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "UDP"  
                            $temp.Open = "True"  
                            $temp.Notes = $returndata   
                            $udpobject.close()   
                        }                       
                    } Catch { 
                        If ($Error[0].ToString() -match "\bRespond after a period of time\b") { 
                            #Close connection  
                            $udpobject.Close()  
                            #Make sure that the host is online and not a false positive that it is open 
                            If (Test-Connection -comp $c -count 1 -quiet) { 
                                Write-Verbose "Connection Open"  
                                #Build report
                                $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                                $temp.Server = $c  
                                $temp.Port = $p  
                                $temp.TypePort = "UDP"  
                                $temp.Open = "True"  
                                $temp.Notes = "" 
                            } Else { 
                                <# 
                                It is possible that the host is not online or that the host is online,  
                                but ICMP is blocked by a firewall and this port is actually open. 
                                #> 
                                Write-Verbose "Host maybe unavailable"  
                                #Build report
                                $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                                $temp.Server = $c  
                                $temp.Port = $p  
                                $temp.TypePort = "UDP"  
                                $temp.Open = "False"  
                                $temp.Notes = "Unable to verify if port is open or if host is unavailable."                                 
                            }                         
                        } ElseIf ($Error[0].ToString() -match "forcibly closed by the remote host" ) { 
                            #Close connection  
                            $udpobject.Close()  
                            Write-Verbose "Connection Timeout"  
                            #Build report 
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname 
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "UDP"  
                            $temp.Open = "False"  
                            $temp.Notes = "Connection to Port Timed Out"                         
                        } Else {                      
                            $udpobject.close() 
                        } 
                    }     
                    #Merge temp array with report              
                    $report += $temp 
					$temp
                }                                  
            }  
        }                  
    }  
    End {  
        #Generate Report  
        $report |ft -AutoSize 
    }
}





 

Hyper-V : Arreter une VM bloquee (Version Powershell)

Bonjour à tous,

Voici une version Powershell du billet suivant : https://blog.piservices.fr/post/2014/12/17/HyperV-2012-R2-Arreter-une-VM-bloquee

Symptomes :

Des actions lancées sur vos VMs (une extinction par exemple) ne s'éxécutent pas.

Le probleme reste présent quelque soit l'outil utilisé (Hyper-V Manager, Failover Cluster Manager ou encore SCVMM).

La VM est donc dans un état instable et il peut etre necessaire de la redémarrer malgrès que celle-ci ne répond plus du tout à aucune action.

Parfois, vous pouvez avoir un status plus explicite sur l'état de la VM via le Failover Cluster Manager, celle-ci sera marquee comme Locked.

Solution :

Il faut tuer le processus VMWP.exe correspondant à la VM directement sur l'hote Hyper-V concerné.

La version Powershell a l'avantage de sa rapidité (vous pouvez le faire en RemotePS directement) ainsi que de minimiser le risque d'erreurs de manipulation (Le GUID n'étant pas un identifiant des plus lisible).

Voici les commandes :

$VMGUID = (Get-VM VM_Name).ID
$VMWMProc = (Get-WmiObject Win32_Process | ? {$_.Name -match 'VMWP' -and $_.CommandLine -match $VMGUID})
Stop-Process ($VMWMProc.ProcessId) –Force

A bientot !

Introduction à Microsoft Advanced Threat Analytics

ATA

Advanced Threat Analytics (ATA) est une plateforme OnPremise qui permet d’analyser l’ensemble des actions effectuées (accès à des ressources, verrouillage de compte, déplacement latéral…) avec des comptes Active Directory.

ATA fonctionne avec un serveur central qui récupère l’ensemble des logs des contrôleurs de domaines.

Ces logs peuvent être récupérés de deux façons :

  • Via un agent installé sur les DC (nommé Lightweight Gateway)
  • Via du port mirroring (nommé Full Gateway)

Ce billet s’intéressera uniquement à la configuration d’ATA avec des Lightweight Gateway.

Mise en place

Prérequis pour les Lightweight Gateway

Pour qu’ATA soit efficace il est impératif d’installer l’agent sur l’ensemble des DC du domaine Active Directory.

Pour la mise en place il faut

  • l’ouverture du port TCP 443 entre les DC et le serveur ATA
  • un compte de service (sans droit d’administration) présent dans l’AD
  • un certificat SSL (optionnel, il est possible d’utiliser un certificat auto-signé pour la console d’administration)

 

Installation du serveur ATA

L’installation du serveur d’administration est très simple et se fait via l’assistant d’installation.

Il est possible de paramétrer le chemin d’installation, le chemin des logs et le certificat qui sera utiliser pour la console web.

image

image

image

image

image

image

La fin de l’installation s’effectue depuis l’interface d’administration web (https://localhost depuis le serveur ATA).

Le compte de service est nécessaire à ce moment pour lire l’AD.

2019-02-01_152514

L’étape suivante consiste à télécharger le setup pour les DC.

2019-02-01_152537

2019-02-01_152547

Installation des Lightweight gateways

L’installation de l’agent sur les DC se fait à travers l’assistant d’installation. Il est possible de paramétrer le chemin d’installation.

2019-02-01_153332

2019-02-01_153525

2019-02-01_153810

2019-02-01_153830

2019-02-01_153911

Une fois l’agent installé, celui-ci démarre automatiquement et le DC est visible dans l’interface d’administration.

Il est nécessaire de modifier la configuration d’un des DC dans l’interface d’administration pour le désigner comme Domain synchronizer candidate.

Tout DC défini comme Domain synchronizer candidat peut être responsable de la synchronisation entre ATA et le domaine Active Directory.

2019-02-01_152654

Analyse

Depuis l’interface d’administration les différentes alertes remontent automatiquement. Celles-ci sont classées par niveau de criticité : High, Medium et Low.

2019-02-27_114458

Il est possible de configurer des alertes mail et/ou d’envoyer l’ensemble de ces logs dans un SIEM.