Powershell et les systèmes de logs – Partie 1
Lorsque nous créons des scripts, rien ne permet de contrôler sa correcte exécution, si ce n’est de partir dans une campagne de tests longue et coûteuse. C’est pourquoi, il est important d’implémenter des logs dans ses scripts, sous forme de fichier, dans les eventlog Windows, dans une base de données, etc…
Plusieurs approches sont possibles, la plus ambitieuse serait d’utiliser Event Tracing for Windows (ETW), Log4Net offre énormément de possibilités, l’utilisation de la classe WMI win32_NTLogEvent est aussi une solution, mais je vais m’attarder sur les Eventlog de Windows ainsi que sur l’écriture d’un fichier de log. ETW, s’approche énormément du développement, ce qui n’est pas mon but, en tout cas, pas sur ce blog. Et pour l’utilisation de Log4Net, notre ami Laurent, demi dieu français du Powershell nous à concocter une splendide doc sur son fonctionnement, vous la trouverez ici. Ce framework permet de “multithreader” l’écriture de log, si vous utilisez des Jobs Powershell par exemple…
Commençons donc à regarder comment fonctionne Get-WinEvent en allant regarder la Technet et The Scripting Guy… On s’aperçoit que la classe retournée est System.Diagnostics.EventLogEntry.
En ce qui concerne Get-EventLog (Page technet et The Scripting Guy) il retourne les résultats de la classe System.Diagnostics.Eventing.Reader.EventLogRecord.
On note quelques différences sur les termes employés, mais pas vraiment de révolution, ah si ! Get-EventLog n’est pas disponible sur PSv1… La partie la plus intéressante avec le cmdlet Get-EventLog réside dans le fait qu’il soit normalement bien plus rapide, en effet il effectue les filtrages avant d’envoyer le résultat de la commande. Imaginez en remote sur un réseau, le gain de temps que cela peut représenter sur des logs système.
Vous me direz, c’est bien beau mais nous on veut écrire nos actions dans les EventLog de Windows, oui, oui j’y viendrais plus tard. Je faisais un petit tour du propriétaire et vais d’abord m’attarder sur la création d’un fichier de log classique.
Il fallait bien une base pour cette série d’article sur les logs et powershell. J’ai donc choisis de vous proposez une petite fonction perso qui est présente dans tous mes scripts.
[powershell]
function Write-Log {
param(
[Parameter(Mandatory=$true)]
[Int]$id,
[Parameter(Mandatory=$true)]
[ValidateSet(“ERROR”,”SUCCESS”,”WARNING”)]
[string]$Status,
[Parameter(Mandatory=$true)]
[String]$date,
[Parameter(Mandatory=$false)]
[String]$logfile = “C:loglog.txt”,
[Parameter(Mandatory=$false)]
[String]$server = $env:computername
)
$msg = $Reason.$id
#echo “$CurrentDate $server : $Status : $msg” >> $logfile
if ($Status -eq “ERROR”) {
Write-Error -Message “$CurrentDate $server : $Status : $msg”
if ($error) {
$error >> $logfile
$error.clear()
}
exit $id
}
elseif ($Status -eq “WARNING”) {
Write-Warning -Message “$CurrentDate $server : $Status : $msg”
}
else {
Write-Verbose -Message “$CurrentDate $server : $Status : $msg”
}
}
[/powershell]
Pour utiliser cette fonction dans votre script, vous pouvez utiliser le try/catch qui va permettre de tester chaque action et de détecter facilement une erreur.
[powershell]
$Reason = @{
1=”reason1″
2=”reason2″
3=”reason3″
4=”reason4″
5=”reason5″
6=”reason6″
#etc…
}
try {
Get-Service
Write-Log -id 1 -Status SUCCESS -date (date -format g)
}
catch {
Write-Log -id 1 -Status ERROR -date (date format g)
}
[/powershell]
L’utilité de cette fonction réside dans le fait qu’elle va vous permettre de stocker les erreurs powershell dans votre fichier de logs et d’inverstiguer en cas de besoin.
Dans la prochaine partie, on va s’attacher à rajouter des écritures dans les EventLogs Windows, ainsi que l’envoit de ces logs par mail histoire de faire une fonction propre et qui pourrait vous être utile !
@+
Salut,
vaste sujet que les logs.
>>la plus ambitieuse serait d’utiliser Event Tracing for Windows (ETW),
Il y a qq temps déjà que j’envisage d’utiliser ce mécanisme, mais par manque de temps j’avance peu sur le sujet.
Quant au try/catch il ne concerne pas les erreurs dites non-bloquantes.
Il me semble que la variable $Reason devrait être déclarée à l’extérieure de la fonction ainsi que le message, car seul l’appelant devrait être en mesure de le construire ( cf. couplage).
A moins de spécialiser cette fonction par traitement, Write-LogTraitement1, Write-LogTraitement2…
C’est un des manques de Powershell.
Je me souviens qu’on en avait déjà discuter sur le forum de powershell-scripting de ETW (que j’ai commencé à regarder, et d’ailleurs j’ai eu avec IIS des problèmes d’encodage de caractères dans un log *.etw…)
Tout à fait pour la variable $reason, le top serait même à mon avis de l’extérioriser dans un xml ou csv, pour générer un tableau excel ou autre dans la documentation qui accompagnera le script.
D’ailleurs pour appuyer cette demande d’une meilleure gestion des logs dans Powershell, il y’a une “pétition”
https://connect.microsoft.com/PowerShell/feedback/details/737041/powershell-should-have-the-ultimate-logging-system
@+
Je connaissais cette demande d’évolution, la V3 offre plus de possibilité côté redirection, mais les framework de logs restent nécessaire dés qu’on souhaite un paramètrage plus poussé.
Sinon d’autres remarques, pour le paramètre VerbosePreference, il y a une collison avec une variable automatique, ensuite -Verbose existe en tant que paramètres commun : Help about_CommonParameters
J’aurais placé le format de la date en paramètre, je pense que tu couples trop tes traitements, car comme tu le dis cette fonction provient de ton environnement, si je veux la réutiliser, je vais être amené à la modifier.
Si elle ne faisait qu’écrire un message dans une destination, l’adaptation ne serait pas nécessaire.
A mon avis, tu commences à faire du dev 😉
Corrections apportées 😉
Et non… maitriser Powershell pour un sysadmin Windows, c’est comme maitrisetr le shell pour un pingouin ! Indispensable ! Donc ce n’est pas du dev 😉