PI Services

Le blog des collaborateurs de PI Services

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 !

Active Directory - Accéder aux principaux outils d'administration AD en ligne de commande

Marre de devoir attendre que le bureau Windows Server 2016/2019 se charge pour chercher votre console d'administration ou peur que le simple fait d'ouvrir la recherche Windows de Windows Server 2012 freeze votre session ?

Voici la liste des commandes les plus utiles pour accéder aux outils d'administration Windows :

 

Active Directory Domains & Trust DOMAIN.MSC
Active Directory Sites & Services DSSITE.MSC
Active Directory Users & Computers DSA.MSC
Certificates snap-in ERTMGR.MSC
Certification Services CERTSRV.MSC
Command Prompt CMD.EXE
Computer Management COMPMGMT.MSC
Device Manager DEVMGMT.MSC
DHCP Manager DHCPMGMT.MSC
Disk Defragmenter DFRG.MSC
Disk Management DISKMGMT.MSC
Distributed File System DFSGUI.MSC
DNS Manager DNSMGMT.MSC
Domain Controller Security Policy DCPOL.MSC
Domain Security Policy DOMPOL.MSC
Event Viewer EVENTVWR.MSC
Hardware and software configuration information MSINFO32.EXE
Internet Authentication Service IAS.MSC
Internet Information Service (\Windows\system32\inetsrv) INETMGR
local Group Policy Editor GPEDIT.MSC
Local Security Policy SECPOL.MSC
Local Users and Groups LUSRMGR.MSC
Microsoft Management Console MMC.EXE
Performance Monitor PERFMON.MSC
Remote Desktop MSTSC
Resultant Set of Policy RSOP.MSC
Routing and Remote Access RRASMGMT.MSC
Run Registry Editor REGEDIT.EXE
Service Configuration SERVICES.MSC
Shared Folders FSMGMT.MSC
Terminal Services TSCC.MSC

Réseau - Trouver le mot de passe d'un réseau wifi précédement utilisé

À quoi cela sert-il ?

En réalité, il y a peu de cas pratiques ou trouver le mot de passe d'un réseau wifi précédemment utilisé est utile, car bien souvent, celui-ci étant sauvegardé, il n'y a pas besoin de l'entrer à nouveau.

Alors à quoi cela sert-il ? C'est avant tout pour mettre en relief que certaines cmdlets Windows sont peu sécurisées et révèlent beaucoup d'informations avec peu de privilèges, ce qui est bien évidemment le combo préféré des hackers.

 

Les commandes

 

Les commandes ci-après sont exécutés avec un utilisateur qui n'est ni administrateur de la machine ni en mode administrateur

La première commande permet d'afficher les profiles auquel la machine s'est déjà connecté

C:\WINDOWS\System32>netsh wlan show profiles
 
Profiles on interface Wi-Fi:
 
Group policy profiles (read only) #Dans le cas d'un PC dans un domaine Active Directory les profiles déployés par GPO sont affichés ci-après
---------------------------------
    Profile_entreprise
    Profile_entreprise2
 
User profiles #Les profiles sont affichés ci-après
-------------
    All User Profile     : MonWifi
    All User Profile     : Faux_reseau
    All User Profile     : Toto

 

 

Cette commande va afficher le mot de passe du profile appelé en clair

C:\WINDOWS\System32>netsh wlan show profiles MonWifi key=clear

Profile MonWifi on interface Wi-Fi:
=======================================================================

Applied: All User Profile

Profile information
-------------------
    Version                : 1
    Type                   : Wireless LAN
    Name                   : MonWifi
    Control options        :
        Connection mode    : Connect automatically
        Network broadcast  : Connect only if this network is broadcasting
        AutoSwitch         : Do not switch to other networks
        MAC Randomization  : Disabled

Connectivity settings
---------------------
    Number of SSIDs        : 1
    SSID name              : "MonWifi"
    Network type           : Infrastructure
    Radio type             : [ Any Radio Type ]
    Vendor extension          : Not present

Security settings
-----------------
    Authentication         : WPA2-Personal
    Cipher                 : CCMP
    Authentication         : WPA2-Personal
    Cipher                 : GCMP
    Security key           : Present
    Key Content            : MonMotDePasseEnClair #Le mot de passe est affiché en clair

Cost settings
-------------
    Cost                   : Unrestricted
    Congested              : No
    Approaching Data Limit : No
    Over Data Limit        : No
    Roaming                : No
    Cost Source            : Default

 

Quels sont alors les moyens de se prémunir de cette vulnérabilité ? Les méthodes sont diverses, telles que l'utilisation du 802.1X ou un changement régulier du mot de passe.


Pour aller plus loin

 

Vidéo youtube qui propose un script python pour automatiser la récupération des mots de passe de tous les profils d'un ordinateur

 

PowerShell - Les bienfaits du splatting

Ça pique les yeux

 

Vous avez peut être déjà eu recours à la commande (cmdlet) suivante pour créér un compte dans l'Active Directory (AD) :

New-ADUser -Name $Name -samAccountName $samAccountName -userPrincipalName $UserPrincipalName -Path $Path -AccountPassword $AccountPassword -DisplayName $DisplayName -GivenName $GivenName -sn $sn -Description $Description -Server $Server -ChangePasswordAtLogon -Enabled

 

Oui ca fait mal aux yeux, mais pourquoi ne pas utiliser le caractère backtick ` qui permet de scinder une cmdlet sur plusieurs lignes ?

New-ADUser -Name $Name -samAccountName $samAccountName 
` -userPrincipalName $UserPrincipalName
` -Path $Path -AccountPassword $AccountPassword 
` -DisplayName $DisplayName -GivenName $GivenName -sn $sn
` -Description $Description -Server $Server
` -ChangePasswordAtLogon -Enabled

 

Effectivement, c'est mieux, mais cela n'est toujours pas très esthétique, peut-on faire encore mieux ?

 

Le splatting

 

Le splatting permet de passer à une cmdlet un hash table (tableau associatif), array (tableau) ou une liste d'arguments qui va contenir la liste des paramètres voulus

$parameters = @{ #Ceci est un hash table qui va associer à gauche du signe égal le nom du paramètre avec à droite la valeur du dit paramètre
    Name = $Name
    samAccountName = $samAccountName
    userPrincipalName = $userPrincipalName
    Path = $Path 
    AccountPassword = $AccountPassword 
    DisplayName = $DisplayName
    GivenName = $GivenName
    sn = $sn
    Description = $Description
    Server = $Server
    ChangePasswordAtLogon = $true
    Enabled = $true
}

New-ADUser @parameters #Pour utiliser le splatting il faut passer en paramètre le hash table à l'aide du caractère @ et non $

 

En plus de rendre le code plus lisible, les méthodes associées aux hash table sont désormais accessibles, si par exemple on veut ajouter ou retirer des éléments plus loin dans le code :

 

$parameters.add('employeeID', $EmployeeID) #Ajout du paramètre à gauche du signe , employeeID et à droite la valeur du dit paramètre dans le hashtable $parameters 
$parameters.remove('ChangePasswordAtLogon') #Suppression du paramètre ChangePasswordAtLogon du hash table $parameters

 

Pour aller plus loin

 

Documentation officielle de Microsoft sur le splatting

Azure - S'octroyer des permissions sur des souscriptions en tant qu'administrateur global

Les souscriptions, des ressources Azure aux permissions isolées

 

Le rôle Azure administrateur global (global administrateur) est le rôle absolu, il octroie le droit de vie ou de mort sur un tenant Azure, cependant, les irrésistibles souscriptions (subscriptions) n'en ont que faire et un global administrator se fera jeter comme un simple utilisateur s'il tente d'accéder à une ressource Azure auquel il n'a pas accès si tenté qu'il puisse déjà la voir, à moins que..

 

S'octroyer les permissions sur toutes les souscriptions

 

Il existe une fonctionnalité Azure qui permet à un Global admin de se conférer des droits sur toutes les subscriptions existantes et ceci en un clic, pour ce faire :

Connectez-vous à portal.azure.com avec votre compte Global admin et dans la liste déroulante de gauche, ouvrez Properties

En bas de la fenêtre dans la section Access management for Azure ressources basculez le bouton en Yes

 

Attention, ce rôle ne fournit pas le rôle Propriétaire (Owner) sur les subscriptons, il fournit le rôle User Access Administrator à la racine ce qui permet d'accéder au RBAC Azure de la subscription et de s'octroyer les droits voulus.


Pensez à retirer vos droits de la subscription avant de basculer à nouveau le bouton évoqué précédemment en No

 

Pour aller plus loin

Article officiel de Microsoft sur la délégation de permissions sur les souscriptions Azure en tant qu'administrateur global

Article officiel de Microsoft sur le RBAC Azure

Active Directory GPO - Faire du security filtering

Security filtering quésaco ?

 

Les GPO ou group policy management permettent de modifier le comportement des ordinateurs d'un parc Active Directory (AD), allant du simple changement d'écran à l'installation d'un logiciel.
Les GPO sont liés (link) à des Organizational Unit (OU) qui contiennent des objets utilisateurs et/ou ordinateurs.

Il existe des cas ou on souhaiterait appliquer une GPO à une population restreinte d'ordinateurs contenu dans une OU qui contient des ordinateurs qui ne doivent pas recevoir la GPO. C'est la que le filtre de sécurité (security filtering) entre en jeu, à travers l'appartenance à un groupe AD, on peut restreindre l'application d'une GPO aux membres du groupe.

Configurer une GPO pour faire du security filtering

 

Les étapes suivantes ne seront pas évoquées :

  • Création d'une GPO
  • Linker la GPO sur une OU
  • Ajout d'un objet utilisateur/ordinateur dans un groupe de sécurité

 

Etape 1 : modifier les security filtering de la GPO dans l'onglet Scope

 

Voici la configuration par défaut du security filtering d'une nouvelle GPO :

 

Authenticated Users indique que la GPO s'appliquera à n'importe tout objet utilisateur et/ou ordinateur qui sont contenus dans les OU ou la GPO est linkés.

 

On va donc supprimer Authenticated Users et ajouter notre propre groupe de sécurité (Group-Security-Filtering-Test) dans lequel on aura placé un object utilisateur/ordinateur.

A noter que la GPO n'a pas besoin d'être linké sur l'OU qui contient le groupe, elle doit cependant être linkée à l'OU qui contient les utilisateurs/ordinateurs du groupe.

 

Etape 2 : modifier les security filtering de la GPO dans l'onglet Delegation

 

Il faut ensuite se rendre dans l'onglet Delegation et ajouter Authenticated Users (ou Utilisateurs Authentifiés), pour rappel Authenticated Users inclut les utilisateurs et les ordinateurs, inutile donc de chercher un groupe Authenticated Computers inexistant.

 

Il nous est ensuite demander quelle permission on souhaite donner au groupe Authenticated Users on choisit Read

 

Dans l'onglet Delegation on a indiqué à quelle population a le droit de lire la GPO, c'est la configuration effectuée dans l'onglet Scope qui détermine si la GPO doit s'appliquer ou non à l'utilisateur/ordinateur

Azure PowerShell - Forcer la connexion à Azure lorsque le réseau est instable

Un peu de contexte

 

Dans certains environnements la connexion réseau vers Internet et notamment vers Azure peut présenter des défaillances.

Lorsque qu'un administrateur se connecte de façon interactive en PowerShell à Azure, un simple échec importe peu et une nouvelle tentative est souvent fructueuse, lorsque c'est une tâche automatisée qui est exécutée c'est une autre histoire, toutes les actions qui dépendent de cette connexion échouerons si la connexion vers Azure AD est un échec.


Le code

$i = 0 #Variable qui sera incrémentée à chaque nouvelle tentative de connexion à Azure
$ConnectAzureAD = "Fail" #Variable qui permet de sortir de la boucle si une tentative de connexion vers Azure AD est réussite
do #Do until permet d'exécuter un script en boucle tant qu'une condition n'est pas atteinte
{
    try #Try catch permet de choisir comment les erreurs de cmdlet seront gérés, ici on s'en sert pour attendre avant la prochaine tentative de connexion et potentiellement faire du logging
    {
        $i++ #Permet d'incrémenter la variable $i
        Connect-AzureAD #Cmdlet qui permet de se connecter à Azure AD, dans un script automatisé, les identifiants d'un compte de service seront utilisé à l'aide du paramètre -Credential $Credentials (objet PSCredential à créer)
        $ConnectAzureAD = "Success" #La connexion à Azure AD est un succès puisque nous sommes dans le try, on peut donc modifier la variable $ConnectAzureAD pour pouvoir sortir de la boucle, code qui fonctionne de paire avec la condition dans le until 
        #Il est recommandé de consacrer une ligne pour faire du logging
    }

    catch 
    {
        #A nouveau il est recommandé de consacrer une ligne pour faire du logging
        Start-Sleep 10 #Permet de faire une pause de 10 secondes avant la nouvelle tentative de connexion
    }
}
until ($ConnectAzureAD -eq "Success" -or $i -eq 6) #Si une connexion vers Azure AD est réussie ou si 6 tentatives infructueuses ont été observées, le script arrêtera d'essayer de se connecter à Azure AD

 

 

En résumé, le script tentera de se connecter à Azure AD en powershell jusqu'à ce qu'une connexion soit établie ou que 6 tentatives de connexions aient échouées.

Azure Accès Conditionnel - Les dépendances de Teams

Vous avez créé un accès conditionnel pour bloquer toutes les applications à l'exception de Teams mais Teams est toujours bloqué ?

 

Vous êtes au bon endroit, cet article vous explique tout ce qu'il y a à savoir sur les dépendances de Teams dans l'accès conditionnel Azure.

 

La configuration de l'accès conditionnel

Commençons par rappeler la configuration de l'accès conditionnel dont il est question:

Ci-dessous l'utilitaire qui permet de créer un accès conditionnel :

  • Block access
    • Cet accès conditionnel applique un refus de connexion (en opposition à grant/autoriser)
  • All cloud apps included and 1 app excluded
    • Cet accès conditionnel concerne toutes les applications Azure à l'exception d'une application
  • Exclude 
    • Permet d'exclure des applications de l'accès conditionnel
  • Microsoft Teams
    • Teams est exclu de l'accès conditionnel

 

Les dépendances de Teams

Teams est dépendant d'autres applications Microsoft : SharePoint, Exchange, Planner, etc.

Ces applications doivent être également exclues dans l'accès conditionnel pour que Teams puisse fonctionner pleinement.


A noter qu'il existe deux types de dépendances :

  • Des applications qui sont Early-bound policy enforcement
    • Cela signifie que la vérification que l'application dont dépend Teams est présente dans l'accès conditionnel se fait avant la connexion de l'utilisateur. En d'autres termes si l'application n'est pas dans la liste d'exclusion de l'accès conditionnel l'utilisateur ne pourra pas se connecter.
  • Des applications qui sont Late-bound policy enforcement
    • En opposition au Early-bound policy enforcement, l'utilisateur pourra se connecter à Teams mais ne pourra pas bénéficier des services d'une application non exclue dans l'accès conditionnel

 

Voici un schéma récapitulatif :

  • Les traits pleins sont des Early-bound policy enforcement
  • Les traits en pointillés sont des Late-bound policy enforcement

 

 

 

Pour aller plus loin


Article officiel de Microsoft sur les dépendances de Teams dans l'accès conditionnel