Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

PowerShell – Créer une fonction de logging

À l’image du chien qui est le meilleur ami de l’homme, le fichier de log est le meilleur ami de l’informaticien qui débug.

Générer des logs lorsqu’un code PowerShell s’exécute permet de savoir si une fonction a été correctement exécutée et si ce n’est pas le cas de savoir précisément à quel endroit un problème est survenu, ce qui va sans dire est d’une aide considérable lors d’un débug.

 

La fonction de log

function Get-CurrentLineNumber #Fonction qui permet de récupérer la ligne actuelle dans un script, elle sera utilisée par le script qui appelle la fonction de log
{ 
    Return $MyInvocation.ScriptLineNumber
}


function Write-Log #Fonction qu'il faudra appeler lorsque l'on voudra faire du logging
{
    [CmdletBinding()] #Déclaration des paramètres qu'il faudra fournir à la fonction pour qu'elle puisse s'exécuter
    param
    (
        [Parameter(Mandatory=$true)] #Indique que ce paramètre est obligatoire
        [ValidateNotNullOrEmpty()] #Indique que ce champ ne peut pas être vide ou null
        [string]$LogFile, #Paramètre qui contient le chemin complet du script qui appelle la fonction de log

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$LogLine, #Paramètre qui contient la ligne à laquelle la fonction de log est appelée

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$LogMessage, #Paramètre qui contient le log 

        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        [string]$LogPath, #Paramètre qui contient le chemin complet du fichier de log
 
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Information','Warning','Error')] #Valeurs disponibles pour qualifier le log
        [string]$LogSeverity #Paramètre qui va quantifier la nature du log parmi les valeurs disponibles
    )

    Begin
    {
        if (!$LogPath) #Code qui permet de générer un dossier de log ainsi que le fichier de log de façon dynamique si le paramètre LogPath est vide
        {
            $CurrentDateFormatForLog = Get-Date -Format "yyyy-MM-dd_HH" #Recupère la date du jour pour la mettre à la fin du nom de fichier de log
            $LogFolderName = "Logs" #Nom du fichier de log
            $LogFolderPath = $PSScriptRoot + "\" + $LogFolderName #Détermine dynamiquement la localisation du dossier de log qui doit se trouver dans le même dossier que le script PowerShell qui appelle la fonction de log
            
            if (!(Test-Path -Path $LogFolderPath))#Vérifie l'existence d'un dossier de log dans le même dossier que le script qui appelle la fonction de log
            {
                New-Item -ItemType Directory -Path $LogFolderPath | Out-Null #Si le dossier de log n'existe pas, le créé
            }
            
            $LogPath = $LogFolderPath + "\" + "Log_" + $CurrentDateFormatForLog + ".csv" #Détermine le nom du chemin complet du fichier de log
        }
    }
    
    Process
    {
        [pscustomobject]@{ #Génére un objet PowerShell dont chaque ligne représente une colonne du fichier de log
            Date = Get-Date -Format "yyyy-MM-dd HH:mm:ss" #Première colonne qui contient la date à laquelle la fonction de log s'est exécuté
            Severity = $LogSeverity #Deuxième colonne qui contient la nature du log
            File = $LogFile #Troisième colonne qui contient le nom du script PowerShell qui appelle la fonction de log
            Line = $LogLine #Quatrième colonne qui contient la ligne à laquelle la fonction de log a été appelée
            Message = $LogMessage #Cinquième colonne qui contient le log
            
        } | Export-Csv -Path $LogPath -Append -NoTypeInformation -Delimiter ";" -Encoding UTF8 #Code qui permet de transformer l'objet PowerShell en fichier de log (csv)
    }
}

 

Exemple d’utilisation de la fonction de log

. .\Write-Log_article.ps1 #Permet de déclarer (dot source) la fonction de log pour qu'elle puisse être utilisée

try #Appelle de la fonction de log pour indique l'utilisateur a bien été trouvé 
{
    $User = Get-ADUser -Identity "maybe" #Cherche un utilisateur dont le samAccountName est maybe
    Write-Log -LogSeverity "Information" -LogMessage "L'utilisateur a bien été récupéré" -LogFile $PSCommandPath -LogLine $(Get-CurrentLineNumber)
    #$PSCommandPath permet de trouver le chemin complet du script qui est en train de s'exécuter
    #Get-CurrentLineNumber fonction déclarée dans le script de log qui permet de récupérer la ligne courante du script qui appelle la fonction de log
}

catch #Appelle la fonction de log si l'utilisateur n'a pas été trouvé et envoie en tant que message de log l'erreur généré par la cmdlet Get-ADUser
{
    Write-Log -LogSeverity "Error" -LogMessage $_.Exception.Message -LogFile $PSCommandPath -LogLine $(Get-CurrentLineNumber) 
}

 

 

Le résultat

Une fois le script PowerShell Call_me_maybe.ps1 appelé, le dossier de log est généré

 

Le fichier de log est généré

 

Le log est généré

Office : Erreur de mise à niveau du client Office 2016 vers la version M365 2008 en utilisant le centre logiciel MECM

Scenario:

L’utilisateur avait Office 2016 32 bits installé et souhaite faire une mise à niveau vers le client M365 v2008 (semi-annual channel).

Problème:

Lors de la mise à niveau, l’erreur 0x80077562 (-2146994846) s’affiche dans le centre logiciel MECM –> Tous les produits office ont cessé de fonctionner et Office 2016 n’a pas été supprimé des programmes.

Lorsque vous essayez de désinstaller manuellement l’ancienne version d’Office 2016, cette erreur s’affiche :

« La langue de ce package d’installation n’est pas prise en charge par le système »

Explication:

Cette erreur est parfois causée par l’échec des précédentes tentatives d’installation/désinstallation du produit Office.

Solution:

1. Réinstallez Office 2016 32 bits à l’aide des sources d’installation d’Office

2. Réessayez l’installation d’Office 365 à partir du centre logiciel

NPS 2019 – Tous les paquets Radius sont ignorés

Le rôle NPS (Network Policy Server) reste le seul moyen natif d’utiliser un serveur Windows pour réaliser des authentifications RADIUS, ce qui est très utile notamment pour gérer l’authentification à des appliances réseau, des bornes Wifi ou même des utilisations plus avancées comme de l’authentification par certificat avec AlwaysOn VPN.

Bien qu’il n’ait pas évolué depuis plusieurs versions de Windows et que sa partie NAP (Network Access Protection) soit dépréciée depuis Windows 2012 R2, il reste parfaitement fonctionnel pour son rôle de serveur RADIUS.

J’ai donc récemment déployé un serveur NPS sous Windows 2019 sur un serveur flambant neuf, configuré mon client Radius (une appliance NTP) ainsi qu’une stratégie d’authentification basique pour pouvoir utiliser des identifiants de l’AD pour me connecter à l’appliance.

Malheureusement, le premier essai de connexion fut un échec. Pas d’affolement, les stratégies NPS sont souvent assez obscures et il est vite arrivé de manquer un paramètre, me dis-je… mais pas cette fois.

J’active donc les logs d’audit de connexion, ils permettent souvent d’en apprendre plus sur les raisons de l’échec. Sauf que cette fois, ils sont intégralement vides : non seulement il n’y a pas d’erreur, mais il n’y a en réalité pas le moindre évènement, comme si la requête RADIUS n’arrivait jamais au serveur.

Les règles de firewall Windows créée automatiquement lors de l’installation du rôle NPS (groupe Network Policy Server) sont pourtant bien présentes et actives, et l’administrateur réseau me confirme que les paquets arrivent bien jusqu’au serveur.

Il est donc temps de sortir Wireshark : effectivement, les paquets RADIUS arrivent bien au serveur mais ensuite rien, aucune réaction; encore une fois comme si la requête n’arrivait jamais au rôle NPS…

Après bien des tentatives infructueuses, j’essaye en désespoir de cause de désactiver le firewall Windows; et miracle, tout tombe en marche.

Le problème se situe donc au niveau du firewall. Après quelques recherches, il apparaît que les règles natives sont configurées pour ne fonctionner que pour le service IAS, ce qui est normal. Par contre, dans Windows 2019, ce service utilise un identifiant de sécurité (service SID) qui l’empêche d’être la cible d’une règle de firewall… et donc la règle ne fonctionne pas et les flux sont bloqués.

Deux solutions s’offrent donc à nous :

  • Reconfigurer le service pour retirer cette restriction à l’aide de la commande sc.exe sidtype IAS unrestricted
  • Reconfigurer les règles de firewall pour qu’elles fonctionnent avec n’importe quel service :  Get-NetFirewallRule -DisplayGroup « Network Policy Server » | where DisplayName -like « *RADIUS* » | Set-NetFirewallRule -Service Any

Après avoir exécuté une de ces deux solutions, tout devrait rentrer dans l’ordre !