Archives de catégorie : Trucs et astuces

Les permissions d’un share

Dans cet article, nous avions exploré les fonctions de Netapi32.dll pour récupérer des informations relatives aux shares SMB.

Nous allons maintenant voir comment récupérer les permissions de ces shares.

Il existe des fonctions qui gèrent les SIDs, ACLs, et ACEs dans advapi32.dll, et nous allons capitaliser sur l’article concernant le Netapi pour en faire un tout, et lire les permissions sans passer par WMI.

Tout d’abord voici une nouvelle version du script netapi.ps1:

Il faudra avec cette version dot-sourcer le script pour que les cmdlets soient définies:

 

Une fois les cmdlets définies nous allons avoir besoin de celle qui récupère le level 502 des shares:

et avec cet array, nous allons pour chaque élément vérifier si le SecurityDescriptor est présent. Pour ensuite récupérer l’ACL dans un buffer en mémoire.

A partir de l’ACL nous pouvons récupérer l’ACE qui contient le SID et la permission.

Avec un peu de gymnastique de pointeurs et buffers de structures, nous allons pouvoir traduire tout cela en objets Powershell qui peuvent être utilisés.

Voici le script complet:

L’output est simple mais peut être adapté:

Sans les mains WMI.

Voici l’article de référence en C# qui traite de ce sujet http://blogs.msdn.com/b/dsadsi/archive/2012/03/30/to-read-shared-permissions-of-a-server-resource-in-c-using-netsharegetinfo.aspx

Voici un mise à jour du script d’exemple, cette nouvelle version traite le problème de l’exception « Some or all identity references could not be translated. ».

Pour régler ce problème nous allons récupérer la liste des utilisateurs locaux d’une machine donnée et comparer les SIDs sans passer par Translate.

 

Voici le script en entier (renseigner $server en début du script):

 

Powershell DSC: trouver le nom d’une fonctionnalité Windows à configurer

Voici une cmdlet bien utile lorsqu’on développe des configurations Powershell DSC.

Imaginons que nous voulons installer la feature Windows Server Backup, afin de planifier des sauvegardes sur nos serveurs Windows.

Dans l’article précédent nous avions configuré le rôle Active Directory Services de la même façon avec le code suivant:

Or comment trouver le nom de la feature Windows Server Backup? Nous pouvons utiliser la cmdlet Get-WindowsFeature, et passer en paramètre des caractères afin de simplifier la requête.

Par exemple si nous voulons voir toutes les fonctionnalités commençant par Windows, ainsi que les fonctionnalités DSC, nous pouvons passer un array, comme ceci:

Le X entre les crochets veut dire que l’option est déjà installée.

On voit aussi le nom de l’option Windows Server Backup, qu’on peut renseigner dans une ressource Powershell DSC:

Et voilà.

Personnaliser l’image d’arrière-plan du Bureau

Voici comment personnaliser l’arrière-plan du bureau avec Powershell, sans avoir ni  à rebooter ni à se relogger.

Nous allons nous servir d’une fonction qui se trouve dans User32.Dll et qui s’appelle SystemParametersInfo et la déclarer dans un namespace qu’on appellera Params  :

Ensuite nous définissons quelques constantes:

L’opération est un changement de l’image en arrière plan du bureau:

Les paramètres sont un changement du fichier INI qui déclare l’image à placer ainsi que l’envoi d’un événement Windows qui alertera Explorer du changement. Explorer gère l’image d’arrière plan.

On combine ces deux valeur avec un bit OR pour créer un bitmask.

Définissons maintenant l’image qu’on veut charger dans une variable:

Finalement on appelle la fonction SystemParamInfo sans oublier le namespace [Params]:

Voici le script en entier:

Pour plus d’info voir ce lien vers MSDN.

Partager ce contenu

Présence d’un paramètre

Voici comment vérifier la présence d’un paramètre pour une fonction, ou un script.

Généralement on décrit une fonction de la façon suivante:

Dans le cas de la fonction MaFonction il peut être utile de vérifier si le paramètre $MyBool (My Bool, pas ma boule! ) est présent ou non.

Pour ceci il faut s’appuyer sur la variable automatique $PSBoundParameters dont la méthode ContainsKey retourne un booléen.

Une variable automatique, est une variable créée et gérée par Powershell, il en existe de toutes sortes et permettent par exemple de connaitre par exemple la disposition du stack d’appel des fonctions, ou la ligne du script en cours d’exécution, ou encore le résultat de la dernière commande.

Voici un exémple:

Partager ce contenu

Les performances d’un tri d’array

Le tri d’array permet de réordonner les éléments d’un array en fonction de la valeur d’un élément. On trie en ordre croissant ou décroissant ceci pour rendre plus lisible ces données.

Lorsque un array contient des centaines de milliers d’éléments il est important de comprendre les différentes techniques de triage ainsi on peut optimiser les performances.

Pour les besoins de cet article, créons un array avec 2500 éléments. Pour cela nous créons un object de type PSCustomObject avec un attribut et nous répétons l’opération 2500 fois pour créer l’array:

Maintenant effectuons différents triages en mesurant les performances.

Commençons en utilisant la cmdlet Sort-Object:

Le temps mesuré en secondes est de: 0.1217965

Maintenant au lieu d’utiliser la cmdlet Sort-Object, utilisons la méthode Reverse() de la classe [Array]:

 

Le temps mesuré en secondes est de: 0.0002594

Une énorme différence!

Créons maintenant une collection de type System.Collections.ArrayList. Les collections contrairement aux arrays classiques ont une particularité, elles sont modifiables, alors que les array de type System.array ne le sont pas, on les appelle immuables. Ceci veut dire que pour ajouter un élément à un array on doit en créer un nouveau objet. Alors que pour une collection, on peut allégrement ajouter et enlever des éléments en utilisant toujours le même objet.

Effectuons maintenant un tri et mesurons le temps:

Le temps mesuré en secondes est de: 0.0002459

Encore une amélioration par rapport à [array]::Reverse(), mais pas si significative que ça.

Nous n’avons pas mesuré le temps passé à créer les collections et array. Et la création d’une collection sera toujours plus rapide. Si nous avons des centaines de milliers d’éléments à traiter une collection sera plus rapide qu’un array.

Partager ce contenu

Attraper une exception en Powershell

Avec Powershell il est possible d’attraper des exceptions. Une Exception est un signal qu’une commande envoie lorsque une erreur se produit.

Ce qui est intéressant avec cette approche est que nous pouvons regrouper une série de commandes dans un script block, et attraper toutes les erreurs d’une seule et même façon.

Sans ça nous devrions vérifier chaque commande et agir en conséquence ce qui augmenterait considérablement la quantité de code à écrire.

On utilise les exceptions avec le trio: Try, Catch, Finally

  1. Try (Essayer), permet d’essayer toutes les commandes dans un script block.
  2. Catch, récupère toutes les erreurs produites par les commandes dans le script block
  3. Finally, permet d’exécuter un script block après que le catch ait fini de traiter les exceptions reçues. (non traité dans cet article)

Voici un exemple:

Dans cet exemple nous listons les fichiers dans le répertoire actuel, nous testons si « localhost » répond au ping, et nous créons le répertoire « c:\temp ».

Toutes ces commandes peuvent avoir des erreurs indépendantes, et pour les attraper, nous allons continuer le code avec la partie catch.

Maintenant nous pour toute exception attrapée nous pouvons afficher le message.

Imaginons maintenant que nous voudrions être plus fins.

Par exemple, la commande New-Item peut produire une exception si le répertoire C:\TEMP existe déjà. Et nous voudrions customiser le message que dans ce cas là.

Tout d’abord il faut trouver la bonne exception, et ce n’est pas simple, mais voici un truc.

On indique tout d’abord à Powershell de s’arrêter si une erreur se produit (normalement il continue) avec la commande suivante:

Ensuite on reproduit l’erreur, dans notre cas on crée le répertoire deux fois pour que la deuxième fois une exception se produise.

Une fois l’exception reçue, on peut regarder son nom exact avec ceci:

Notre exception particulière qui apparaît lorsque on essaye de créer un dossier qui existe déjà est : System.IO.IOException.

Du coup on peut faire un catch dédié à ce type d’exceptions avec ceci:

Notre exception IO sera attrapée par son catch individuel et pourra être traitée à sa façon.

 

Measure-Time

Une question StackOverflow était la suivante: Comment peut-on faire pour avoir l’équivalent de la commande time de Linux/Unix sur Windows à travers Powershell.

Cette commande permet de savoir le temps que passe une expression ou une autre commande en mode User et mode System. Dans le monde Windows le mode System est le mode Kernel, pendant lequel, si une erreur se produit, la machine fait un crash dump (le fameux blue screen of death).

La première réaction est bien sûr d’utiliser la cmdlet Measure-Command, qui permet mésurer le temps qu’une commande utilise pour s’exécuter.

Or Measure-Time ne mesure pas plus en détail le temps Kernel versus le temps User: ça ne donne que l’elapsed time.

Du coup nous allons créer notre propre cmdlet pour simuler cette commande time.

On appellera cette cmdlet Measure-Time.

La cmdlet utilise du C# pour importer la fonction GetProcessTimes depuis Kernel32.DLL. Elle va également démarrer un process avec la commande passée en paramètre, et une fois le process terminé elle pourra à travers GetProcessTimes mesurer les temps passés en mode Kernel, User et l’elapsed time.

J’ai ajouté un switch appelé -Silent qui permet de cacher le output généré par la commande.

Partager ce contenu

WMI événements permanents

Windows Management Instrumentation (WMI) est une technologie Microsoft permettant de manager des serveurs et postes de travail distants à travers des scripts ou de la programmation.

Une des fonctionnalités de WMI qui est assez méconnue est la possibilité de créer de événements permanents.

Ces événements sont démarrés lorsque les conditions d’une query WQL sont remplies.

Dans cet article nous allons voir comment on peut mettre en place un événement permanent qui démarre lorsque une clé USB est insérée dans l’ordinateur.

Les événements permanents sont constitués de trois éléments:

  • Un filtre qui définit la query à exécuter.
  • Un consommateur (ou consumer), qui va consommer l’événement en exécutant un programme ou un script lorsque le filtre attrape l’événement.
  • Une liaison (ou bind) qui lie le filtre au consommateur.

Créons un filtre:

Définissons le langage pour la query:

Définissons la query:

Dans notre cas, la query sélectionne tous les événements dans la liste des événements de créations mais filtre pour récupérer que ceux qui concernent la création de disques logiques (win32_logicaldisk) et ceci toutes les 5 secondes.

Donnons un nom à notre filtre et définissons son espace de nommage:

Finissons la création du filtre, mais récupérons également son chemin d’accès, car il s’agit d’un filtre permanent, c-a-d définitif. Il restera même si on redémarre la machine. Nous avons besoin du chemin d’accès pour le supprimer.

Notre filtre créé, regardons maintenant le consommateur.

Définissons la commande à exécuter

Dans notre cas, nous allons invoquer un script Powershell qui regardera la lettre du lecteur USB. Si cette lettre n’est pas « O: » le script changera la lettre utilisée.

Définissons le path et l’exécutable pour le programme à démarrer:

Sauvegardons notre consommateur et récupérons le path;

Voilà notre consommateur et notre filtre sont prêts. Créons le lien entre les deux:

Indiquons le filtre et le consommateur:

Sauvegardons le lien et récupérons le path:

Regardons maintenant le script reassignDriveLetter.ps1.

En gros ce script va regarder la liste des drives sur l’ordinateur, et trouver celui est un drive USB. Si la lettre du drive n’est pas « O: » il la changera:

Si l’on veut supprimer ces événements on peut utiliser leur path:

 

Voici le script complet:

 

Partager ce contenu

La gestion des jobs Powershell

Une fonction fondamentale de Powershell est la possibilité d’exécuter des commandes en parallèle à travers les jobs.

Lorsqu’on écrit des scripts, les commandes s’exécutent au fur et à mesure, de façon séquentielle. Mais il est parfois souhaitable d’exécuter les commandes en parallèle afin d’optimiser l’exécution dans son ensemble.

Par exemple si je devais auditer les ACL de tous les shares sur un serveur de fichier, je pourrais considérer  de créer un job pour chaque share et ainsi exécuter les audits en parallèle.

Ou si je devais convertir des fichiers d’un format vers un autre, la parallélisation devient intéressante, car je pourrais ainsi utiliser l’hyperthreading de mon CPU .

Par contre qui dit jobs en parallèle dit gestion des jobs, car si j’ai 10.000 shares à parser, je ne veux pas créer 10.000 jobs en parallèle, mais contrôler le nombre de jobs qui s’exécutent.

Imaginons une fonction appelée Convert-File qui prend en paramètres des variables propres à la conversion de fichiers.

Je peux l’appeler de façon séquentielle

Dans ce cas je dois attendre que file1 soit converti pour commencer la conversion de file2.

Je vais alors modifier ma fonction pour qu’elle crée des jobs Powershell.

J’utilise Start-Job en créant un scriptblock, ou bloc de code, à qui je passe les arguments en paramètre à travers -ArgumentList. Ceci est nécessaire car les variable $density, $quality, $source et $target sont des variables locales et elles appartiennent à la console Powershell actuelle, en créant un job, je crée une nouvelle console, et donc un nouveau contexte et donc mes variables locales ne seront pas visibles. Pour les référencer dans le script block on peut utiliser les éléments de $args, qui est l’array passée à travers  -ArgumentList. L’ordre sera le même que celui créé, donc attention à ça.

A chaque fois que j’appelle Convert-File un job est créé. Et ceci peut poser problème car aucune gestion des jobs en cours n’est faite. Du coup si j’appelle ma fonction énormément de fois, mon système risque d’avoir des problèmes. Nous avons besoin d’un peu de gestion.

Pour récupérer la liste des jobs démarrés, nous pouvons utiliser la cmdlet Get-Job. Nous pouvons envoyer le résultat de Get-Job vers Where-Object à travers un pipe pour vérifier la variable membre State, qui nous dit si le job est Running, ou Completed. Ceci nous donnera la liste des jobs en cours d’exécution.

$running est un array qui possède un compteur que nous pouvons vérifier pour savoir combien de jobs sont en cours d’exécution.

Avec cette information nous pouvons limiter le nombre de jobs. Car nous pouvons attendre qu’un de ces jobs soit fini pour démarrer le suivant.

On fera ceci avec Wait-Job en utilisant le paramètre -Any, ce qui veut dire que dés que un des jobs actuellement en cours est terminé, Wait-Job redonne la main.

Donc si on crée une boucle, on vérifie que le nombre de jobs en cours n’est pas supérieur à une variable qui définit le nombre de jobs maxi et attend qu’un de ces jobs soit terminé avant de démarrer le suivant.

Voici une vue globale de la fonction qui gère les jobs:

Quand j’utilise des jobs, je termine toujours le script par

Ceci permet de s’assurer que tous les jobs sont bien finis avant de continuer ainsi que de récupérer les outputs de toutes les consoles et ainsi savoir si tout s’est bien passé.

Partager ce contenu

Conversion vers Hex

Il est parfois nécessaire de transformer une valeur décimale (base 10) en hexadécimale (base 16) pour les besoins d’un outil.

Par exemple l’outil EMCACL est utilisé pour placer le SID d’un compte Isilon sur un share SMB. Or cet outil requiert le SID en hexa.

Voici un bout de code qui permet de transformer une chaîne de caractères contenant un nombre décimal en hexa.

On crée une variable avec un nombre quelconque qu’on appelle $a.

On utilise la méthode ToString de la classe Convert qui prend 2 paramètres, une variable de différents types et un format désiré. Pour l’exemple j’ai utilisé un string. Le 16 c’est pour hexa.

On prend le résultat qu’on met en majuscules, car par défaut il sera en minuscules.

Et on l’affiche.

Voici le code.