Administration de Windows

Simplifiez l’administration de la stratégie de groupe avec Windows PowerShell

Thorbjörn Sjövold

 

Vue d'ensemble:

  • Qu’est-ce que Windows PowerShell ?
  • Méthode de gestion précédente des GPO
  • Migration des scripts vers Windows PowerShell

La technologie Microsoft Group Policy n’a pas pris du jour au lendemain : elle était assez difficile à saisir et exigeait que vous adoptiez Active Directory, un étrange nouveau service qui ne ressemblait pas du tout aux domaines Compte/Ressource qui étaient la norme à l’époque. Aujourd’hui, la stratégie de groupe

est la colonne vertébrale de gestion de presque toutes les organisations dotées d’une infrastructure Windows®. J’ai le sentiment que c’est exactement ce qui arrivera avec Windows PowerShell™, la technologie de gestion de Microsoft la plus récente. En fait, Windows PowerShell va probablement faciliter de manière significative votre tâche en tant qu’administrateur de stratégie de groupe.

Dans cet article, je montre comment les API de la Console de gestion des stratégies de groupe (GPMC) de Microsoft® écrites pour les langages Windows Scripting Host comme VBScript (et les langages de script basés sur COM en général) peuvent être directement consommés à partir de Windows PowerShell pour simplifier la façon dont vous gérez la stratégie de groupe dans votre environnement.

Tâches de la stratégie de groupe des scripts

Lorsque Microsoft a lancé la GPMC il y a quelques années, les administrateurs de la stratégie de groupe ont soudain eu plusieurs fonctionnalités pratiques à leur disposition. Ceci est le cas du composant logiciel enfichable MMC centré sur la stratégie de groupe, lequel constituait un net progrès en termes de gestion de cet élément, en particulier par rapport aux utilisateurs et ordinateurs Active Directory®. De plus, il y avait une API tout neuve qui permettait aux administrateurs d’utiliser les langages basés sur COM (tels que VBScript) pour réaliser l’administration des tâches de la stratégie de groupe, comme la sauvegarde et la restauration des Objets de stratégie de groupe (GPO), leur migration entre domaines, la configuration des paramètres de sécurité sur les GPO et les liens, ainsi que l’établissement de rapports.

Malheureusement, la GPMC ne permettait pas d’éditer les véritables paramètres configurés au sein des Objets de stratégie de groupe. En d’autres termes, vous pouviez exécuter des opérations sur le conteneur de GPO, telles que lire les versions des GPO et les dates de modification, créer des GPO tout neufs, sauvegarder et restaurer/importer des GPO à partir de domaines différents, etc., mais vous ne pouviez pas ajouter ou modifier le contenu du GPO par programmation, comme par exemple ajouter un nouveau dossier redirigé ou une nouvelle installation de logiciel. Ce que vous faisiez plutôt en général était de créer le GPO, configurer manuellement tous les paramètres à l’aide de l’Éditeur d’objet de stratégie de groupe, puis de le sauvegarder et de l’importer dans un environnement de test. Lorsque tout était vérifié et fonctionnait convenablement, vous l’importiez dans un environnement réel. Même si cette fonctionnalité manquait, l’usage de scripts à la place de l’interaction manuelle avec l’API du GPMC réduisait considérablement le temps, les efforts et les erreurs dans l’administration quotidienne de la stratégie de groupe.

Prochaine étape

En quoi Windows PowerShell est-il différent des langages de script comme VBScript ? Pour commencer, Windows PowerShell est un noyau et, au moins pour ce qui nous concerne, vous pouvez comparer un noyau à un interpréteur de ligne de commande. Bien que VBScript puisse s’exécuter à partir de la ligne de commande, un fichier VBScript ne peut pas être exécuté ligne par la ligne. Un script Windows PowerShell, par contre, peut être immédiatement créé comme une série de commandes individuelles. De plus, Windows PowerShell dispose de fonctions dont le comportement s’apparente beaucoup à des sous-routines VBScript, ce qui peut être créé en temps réel au niveau de l’invite de commande de Windows PowerShell.

Encore mieux, Windows PowerShell est construit sur Microsoft .NET Framework, tandis que VBScript se base sur la technologie COM plus ancienne. Cela signifie que la vaste quantité de code .NET produite aujourd’hui peut être directement utilisée à partir de Windows PowerShell.

Pour résumer, avec Windows Powershell, vous avez une prise en charge complète du script et un mode interactif, tout en un. Les exemples que je donne ici seront tous des saisies de lignes de commande, pour que vous puissiez taper en lisant ; cependant, ils fonctionneront tout aussi bien si vous les mettez dans un fichier script Windows PowerShell et que vous exécutez ce dernier.

Recréez d’anciens scripts à l’aide de Windows PowerShell

La dernière chose que vous voulez faire lorsque vous commencez à utiliser une nouvelle technologie est de vous débarrasser du travail que vous avez déjà accompli. Il existe trois approches pour accéder à des objets COM à partir de l’API de la GPMC ou simplement pour réemployer l’ancien VBScript. Vous pouvez sélectionner l’une de ces trois options :

  • Créer un cmdlet Windows PowerShell en utilisant un langage de programmation, tel que C# ou C++ géré.
  • Utiliser Windows PowerShell pour accéder au ScriptControl dans MSScript.ocx pour encapsuler de vieux scripts.
  • Encapsuler les appels de COM dans des fonctions Windows PowerShell réutilisables ou appeler directement les objets COM.

Je vais me focaliser principalement sur la troisième option, mais penchons-nous d’abord rapidement sur toutes les options.

Création d’un cmdlet Windows PowerShell

Microsoft a inclus un grand nombre de cmdlets dans Windows PowerShell, vous permettant de copier des fichiers, formater les résultats, récupérer la date et l’heure, etc., mais vous pouvez également créer vos propres cmdlets. Ce processus est entièrement décrit à l’adresse msdn2.microsoft.com/ms714598.aspx. Pour résumer, voici la marche à suivre :

  • Créez une DLL de bibliothèque de classes dans un langage de programmation .NET, tel que C#.
  • Créez une nouvelle classe et héritez du cmdlet de la classe de base.
  • Définissez des attributs qui déterminent le nom, l’usage, les paramètres d’entrée, etc., puis ajoutez votre code.

Étant donné que Windows PowerShell est construit sur .NET Framework, tous les types (que ce soit une chaîne, un objet, etc.) renvoyés ou passés comme des paramètres ont exactement le même code que dans Windows PowerShell ; aucune conversion spéciale n’est requise.

Le véritable avantage de cette solution est que vous avez un langage de programmation complet à votre disposition.

Encapsulage d’anciens scripts à l’aide de l’objet ScriptControl de MSScript.ocx

Pour exécuter un fichier VBScript, vous avez bien sûr besoin du moteur VBScript. Ce qui est moins évident est que ce moteur est un objet COM et, puisque vous pouvez utiliser des objets COM à partir de Windows PowerShell, vous pouvez invoquer le moteur VBScript. Voici ce à quoi cela pourrait ressembler :

$scriptControl = New-Object -ComObject ScriptControl
$scriptControl.Language = ‘VBScript’
$scriptControl.AddCode(
    ‘Function ShowMessage(messageToDisplay)
    MsgBox messageToDisplay
    End Function’)
$scriptControl.ExecuteStatement(‘ShowMessage
    “Hello World”’)

Si vous entrez ce code dans l’Interface de ligne de commande (CLI) de Windows PowerShell, la fonction ShowMessage de VBScript est appelée et exécutée avec un paramètre, entraînant l’affichage d’une Boîte de message indiquant Hello World.

Maintenant, certains d’entre vous pourraient penser : « Génial ! Je sais désormais utiliser COM à partir de Windows PowerShell. Je peux arrêter de lire cet article et commencer à remplir l’objet ScriptControl avec mon ensemble de vieux scripts de la GPMC ». Malheureusement, ce n’est pas le cas. Cette technique devient rapidement très complexe et difficile à déboguer dès que la taille des scripts augmente.

Encapsulage des objets COM

La meilleure option est donc la troisième : Encapsuler les appels COM dans des fonctions Windows PowerShell réutilisables, ce qui vous permet de consommer les objets COM dans l’API de la GPMC. La ligne ci-dessous montre comment créer un objet .NET directement dans Windows PowerShell. Dans ce cas, c’est un objet FileInfo qui peut être utilisé pour obtenir la taille du fichier :

$netObject = New-Object System.IO.FileInfo(
“C:\boot.ini”) # Create an instance of FileInfo 
               # representing c:\boot.ini

Notez que # est utilisé dans Windows PowerShell pour les commentaires incorporés. À l’aide de cet objet FileInfo nouvellement instancié, vous pouvez obtenir facilement la taille de boot.ini en tapant simplement le code suivant :

$netObject.Length # Display the size in bytes of the
                  # file in the command line interface

Mais attendez, n’étions-nous pas censés ne pas parler des objets COM et de la conversion VBScript ? Oui, mais regardez la commande suivante :

$comFileSystemObject = New-Object –ComObject Scripting.FileSystemObject

Vous remarquerez que la syntaxe est en gros identique à celle que j’ai utilisée précédemment pour créer des objets natifs à partir de .NET Framework, à deux différences près. D’abord, j’ai ajouté le commutateur -ComObject qui fait pointer Windows PowerShell vers le monde COM au lieu du monde .NET. Ensuite, j’utilise un COM ProgID au lieu d’un constructeur .NET, dans ce cas Scripting.FileSystemObject. Le ProgID a le même nom que vous avez toujours utilisé. Dans VBScript, l’équivalent serait :

Set comFileSystemObject = CreateObject(
    “Scripting.FileSystemObject”)

Pour obtenir la taille de fichier en utilisant VBScript, ajoutez la ligne ci-dessus à un fichier avec le code suivant :

Set comFileObject = comFileSystemObject.GetFile(
    “C:\Boot.ini”)
WScript.Echo comFileObject.Size

Ensuite, exécutez-la à l’aide de Cscript.exe, par exemple. Dans Windows PowerShell, vous le feriez comme ceci (directement à partir de la ligne de commande Windows PowerShell si vous le souhaitez) :

$comFileObject = $comFileSystemObject.GetFile(
    “C:\boot.ini”)
$comFileObject.Size

Bien sûr, pour convertir un VBScript qui lit la taille d’un fichier, j’aurais pu utiliser le cmdlet Windows PowerShell qui gère les objets dans des lecteurs, mais j’ai voulu vous montrer combien il est facile d’accéder à COM à partir de Windows PowerShell. Notez que même si je dis à Windows PowerShell de créer un objet COM, l’objet qui est en fait créé, ici $comFileSystemObject, est un objet .NET qui encapsule l’objet COM et expose son interface. Cependant, pour ce qui nous intéresse dans cet article, cela n’a pas vraiment d’importance.

Windows PowerShell en action

Maintenant que vous avez vu comment accéder à COM à partir de Windows PowerShell, centrons-nous sur la stratégie de groupe. Les exemples décrits ici montreront des extraits de code courts pour vous donner une idée de l’utilisation des API de la GPMC à partir de Windows PowerShell, mais vous trouverez un ensemble complet de fonctions Windows PowerShell dans le téléchargement du code associé à cet article, à l’adresse technetmagazine.com/code07.aspx. La figure 1 liste les fonctions comprises dans ce téléchargement.

Figure 1 Fonctions de personnalisation du téléchargement

Nom de la fonction Description
BackupAllGpos Sauvegarde tous les GPO dans un domaine
BackupGpo Sauvegarde un seul GPO
RestoreAllGpos Restaure tous les GPO d’une sauvegarde vers un domaine
RestoreGpo Restaure un seul GPO d’une sauvegarde
GetAllBackedUpGpos Récupère la dernière version des sauvegardes de GPO d’un chemin donné
CopyGpo Copie les paramètres d’un GPO dans un autre GPO
CreateGpo Crée un nouveau GPO vide
DeleteGpo Supprime un GPO
FindDisabledGpos Renvoie tous les GPO où l’utilisateur et la partie informatique sont tous deux désactivés
FindUnlinkedGpos Renvoie tous les GPO qui n’ont pas de lien
CreateReportForGpo Crée un rapport XML pour un seul GPO dans un domaine
CreateReportForAllGpos Crée un rapport XML séparé pour chaque GPO dans un domaine
GetGpoByNameOrID Trouve un GPO par son nom d’affichage ou son identifiant
GetBackupByNameOrId Trouve une sauvegarde de GPO par son nom d’affichage ou son identifiant
GetAllGposInDomain Renvoie tous les GPO dans un domaine

Lorsque vous lirez cette section, n’hésitez pas à démarrer la ligne de commande Windows PowerShell et à taper les commandes. N’oubliez pas cependant que certaines commandes dépendent des commandes précédentes. En d’autres termes, certains des objets créés à l’origine seront utilisés plus tard, restez donc dans la même session Windows PowerShell. Si vous fermez la session, vous devrez tout recommencer de zéro et retaper toutes les commandes.

Donc, créons un nouveau GPO en utilisant Windows PowerShell. L’équipe de stratégie de groupe chez Microsoft a inclus à la GPMC plusieurs exemples VBScript fonctionnant parfaitement, dont vous pouvez tirer parti pour accélérer le processus. Ils se trouvent dans le répertoire %ProgramFiles%\GPMC\Scripts, où vous trouverez également un fichier appelé gpmc.chm et qui contient la documentation des API de la GPMC. Jetons un coup d’œil au script CreateGPO.wsf et disséquons-le pour voir son fonctionnement.

Vers le haut de la page, vous verrez cette ligne :

Dim GPM
Set GPM = CreateObject(“GPMgmt.GPM”)

Il s’agit grosso modo du point de départ de toute session de gestion ou de tout script de la stratégie de groupe, parce que cette ligne instancie la classe GPMgmt.GPM, laquelle fournit les accès à la plupart des fonctionnalités de la GPMC. Poursuivons et faisons la même chose avec Windows PowerShell :

$gpm = New-Object -ComObject GPMgmt.GPM

Maintenant que vous disposez du point de départ pour la gestion de la stratégie de groupe, l’étape suivante consiste à déterminer ce que vous pouvez en faire. Normalement, vous iriez voir la documentation pour trouver ce type d’informations, mais Windows PowerShell a une fonctionnalité très pratique. Si vous tapez la ligne suivante, vous obtenez les résultats montrés à la figure 2 :

Figure 2 Résultat Get-Member

Figure 2** Résultat Get-Member **(Cliquer sur l'image pour l'agrandir)

$gpm | gm

Très pratique, si vous voulez mon opinion. Notez comment le cmdlet Get-Member (ou gm) vous permet de voir les propriétés et les méthodes que l’objet prend en charge dès la ligne de commande. Ce n’est bien évidemment pas la même chose que de lire la documentation, mais cela facilite l’utilisation des objets que vous connaissez déjà lorsque vous ne vous rappelez pas du nombre exact de paramètres, du nom, etc. Il est important de remarquer que lorsque vous regardez les listings de nœuds de la documentation de la GPMC, il semble que l’objet GPM et toutes les autres classes sont préfixés de la lettre I ; ceci est dû aux fonctionnements internes de COM et ne nous concerne pas vraiment dans le cas présent ; ce codage vise les programmateurs C++ qui écrivent du code COM natif et indique la différence entre une interface et la classe qui l’implémente. Remarquez aussi qu’en utilisant les API de la GPMC, il n’y a qu’un seul objet que vous devez créer de cette façon : GPMgmt.GPM ; tous les autres le sont à l’aide des méthodes qui démarrent avec cet objet GPM.

Abordons maintenant la création d’un nouveau GPO.

La figure 3 montre combien il est simple de créer un GPO. Notez que j’ai omis une certaine partie du code, y compris la gestion des erreurs (par exemple, ce qui se produira si vous n’êtes pas autorisé à créer des GPO) et j’ai codé en dur un nom de domaine...vous devriez savoir de quoi je parle.

Figure 3 Créez un GPO

$gpmConstants = $gpm.GetConstants() 
# This is the GPMC way to retrieve all 
# constants
$gpmDomain =$gpm.GetDomain(“Mydomain.local”, “”, $gpmConstants.UseAnyDC)
# Connects to the domain where the GPO should 
# be created, replace Mydomain.local with the 
# name of the domain to connect to.
$gpmNewGpo = $gpmDomain.CreateGPO() 
# Create the GPO
$gpmNewGpo.DisplayName = “My New Windows PowerShell GPO” 
# Set the name of the GPO

Maintenant que vous savez créer un GPO, ouvrons-en un plutôt qui existe déjà. Vous avez encore la référence au domaine, $gpmDomain, donc tapez ce qui suit :

$gpmExistingGpo = $gpmDomain.GetGPO(
  “{31B2F340-016D-11D2-945F-00C04FB984F9}”) 
# Open an existing GPO based on its GUID, 
# in this case the Default Domain Policy.
$gpmExistingGpo.DisplayName 
# Show the display name of the GPO, it 
# should say Default Domain Policy
$gpmExistingGpo.GenerateReportToFile($gpmConstants.ReportHTML, “.\DefaultDomainPolicyReport.html”

Ceci vous donne un rapport complet HTML des paramètres de la stratégie de domaine par défaut, mais vous pouvez évidemment utiliser l’une des méthodes et des propriétés, telles que ModificationTime, qui vous indique la dernière date de modification du GPO, afin de déterminer quand les paramètres du GPO ont été changés.

C’est extrêmement utile. Vous avez certainement déjà fait l’expérience d’une situation où les téléphones commencent à sonner dans tous les sens et où les utilisateurs se plaignent que leurs ordinateurs ont un comportement étrange. Vous soupçonnez que cela est lié à la modification, l’ajout ou la suppression d’un paramètre GPO, mais vous ne savez absolument pas quel GPO vous devez inspecter. Rassurez-vous, Windows PowerShell est là ! Si vous entrez le script montré à la figure 4 au niveau de la ligne de commande Windows PowerShell, vous obtenez tous les GPO modifiés au cours des dernières 24 heures.

Figure 4 Découverte des GPO modifiés

$gpmSearchCriteria = $gpm.CreateSearchCriteria() 
# We want all GPOs so no search criteria will be specified
$gpmAllGpos = $gpmDomain.SearchGPOs($gpmSearchCriteria) 
# Find all GPOs in the domain
foreach ($gpmGpo in $gpmAllGpos)
{
if ($gpmGpo.ModificationTime -ge (get-date).AddDays(-1)) {$gpmGpo.DisplayName}
# Check if the GPO has been modified less than 24 hours from now 
}

Remarquez l’opérateur -ge, qui signifie supérieur ou égal à. Ceci pourrait vous paraître étrange si vous êtes habitué aux opérateurs < et > des autres langages de programmation et de script. Mais ces opérateurs sont utilisés pour la redirection, par exemple pour rediriger les résultats vers le fichier, et ne pourraient donc pas servir d’opérateurs de comparaison dans Windows PowerShell.

Conclusion

Le code de la figure 5 liste l’intégralité du script nécessaire à la copie des paramètres d’un GPO dans un autre GPO. Vous devriez maintenant savoir comment utiliser cette nouvelle technologie avec la stratégie de groupe et comment réemployer des objets COM ou du code VBScript qui consomme un objet COM.

Figure 5 Copiez les paramètres d’un GPO dans un autre GPO

###########################################################################
# Function  : CopyGpo
# Description: Copies the settings in a GPO to another GPO
# Parameters : $sourceGpo     - The GPO name or GPO ID of the GPO to copy
#           : $sourceDomain   - The dns name, such as microsoft.com, of the domain where the original GPO is located
#           : $targetGpo      - The GPO name of the GPO to add
#           : $targetDomain   - The dns name, such as microsoft.com, of the domain where the copy should be put
#           : $migrationTable - The path to an optional Migration table to use when copying the GPO
# Returns   : N/A
# Dependencies: Uses GetGpoByNameOrID, found in article download
###########################################################################
function CopyGpo(
 [string] $sourceGpo=$(throw ‘$sourceGpo is required’),
 [string] $sourceDomain=$(throw ‘$sourceDomain is required’),
 [string] $targetGpo=$(throw ‘$targetGpo is required’),
 [string] $targetDomain=$(throw ‘$targetDomain is required’),
 [string] $migrationTable=$(“”),
 [switch] $copyAcl)
{
 
 $gpm = New-Object -ComObject GPMgmt.GPM # Create the GPMC Main object
 $gpmConstants = $gpm.GetConstants() # Load the GPMC constants
 $gpmSourceDomain = $gpm.GetDomain($sourceDomain, “”, $gpmConstants.UseAnyDC) # Connect to the domain passed 
                                                                              # using any DC
 $gpmSourceGpo = GetGpoByNameOrID $sourceGpo $gpmSourceDomain
 # Handle situations where no or multiple GPOs was found
 switch ($gpmSourceGpo.Count)
 {
   {$_ -eq 0} {throw ‘No GPO named $gpoName found’; return}
   {$_ -gt 1} {throw ‘More than one GPO named $gpoName found’; return} 
 }
 if ($migrationTable)
 {
   $gpmMigrationTable = $gpm.GetMigrationTable($migrationTable)
 }

 $gpmTargetDomain = $gpm.GetDomain($targetDomain, “”, $gpmConstants.UseAnyDC) # Connect to the domain passed 
                                                                              # using any DC

 $copyFlags = 0
 if ($copyAcl)
 {
   $copyFlags = Constants.ProcessSecurity
 }
 $gpmResult = $gpmSourceGpo.CopyTo($copyFlags, $gpmTargetDomain, $targetGpo)
 [void] $gpmResult.OverallStatus
 
}

Windows PowerShell sera, tout comme la stratégie de groupe l’est déjà, un composant naturel de tout environnement de gestion Windows. Cependant, il existe des millions de lignes VBScript qui devront être migrées ou maintenues et j’espère que ce didacticiel vous aidera.

Plusieurs sources sont à votre disposition pour améliorer l’administration de votre stratégie de groupe et les autres domaines où vous avez précédemment utilisé VBScript, y compris les fonctions de téléchargement de Windows PowerShell et un superbe guide de conversion VBScript/Windows PowerShell qui se trouve sur le site Web de TechNet. Celui-ci fournit des conseils sur la réalisation des tâches communes dans Windows PowerShell lorsque vous connaissez l’équivalent en VBScript. Vous le trouverez sur microsoft.com/technet/scriptcenter/topics/winpsh/convert.

En outre, l’API de la GPMC est entièrement décrite ; vous pouvez télécharger ces informations à partir du site Stratégie de groupe sur microsoft.com/grouppolicy.

Enfin et surtout, si vous n’avez pas encore installé Windows PowerShell, qu’attendez-vous ? Téléchargez-le sur microsoft.com/powershell. Amusez-vous !

Thorbjörn Sjövold est Directeur technique et fondateur de Special Operations Software (www.specopssoft.com), un fournisseur de produits d’extension pour la gestion et la sécurité des systèmes basés sur la stratégie de groupe. Vous pouvez le joindre à thorbjorn.sjovold@specopssoft.com.

© 2008 Microsoft Corporation et CMP Media, LLC. Tous droits réservés. Toute reproduction, totale ou partielle, est interdite sans autorisation préalable.