jeudi 24 janvier 2019

Détecter les modifications sur GPO et faire un versionning

Bonjour, et bonne année!

Un petit coup de mou sur le blog, mais rien d'inquiétant, juste que ces derniers temps ont été plutôt calmes en terme de création de scripts et que je n'ai pas vraiment eu à faire quoi que ce soit qui mérite de faire un article. 

Mais c'est du passé, car j'ai ENFIN (merci les joies de l'auto-projet) quelque chose à mettre pour nourrir cet espace, et je dois avouer en être plutôt fier de ce morceau de code car il m'aura offert quelques beaux petits casse-tête pour faire fonctionner le tout.

Aujourd'hui, je vais vous présenter un script qui vous permettra de :

  1. Détecter les modifications faites sur les GPO
  2. Créer un versionning des dites-GPO afin de pouvoir restaurer en cas de problème
  3. Automatiser un export des paramétrages de GPO format HTML pour une lecture facile

Alors, la première question que je mettrai à cet article est : 

POURQUOI?


 La raison est simple. Chez mon client, nous sommes plusieurs personnes à nous occuper de l'informatique et nous avons donc bien souvent à effectuer telle ou telle opération sur les politiques de groupe, mais nous n'avons pas forcément l'information de ce qui a été changé. Cela peut poser des problèmes, puisqu'une personne peut écraser ou mettre en place des paramètres qui vont à l'encontre du fonctionnement d'une modification effectuée par un collègue, voir même causer des dysfonctionnements imprévus.

Il nous fallait donc un moyen de savoir et de suivre les changements effectués. 


COMMENT?


L'idée initiale m'est venue en voyant les différents mails que nous recevions de notre wiki interne. A chaque modification sur une information, nous recevons un mail avec le changement surligné en vert et l'ancienne valeur barrée en rouge.

Exemple de modification sur un wiki
Ayant des GPO de plus en plus tentaculaires et complexes au fur et à mesure du temps, il m’apparaissait évident qu'avoir un suivi identique à celui du Wiki serait une bonne chose, car nous aurions visibilité sur ce qui existe, ce qui est modifié et ce qui est supprimé.

L'outil de base pour gérer les GPO me semblait tout trouvé (et les gens qui suivent un peu le blog ne seront pas étonnés) : Powershell ; mais l'outil pour faire le surlignage des modifications fut bien plus compliqué à trouver que prévu.

J'avais déjà en tête l'idée générale de fonctionnement du script : 
  • Sauvegarder les GPOs pour extraire leur date de modification
  • Vérifier que leur date de modification était plus récente que leur dernière sauvegarde
  • Trouver les nouvelles GPOs et les GPOs supprimées
  • Comparer les deux fichiers pour ressortir les lignes non similaires ainsi qu'un contexte pour comprendre la différence (numéro de la ligne + X lignes avant et après)
  • Génerer un fichier récapitulatif des différences
  • Supprimer les backups sans différences
  • Envoyer un mail avec toutes les infos
  • ???
  • Profit
Le vrai point problématique était : Comment comparer les deux fichiers ?

Les mettre à plat et comparer chaque ligne aurait posé un problème, puisque les fichiers de GPO sont comme des fichiers xml et l'activation d'une valeur créera un nouveau bloc de balises qui ne sera pas dans le fichier précédent, et considérera donc tout le restant du fichier comme "différent".
Comparer une valeur et la rechercher n'est pas possible non plus, puisque dans l'exemple de recherche d'une valeur "Disabled", il peut très bien y avoir 30 endroits avec cette même valeur.

Il me fallait donc passer par un outil tiers, car je ne tenais pas plus que ça à réinventer la roue.

mercredi 5 septembre 2018

Echapper des caractères HTML

Le HTML c'est bien, mais quand c'est fabriqué à la main depuis un script, c'est pas forcément le mieux... surtout quand, comme dans mon cas, on doit ressortir des extraits de XML et les afficher dans une page HTML ... tout est interprété comme des balises HTML et c'est le cirque!

Alors je me suis dit : "C'est simple... il suffit de faire des .replace() sur chaque type de caractère interprété en HTML et les remplacer par leur valeur échappée!" 
Par exemple le symbole < est à remplacer par &lt;  et ainsi de suite...

Je me suis dit que c'était tout de même un beau travail de m.....oyen-âge et qu'il devait exister une manière plus propre d'effectuer les choses!

Et il s'avère qu'après une brève recherche, la solution se trouve (comme pour beaucoup de choses concernant Powershell) du coté du C#!

Une petite commande et on échappe automatiquement nos chaînes de caractères de tout caractère qui serait interprété par la page !

Pour cela, on va appeller la classe [System.Web.HttpUtility] qui, comme vous vous en doutez, regroupe quelques utilitaires pour les pages Web. Avec une petite méthode HTMLENCODE() nous voilà avec possibilité d'encoder notre texte d'un claquement de... clavier.

Et pouf!
 Plus de problèmes :)

jeudi 19 juillet 2018

Impossible d'établir une PSSESSION sans préciser le FQDN

Petit souci devant lequel je suis tombé.

Impossible de pouvoir se connecter à une machine distante sans rentrer le fully qualified domain name. Mais si, vous savez, l'identifiant au format machine.domaine.

J'arrive sur ce message. 

Sans le fqdn :(

Alors qu'avec le FQDN, pas de problème.

Avec le fqdn :)

Après quelques recherches, et étant connecté derrière un proxy, il s'avère que la manière dont les sessions PSSESSION se connectent dépendent d'une option, que l'on trouve dans la cmdlet PSSESSIONOPTION et qui se nomme ProxyAccessType.

Par défaut, la valeur est "None".  

Ce qui signifie que dans notre exemple, voyant que ce n'est pas une machine explicitement estampillée avec le domaine, on ne va pas chercher à la résoudre puisque aucune information de connexion au proxy n'est précisée.

En changeant la valeur, on peut ordonner de tout résoudre localement.

Pour ça, et pour éviter d'avoir à le refaire à chaque fois on saisit dans le terminal powershell : notepad $profile

 On ajoute cette commande : 

$PSSessionOption = New-PSSessionOption -ProxyAccessType NoProxyServer

 
On enregistre et on ferme.

On fait de même depuis l'ISE, et on laisse derrière nous ce souci :)

En espérant que cela vous aura été utile.

mardi 9 janvier 2018

Erreur 500 sur OnCommand System Manager

Petit article sur un point qui m'aura fait me casser un peu la tête. Voici la situation...

Vous avez un controlleur Netapp sur votre lieu de travail / chez un client, et depuis peu, vous n'arrivez plus à accéder au OnCommand System Manager, car lorsque vous vous connectez -et bien que vos identifiants soient corrects- vous rencontrez cette erreur 500 : Connexion refusée.

Nope.
Difficile de savoir ce qui cause cela, étant donné que la partie "show details" est aussi locace que Bernardo essayant d'expliquer quelque chose à Stevie Wonder. (Pour ceux du fond qui n'ont pas suivi, Bernardo est le valet muet de Zorro, il ne s'exprime qu'en langue des signes, et Stevie Wonder est aveugle.)

La raison est très certainement liée à un changement de version de Java sur le poste, puisque la version 8 de java est assez stricte en terme de sécurité et n'accepte pas les certificats SSL dont le cryptage n'est pas d'au moins 2048 bits... et les clés générées par défaut pour la partie SSL de Netapp sont de 512 bits.

Pour corriger le problème, il va falloir régénérer des clés. Connectez vous en ssh sur chaque storage, et lancez la commande suivante : 

secureadmin setup ssl

Une fois renseignés les diverses informations, précisez 2048 en longueur de clé. Une fois le certificat recrée, SSL sera automatiquement redémarré avec les modifications effectuées et vous pourrez vous connecter sans problème.

 

vendredi 7 juillet 2017

Récupérer la version (32 ou 64 bits) des versions d'Office installées sur le parc informatique.

Pourquoi faire?



Dans le cadre de l'évolution du parc informatique de mon client, nous avons fait évoluer Office, passant de l'honorable (mais vieillissant) Office 2010 à la version Office 365.

Bien entendu, (car rien n'est simple dans la vie) certaines installations d'Office 2010 sont en x86, alors que d'autres sont sur du x64, et c'est un message d'incompatibilité explicite qui vous saute au visage lorsque vous cherchez à installer une version x86 de 365 sur un 2010 en x64 (et réciproquement).


Nope.



Il nous est donc nécessaire de connaitre au préalable la version d'office installée sur chaque ordinateur... Mais comment faire, oui?



Comment faire?




Solution 1, alias "Moi, J'adore bien bosser 20 fois plus pour rien" : Aller dans n'importe quel logiciel de la suite Office, ouvrir "Fichier", puis "Aide" et regarder la réponse dans la fenêtre. Vous pouvez maintenant aller sur CHAQUE ordinateur pour aller vérifier quelle version est installée.






Solution 2, alias "Oui, je suis un SysAdmin qui se respecte" : Mon script Powershell, une GPO, un répertoire public, et on peut bosser sur autre chose le temps que l'information remonte d'elle même.



mardi 11 avril 2017

Une fonction de création de password simple

Pourquoi faire?



Créer un mot de passe sécurisé est quelque chose d'important. Dans un script pour un client, j'ai crée un formulaire de création d'utilisateur afin de centraliser l'ensemble des actions nécessaires (création du compte AD, attribution des groupes de sécurité, création du partage NetApp, modification des ACL sur l'espace personnel utilisateur), mais les utilisateurs n'avaient que peu d'affection pour les mots de passe que mon script générait automatiquement.



Bon, peut être un mot de passe tel que !A@@#%75;$$ n'est pas forcément aisé à saisir, mais ce n'est qu'une seule fois... et au bout de 5 utilisateurs qui bloquent leur compte fraîchement créé, j'ai du adapter mon script pour quelque chose de plus "agréable" sans pour autant lésiner sur la sécurité.



J'ai donc crée une fonction simple mais pratique, qui permet d'avoir un mot de passe à la fois facile à retenir, à taper, et sécurisé.



Comment faire?




Nous créons tout d'abord un dictionnaire de mots, 26 séries de 3 mots, pour 26 lettres, et un nombre aléatoire entre 0 et 9999, toujours affiché sur 4 chiffres. Nous sélectionnons deux mots, ainsi qu'un chiffre, et nous affichons cela dans un ordre aléatoire.



Script final.





function Create-Pass {
    $result = New-Object System.Collections.ArrayList
    $dictionnaire = New-Object System.Collections.ArrayList
    $dictionnaire.AddRange(@(
    @("Arbre","Amont","Assiette"),
    @("Bouleau","Bateau","Barque"),
    @("Chenille","Canard","Client"),
    @("Drap","Domaine","Dolmen"),
    @("Echelle","Escabeau","Evident"),
    @("Frigo","Froid","France"),
    @("Graphique","Grand","Grive"),
    @("Hirondelle","Hibou","Herbe"),
    @("Ignam","Invite","Isidore"),
    @("Jeudi","Jaune","Jeune"),
    @("Koala","Kabuki","Kaki"),
    @("Lama","Loutre","Large"),
    @("Marsouin","Monstre","Michel"),
    @("Nouveau","Nature","Noire"),
    @("Orange","Oubli","Organe"),
    @("Pierre","Poutre","Pieds"),
    @("Quatre","Quand","Quatuor"),
    @("Rouge","Ranger","Rongeur"),
    @("Sophie","Salon","Super"),
    @("These","Tamis","Truelle"),
    @("Unique","Usage","Ultime"),
    @("Verre","Voir","Verifier"),
    @("Wapiti","Wagon","Walter"),
    @("Xenon","Xanthie","Xeres"),
    @("Yack","Yacht","Yaourt"),
    @("Zebre","Zouave","Zeste")
    ))
    #On crée un nombre au hasard
    $rand = "{0:D4}" -f (Get-Random -Minimum 0 -Maximum 9999)
    #On prend une lettre au hasard, et un indice entre 0 et 3 pour un mot aléatoire dans la lettre choisie, on ajoute la selection à la liste
    $result.Add($dictionnaire.Item($(Get-Random -Minimum 0 -Maximum $($dictionnaire.Count)))[$(Get-Random -Minimum 0 -Maximum 3)]) | Out-Null
    #On recommence, pour avoir 2 mots
    $result.Add($dictionnaire.Item($(Get-Random -Minimum 0 -Maximum $($dictionnaire.Count)))[$(Get-Random -Minimum 0 -Maximum 3)]) | Out-Null
    #On ajoute le nombre aléatoire
    $result.Add($rand) | Out-Null
    $password = $null
    0..2 | foreach {
        #On selectionne une entrée au hasard dans la liste précedente des elements choisis aléatoirement
        $index = get-random -Minimum 0 -Maximum $($result.Count)
        #On ajoute le mot choisi à la chaine de résultat
        $password += $result[$index]
        #On retire l'objet ajouté de la liste pour pouvoir tirer un nouvel élement à ajouter
        $result.RemoveAt($index)

    }
 return $password

}



Le résultat est le suivant :


Hop! Des mots de passe!


Si vous souhaitez enregistrer le mot de passe dans une variable, rien de plus simple.


Hop! Un mot de passe enregistré!

Et si vous souhaitez convertir le mot de passe aléatoire en type sécurisé utilisable pour une action sécurisée (comme l'attribuer à un compte Windows)

Hop! Un mot de passe sécurisé!

mercredi 1 février 2017

Modifier le déroulement d'un script avec une touche clavier.

Pourquoi faire?



L'un de mes scripts est utilisé pour extraire des données du site de ma société où sont enregistrés les tickets d'incidents, afin de créer des reportings pour mon client sur le mois passé. 

Comme il se lance en tâche planifiée tous les 1 du mois, il va chercher tout seul comme un grand les dates actuelles et en déduit la date de début et date de fin.
Récemment, nous avons eu à faire des requêtes pour extraire non pas sur le mois mais sur l'année entière. Et changer à chaque fois les dates en allant dans le script n'est pas des plus pratiques. Il a fallu changer tout ça!


Comment faire?



J'ai donc recherché une manière de faire autre, afin de pouvoir différencier le lancement d'un script par tâche planifiée ou le lancement d'un script en double-cliquant sur un fichier .ps1

La solution première qui m'est apparue était de me dire qu'un appui sur une touche dans un délai imparti permettrait de savoir qu'une personne est aux commandes, et qu'on pourrait donc renseigner les dates!


Etant donné que Read-Host bloque la console, se servir de cette commande aurait également bloqué l'exécution d'un script en tâche planifiée... on oublie!



Il y'a également la fonction readkey() disponible dans la variable $host qui permet de savoir quelle touche à été utilisée!



$host.ui.RawUI.ReadKey("noecho,includekeydown")



Mais hélas, elle aussi bloque la console en attente d'une touche...



Il existe une autre propriété dans $host.ui.RawUi, nommé "KeyAvailable", qui nous renvoie un booléen en fonction de si oui ou non une touche est appuyée... et qui n'est pas bloquante!



Il nous suffit donc de marier cette propriété et la fonction ReadKey() afin de pouvoir avoir un déclencheur de sortie de boucle sur appui de touche, puis une vérification de la touche utilisée!



On mixe le tout... et voila le script en question!