Inventaire des Service Packs Windows XP - Deuxième partie

Paru le 29/03/2005

Par Les Messieurs Script de Microsoft

Le Docteur Scripto au travail

Le magasin de scripts du Docteur Scripto aborde les problèmes de script liés à l'administration du système, souvent tirés des expériences de nos lecteurs, et développe des scripts pour les résoudre. La plupart des exemples du référentiel de scripts exécutent une seule tâche, assez simple. Dans cette rubrique, en revanche, nous rassemblerons différents éléments afin d'obtenir des scripts plus complexes. Ces scripts ne seront ni complets ni infaillibles, mais vous montreront la marche à suivre pour créer des scripts à partir de modules de code réutilisables, gérer des erreurs et des codes de retour, obtenir des entrées et des sorties à partir de différentes sources, exécuter des scripts sur plusieurs ordinateurs, et effectuer d'autres opérations avec vos scripts de production.

Nous espérons que ces articles et scripts vous seront utiles – faites-nous part de votre avis, des solutions que vous proposez pour ces problèmes et des sujets que vous souhaiteriez voir publiés.

Cet article est le deuxième d'une série de trois. Nous vous conseillons de commencer par le premier article : Inventaire des Service Packs Windows XP – Première partie site en anglais

Sommaire,Sur cette page

Inventaire des Service Packs Windows XP – Deuxième partie
Recensement des ordinateurs d'un conteneur Active Directory à l'aide d'ADSI
ADO et la recherche dans Active Directory
Procédures et étendue des variables
La totale
Pour en savoir plus

Inventaire des Service Packs Windows XP – Deuxième partie

Lorsqu'une échéance importante approche, il n'est pas rare de trouver les Messieurs Script dans notre pièce stratégique — un jacuzzi de taille olympique doté d'un seau à champagne incorporé, situé dans notre superbe appartement — les yeux tournés vers les montagnes Cascade, en train de méditer. Nous sommes des philosophes créatifs : c'est la raison pour laquelle nous sommes riches et célèbres (et toujours les premiers pour les croissants gratuits du vendredi matin).

Déléguer étant l'une de nos spécialités, nous laissons donc Dr. Scripto, notre conseiller surmené et fatigué, peaufiner les détails. Comme vous pouvez l'imaginer, lorsqu'il a entendu dire qu'à partir du 12 avril il ne serait plus possible d'empêcher l'installation du Service Pack 2 Windows XP, il s'est mis à travailler comme un fou. Avec le style mélodramatique qui le caractérise, il l'a appelé « Le jour SP2 ». Tout en travaillant, il imite Winston Churchill disant : « Nous devons en écrire des scripts sur les plages, nous devons en écrire des scripts dans les rues » et mordillant son cigare.

Comme d'habitude, il exagère un peu. Tout d'abord, le Service Pack 2 est loin d'être une invasion : il apporte de nombreuses fonctionnalités nouvelles, comme le Pare-feu Windows, qui empêchent les véritables invasions des vers et des virus, tout en garantissant l'application de correctifs sur les ordinateurs. Ensuite, le délai du 12 avril s'applique uniquement si vous utilisez les services des Mises à jour automatiques ou Windows Update pour maintenir les correctifs à jour sur vos clients Windows XP. Si vous aviez décidé de déployer le Service Pack 2 après le 12 avril, vous auriez pu désactiver la mise à jour, même si, de ce fait, vous n'auriez pas pu bénéficier des avantages liés à l'application automatique des correctifs.

Dans tous les cas, nous voulons connaître le bilan sur nos clients. Ainsi, notre tâche consiste à écrire un script qui réalise l'inventaire de tous les clients Windows XP afin d'identifier ceux qui n'ont pas encore été mis à niveau vers le Service Pack 2, puis exploiter ces informations. Dans l'article précédent, nous vous avons expliqué comment le faire sur les ordinateurs non joints à un domaine Active Directory, à l'aide de WMI. Cette semaine, nous allons illustrer deux approches applicables dans un environnement Active Directory.

Recensement des ordinateurs d'un conteneur Active Directory à l'aide d'ADSI

Si vous avez déjà utilisé ADSI (Active Directory Service Interfaces), vous êtes probablement en mesure d'imaginer comment fonctionne notre premier script. Dans le cas contraire, voici sa fonction. D'abord, il établit une liaison avec un conteneur Active Directory spécifique à l'aide du fournisseur LDAP et filtre la collection d'objets qu'il obtient afin d'en extraire uniquement les objets ordinateur. Ensuite, pour chaque ordinateur de la collection filtrée, il vérifie l'attribut OperatingSystemVersion, qui permet de savoir si Windows XP est en cours d'exécution. Au lieu d'utiliser l'attribut ADSI, qui contient le nom du système d'exploitation, nous avons choisi celui qui renvoie le numéro de version et de build de Windows XP : 5.1 (2600). Si l'ordinateur exécute Windows XP mais pas le Service Pack 2, le script affiche la version et le Service Pack, qu'il obtient via l'attribut OperatingSystemServicePack.

IADsComputer

Nous n'allons pas agrémenter ce script d'une super puissance, de deux carburateurs, d'un double échappement ou de décalcomanies représentant des flammes ! Nous réservons tout cela pour notre version finale, qui a une approche différente. Bien que nous aimions notre vieil ami ADSI, ce n'est pas la technologie la plus performante pour exécuter cette tâche, et ceci pour des raisons que vous allez découvrir très rapidement.

On Error Resume Next
strContainer = "CN=computers,DC=fabikam,DC=com"
Set colComputers = GetObject("LDAP://" & strContainer)
colComputers.Filter = Array("Computer")
For Each objComputer in colComputers
  If objComputer.operatingSystemVersion = "5.1 (2600)" And _
   Not (objComputer.operatingSystemServicePack = "Service Pack 2") Then
    strComputer = objComputer.CN
    Wscript.Echo
    Wscript.Echo strComputer
    Wscript.Echo String(Len(strComputer), "-")
    Wscript.Echo "  " & objComputer.OperatingSystemVersion
    Wscript.Echo "  " & objComputer.OperatingSystemServicePack
  End If
Next

Ce script vérifie l'ensemble des ordinateurs à condition que le conteneur des ordinateurs de fabrikam.com ne comporte pas de sous-conteneurs. Mais, que se passe-t-il si le conteneur est une unité d'organisation (OU) qui contient d'autres sous-unités d'organisation ? Comme ce script ne peut pas rechercher les ordinateurs dans les sous-conteneurs, nous obtiendrons une liste incomplète. Vous pourriez écrire une fonction qui parcourt la structure de l'annuaire de manière récurrente, mais le code deviendrait un peu trop élaboré et entraînerait un investissement en temps trop important. La récurrence, ce sera pour plus tard, pour l'instant, nous avons d'autres chats à fouetter (voilà l'état de votre cerveau après quelques heures de VBScript). Nous en parlerons peut-être à un autre moment.

ADO et la recherche dans Active Directory

Pour exécuter cette tâche, nous n'avons pas besoin d'utiliser ADSI car il existe une solution plus appropriée. ADO (ActiveX Data Objects) est une technologie de script qui fonctionne avec des bases de données et Active Directory en est une. Le code ADO peut vous intimider au premier abord, mais une fois que vous l'appréhendez, c'est un vrai jeu d'enfant dans une bibliothèque d'automatisation COM. Vous pouvez parcourir aisément une hiérarchie d'annuaire à l'aide d'ADO : il suffit de définir une propriété qui indique à la requête ADO d'explorer les sous-arborescences du conteneur dans leur intégralité. Et plus important encore, si votre annuaire est volumineux, vous gagnerez en rapidité à effectuer vos recherches avec ADO par rapport à ADSI : lors d'un test informel, nous avons trouvé que les recherches effectuées avec ADO étaient environ 100 fois plus rapides.

Dans le cadre de cet article, notre script principal utilisera ADO pour extraire les informations de tous les ordinateurs d'un domaine Active Directory ou d'un autre conteneur. Dans la mesure où ADO a été conçu pour fonctionner avec des bases de données, il accepte les requêtes SQL (de même que les requêtes au format LDAP). Dans ces types de requêtes, toutefois, nous utilisons encore le même ADsPath et d'autres attributs ADSI, tel que nous l'avons fait dans le script précédent, car ADO travaille conjointement avec ADSI.

Avant de développer le script complet, nous allons présenter une version abrégée qui utilise un code ADO similaire mais affiche les résultats à l'écran au lieu de les envoyer vers une feuille de calcul Excel. Nous pourrons ainsi nous concentrer sur la façon dont ADO fonctionne.

Lorsque nous utilisons ADO, nous commençons généralement par créer deux objets : ADODB.Connection et ADODB.Command. Nous indiquons à l'objet Connection le type de base de données auquel nous voulons nous connecter. Dans le cadre de ce script, nous indiquons à ADO de se connecter au fournisseur Active Directory. Nous pourrions également transmettre à l'objet Connection des informations d'identification alternatives, mais cela compliquerait le script.

Nous indiquons ensuite à l'objet Command le type de requête que nous voulons exécuter sur la base de données. La constante ADS_SCOPE_SUBTREE, que nous attribuons à la propriété Searchscope de l'objet Command, indique à ADO que nous voulons également rechercher les conteneurs situés dans le conteneur de premier niveau et dans l'ensemble des sous-niveaux de la hiérarchie. Nous aurions pu également utiliser d'autres constantes pour rechercher uniquement le conteneur spécifié ou ce conteneur et ses enfants immédiats.

La propriété Page Size de l'objet Command indique le nombre d'enregistrements que la requête doit extraire de l'annuaire avec une seule exécution d'ADO. Il est important de spécifier une taille de page car, dans le cas contraire, ADO renvoie 1 000 enregistrements (par défaut) puis arrête le traitement de la requête. Si vous définissez la taille de page, les enregistrements sont renvoyés par lots de cette taille tant que tous les enregistrements correspondants n'ont pas été extraits. La règle d'or consiste à définir la propriété Page Size sur une valeur comprise entre 100 et 1 000, en fonction de la taille de l'annuaire.

Dans ce premier script ADO, notre requête extrait uniquement les ordinateurs exécutant Windows XP sur lesquels le Service Pack 2 n'a pas été installé. Nous filtrons les objets du conteneur à l'aide d'une clause WHERE, modifiée ultérieurement par des instructions AND et AND NOT.

"SELECT CN, operatingSystemVersion, operatingSystemServicePack " _
   & "FROM 'LDAP://DC=fabrikam,DC=com' " _
   & "WHERE objectCategory='computer' " _
   & "AND operatingSystemVersion = '5.1 (2600)' " _
   & "AND NOT operatingSystemServicePack = 'Service Pack 2'"

Vous avez probablement déjà vu des requêtes SQL lorsque vous avez travaillé avec des bases de données ou dans les requêtes WQL utilisées avec WMI. Nous n'allons pas les décrire en détail, mais il est important de garder à l'esprit certains éléments lorsque vous exécutez des requêtes dans Active Directory à l'aide d'ADO. Vous avez certainement remarqué qu'après l'instruction SELECT, nous dressons la liste des attributs que nous souhaitons extraire d'Active Directory. Dans les requêtes WQL, nous utilisons souvent un astérisque (*) pour extraire toutes les propriétés d'une classe. Pour les requêtes SQL dans ADO, l'astérisque renvoie uniquement l'attribut ADsPath. Par conséquent, si vous voulez extraire d'autres propriétés, vous devez les spécifier.

ADODB

Dans la dernière partie du script, nous exécutons la commande qui lance cette requête. La commande renvoie un jeu d'enregistrements de tous les objets du conteneur et de ses conteneurs enfants répondant aux critères de la requête. Nous utilisons la méthode Sort du jeu d'enregistrements pour trier les objets dans l'ordre alphabétique de leur nom commun (CN), puis nous nous plaçons sur le premier objet. Nous parcourons ensuite le jeu d'enregistrements à l'aide d'une boucle Do Until, en utilisant la méthode MoveNext à la fin de chaque enregistrement et en vérifiant si la propriété EOF (fin de fichier) du jeu d'enregistrements a la valeur True avant de passer à l'itération suivante. ADO n'exécute pas automatiquement ces opérations pour nous. C'est pour cette raison que nous devons placer le curseur à l'endroit voulu dans le jeu d'enregistrements et le déplacer à l'aide des fonctionnalités offertes par ADO.

Pour une description plus détaillée de la création de scripts avec ADO, consultez le webcast des Messieurs Script (en anglais) « Poking Your Nose Into Active Directory » (vous trouverez le lien à la fin de cet article).

'List all computers in an AD domain running XP but not SP2 using ADO.

On Error Resume Next

Const ADS_SCOPE_SUBTREE = 2
strContainer = "DC=fabrikam,DC=com"

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = ("ADsDSOObject")
objConnection.Open "Active Directory Provider"
objCommand.ActiveConnection = objConnection
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.Properties("Page Size") = 1000
objCommand.CommandText = _
  "SELECT CN, operatingSystemVersion, operatingSystemServicePack " _
   & "FROM 'LDAP://" & strContainer & "' " _
   & "WHERE objectCategory='computer' " _
   & "AND operatingSystemVersion = '5.1 (2600)' " _
   & "AND NOT operatingSystemServicePack = 'Service Pack 2'"
Set objRecordSet = objCommand.Execute
objRecordSet.Sort = "CN"
objRecordSet.MoveFirst 
Do Until objRecordSet.EOF
  Wscript.Echo
  Wscript.Echo objRecordSet.Fields("CN").Value
  Wscript.Echo String(Len(objRecordSet.Fields("CN").Value), "-")
  Wscript.Echo "  " & objRecordSet.Fields("operatingSystemVersion").Value
  Wscript.Echo "  " & objRecordSet.Fields("operatingSystemServicePack").Value
  objRecordSet.MoveNext 
Loop

Procédures et étendue des variables

Nous utilisons dans notre script final du code très similaire à celui que nous avons utilisé dans le script précédent, sauf que nous envoyons les résultats vers une feuille de calcul Excel. À l'instar de l'article précédent, nous fractionnons certaines fonctionnalités du script en procédures distinctes. Cette fois-ci, nous utilisons des sous-routines pour insérer les résultats dans la feuille de calcul et gérer les erreurs.

Nous aurions également pu isoler dans une fonction la logique consistant à trier les noms d'ordinateurs dans des variables distinctes en fonction de la version du système d'exploitation et du Service Pack, mais ça n'en valait pas vraiment la peine. Nous ne voulons choquer personne, mais les décisions concernant la façon de fractionner un script en composants relève plus d'un art que d'une science, en tout cas lorsqu'elles doivent être prises par les Messieurs Script.

VBScript (et la plupart des langages de programmation) offre d'autres fonctionnalités utilisées dans cet article et dans le précédent, applicables aux sous-routines et aux fonctions. Si vous ne connaissez pas les concepts de variables globales et étendue des variables, voici une explication succincte.

Le concept d'étendue apparaît lorsque vous utilisez des procédures dans un script. Chaque variable a une étendue à l'intérieur du script. Les variables déclarées ou initialisées dans la routine principale du script sont des variables globales qui ont une étendue globale. Cela signifie que les sous-routines et les fonctions peuvent lire et modifier les valeurs de ces variables. Nous utilisons dans nos scripts la convention facultative qui consiste à faire précéder de « g_ » les noms des variables globales.

Cependant, si vous utilisez une variable uniquement dans une sous-routine ou une fonction, son étendue sera locale, c'est-à-dire, limitée à cette procédure. La partie principale du script et d'autres procédures ignorent l'existence de la variable et ne peuvent pas y accéder.

Un moyen de partager des variables entre les composants d'un script consiste à les transférer sous forme de paramètres de la partie principale du script vers les procédures, et d'une procédure à une autre. En ce qui concerne les variables globales, vous ne pouvez que les déclarer, initialiser ou utiliser au sein du script avant d'appeler une procédure. Ensuite, elles deviennent automatiquement accessibles aux procédures.

VBScript rend la situation quelque peu confuse car vous n'êtes pas obligé de déclarer les variables avant de les utiliser (sauf si vous insérez Option Explicit au début du script). Vous pouvez utiliser le mot clé Dim pour déclarer des variables, mais cette option est facultative. Si vous commencez à utiliser une variable sans l'avoir déclarée ou initialisée au préalable (en lui attribuant une valeur), VBScript suppose que sa valeur est soit 0, soit une chaîne vide (""), en fonction du contexte.

Voici deux exemples succincts qui illustrent comment vous pouvez jeter la confusion dans votre esprit si vous ne comprenez pas la différence entre les variables globales et les variables locales.

'main body of script

Sub1
Sub2
WScript.Echo x + y

'procedures

Sub Sub1
  x = 2
End Sub

Sub Sub2
  y = 5
End Subs

Sortie :

0

Contrairement à ce que vous pouvez imaginer (surtout si vous n'avez pas encore assimilé les subtilités de l'étendue), ce script affiche la valeur 0 comme résultat de la somme de x et de y. Cela s'explique par le fait que nous avons appelé les sous-routines sans avoir attribué au préalable des valeurs à ces variables dans le corps du script. Par conséquent, lorsque nous envoyons un écho de ces variables, VBScript suppose qu'elles ont toutes deux la valeur 0. Le corps du script ignore que Sub1 et Sub2 ont attribué d'autres valeurs à x et à y car l'étendue des variables au sein de chaque procédure est locale, même si les variables locales portent les mêmes noms que celles figurant dans la partie principale du script.

Dans le script suivant, en revanche, nous attribuons explicitement des valeurs à x et à y dans la partie principale du script, avant de les utiliser dans les sous-routines. Cela confère aux variables une étendue globale.

'main body of script

x = 0
y = 0

Sub1
Sub2
WScript.Echo x + y

'procedures

Sub Sub1
  x = 2
End Sub

Sub Sub2
  y = 5
End Sub

Sortie :

7

À présent, les sous-routines ont accès aux variables globales x et y dans la partie principale du script et peuvent ainsi modifier leurs valeurs. Cette fois-ci, la valeur de x + y est égale à 7.

La totale

Voici enfin le script complet dans toute sa splendeur : il extrait les noms de tous les ordinateurs d'un domaine Active Directory par système d'exploitation et Service Pack et les envoie vers une feuille de calcul Excel.

Pour exécuter ce script sur votre réseau local, ce dernier doit utiliser Active Directory et Excel doit être déjà installé sur l'ordinateur local. Vous devez remplacer la valeur attribuée à la variable strContainer par le nom du conteneur du réseau dont vous souhaitez faire l'inventaire. Par ailleurs, le répertoire C:\Scripts doit exister sur l'ordinateur local. Dans le cas contraire, vous devez remplacer le chemin d'accès dans strOutputFile par un autre chemin présent sur cet ordinateur. (Si vous le souhaitez, vous pouvez également modifier le nom de fichier de la feuille de calcul.) Bien que ce script se limite à collecter des informations, gardez à l'esprit qu'il est souvent judicieux de tester d'abord les scripts sur un réseau de test avant de les exécuter sur un réseau de production.

L'exécution du script peut prendre un certain temps lorsqu'il s'agit d'un annuaire de grande taille. En règle générale - en fonction de la vitesse du réseau, de la configuration de l'annuaire, de la puissance de traitement de la station de travail sur laquelle le script s'exécute et d'une multitude d'autres facteurs - comptez environ 30 secondes pour 1 000 ordinateurs.

Dans ce script, nous allons rechercher tous les ordinateurs du conteneur et de ses sous-conteneurs et pas uniquement ceux qui exécutent Windows XP sans le Service Pack 2, comme nous l'avons fait dans le script précédent.

Nous aurions pu continuer à utiliser ADO dans ce script afin de trier les résultats par nom et par Service Pack, mais afin d'essayer une autre technique, nous avons choisi de trier les noms d'ordinateurs en leur attribuant différentes variables en fonction du système d'exploitation et du Service Pack installés, et en comptant le nombre d'ordinateurs par catégorie. Pour les ordinateurs exécutant Windows XP, nous allons utiliser une variable distincte pour chaque Service Pack. Quant aux ordinateurs qui n'exécutent pas Windows XP, nous allons les rassembler en une seule variable, car ils n'entrent pas dans le cadre du présent scénario. Enfin, nous allons trier les noms d'ordinateurs par ordre alphabétique pour chaque catégorie de la feuille de calcul.

Pour transformer ce script en un rapport périodique sur l'état des mises à jour des Services Packs sur le réseau, vous pourriez ajouter de manière dynamique la date au nom de fichier de la feuille de calcul avec un code similaire au suivant, qui envoie la sortie de la fonction VBScript Date :

strDate = Replace(Date, "/", "-")
strOutputFile "c:\scripts\xpsp-" & strDate & ".xls"

Vous pourriez ensuite programmer son exécution afin qu'elle se déroule quotidiennement (ou à des intervalles réguliers) à l'aide de At.exe ou du Planificateur de tâches. (Vous pourriez aussi planifier une commande AT avec un script WMI à l'aide de la classe Win32_ScheduledJob.)

L'exécution de ce script peut prendre un certain temps sur un réseau de grande taille. En règle générale - en fonction de la vitesse du réseau, de la configuration de l'annuaire, de la puissance de traitement de la station de travail sur laquelle le script s'exécute et d'une multitude d'autres facteurs - comptez au moins 30 secondes pour 1 000 ordinateurs. Vous devez bien évidemment exécuter le script avec des informations d'identification d'administrateur, qui comprennent tous les autres privilèges requis pour accéder à l'annuaire Active Directory de votre réseau.

Nous avons divisé le script en une routine principale et deux sous-routines.

  • La routine principale initialise les constantes et les variables globales, puis se connecte à ADO et recherche dans l'annuaire tous les objets ordinateur du conteneur. Lorsqu'elle passe en revue les objets, elle trie leurs noms dans des variables globales distinctes par système d'exploitation et par Service Pack (g_strSP2, g_strSP1, g_strSP0 et g_strNotXP, chacune étant une chaîne de noms délimités par des virgules), puis réalise un décompte dans chaque catégorie. Elle appelle ensuite une sous-routine afin d'écrire les résultats sur une feuille de calcul.

    Toujours dans la routine principale, le script recherche des erreurs en cas d'échec de la connexion au fournisseur Active Directory via ADO, et à nouveau, en cas d'échec de l'exécution de la requête. En cas d'erreur, il appelle un code de traitement des erreurs, qui est divisé en une nouvelle sous-routine.

  • La sous-routine WriteSpreadsheet extrait en tant que paramètre une chaîne contenant le chemin d'accès et le nom du fichier de feuille de calcul à créer. Elle se connecte au modèle d'objet Excel en appelant la méthode VBScript CreateObject et en lui transmettant l'identificateur programmatique (ProgID) Excel.Application. Cet appel crée un nouvel objet Excel qui permet au script d'accéder à toutes les fonctionnalités d'Excel.

    Avec Excel, vous ajoutez d'abord un objet classeur à l'objet Excel, vous y écrivez et formatez les données, puis vous sauvegardez le classeur en tant que fichier ; dans le cas présent, sous le nom de fichier transmis en tant que paramètre.

    La plupart du code contenu dans cette sous-routine écrit des titres dans des cellules spécifiques et les formate. Il additionne les variables de compteur globales (g_int*) afin d'afficher le nombre total d'ordinateurs dans chaque catégorie. Pour afficher la liste des ordinateurs, le code convertit les variables de chaîne globales (g_str*) en tableaux. Pour ce faire, il les fractionne en éléments de tableau, au niveau des virgules, à l'aide de la fonction VBScript Split. Ensuite, il passe en revue les tableaux et écrit chaque nom dans une colonne, en incrémentant le numéro de ligne pour chaque entrée. Pour trier les noms contenus dans chaque colonne, il crée deux objets Excel Range, le premier correspondant à la plage de données et le second à une clé de tri. Le code appelle ensuite la méthode Excel Sort sur le premier objet plage et lui transmet la deuxième plage, à savoir la clé de tri, sous forme de paramètre.

    Enfin, la sous-routine enregistre la feuille de calcul sous le nom de fichier attribué à g_strContainer, en remplaçant, si nécessaire, un fichier existant.

  • La sous-routine HandleError extrait en tant que paramètre l'objet VBScript Err, qui contient les informations sur l'erreur et un message d'erreur personnalisé en fonction du contexte. Elle affiche le message personnalisé et le numéro, l'origine et la description de l'erreur contenus dans l'objet Err.

Voici le code :

'List all computers in an AD domain running XP by SP using ADO.
'Output to Excel spreadsheet.

On Error Resume Next

'Initialize constants and variables.
Const xlAscending = 1
Const xlYes = 1
Const ADS_SCOPE_SUBTREE = 2
g_strContainer = "dc=na,dc=fabrikam,dc=com"
strQuery = "SELECT CN, operatingSystemVersion, operatingSystemServicePack " _
 & "FROM 'LDAP://" & g_strContainer & "' WHERE objectCategory='computer'"
strOutputFile = "c:\scripts\xpsp.xls"

g_strSP2 = ""
g_strSP1 = ""
g_strSP0 = ""
g_strNotXP = ""
g_intSP2 = 0
g_intSP1 = 0
g_intSP0 = 0
g_intNotXP = 0

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = ("ADsDSOObject")
objConnection.Open "Active Directory Provider"
If Err <> 0 Then
  HandleError Err, "Unable to connect to AD Provider with ADO."
End If
objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.CommandText = strQuery
Set objRecordSet = objCommand.Execute
If Err <> 0 Then
  HandleError Err, "Unable to execute ADO query."
End If

WScript.Echo "Gathering data from Active Directory ..."

objRecordSet.MoveFirst 
Do Until objRecordSet.EOF
  If objRecordSet.Fields("operatingSystemVersion").Value = "5.1 (2600)" Then
    If objRecordSet.Fields("operatingSystemServicePack").Value = _
     "Service Pack 2" Then
      g_strSP2 = g_strSP2 & objRecordSet.Fields("CN").Value & ","
      g_intSP2 = g_intSP2 + 1
    ElseIf objRecordSet.Fields("operatingSystemServicePack").Value = _
     "Service Pack 1" Then
      g_strSP1 = g_strSP1 & objRecordSet.Fields("CN").Value & ","
      g_intSP1 = g_intSP1 + 1
    Else
      g_strSP0 = g_strSP0 & objRecordSet.Fields("CN").Value & ","
      g_intSP0 = g_intSP0 + 1
    End If
  Else
    g_strNotXP = g_strNotXP & objRecordSet.Fields("CN").Value & ","
    g_intNotXP = g_intNotXP + 1
  End If
  objRecordSet.MoveNext 
Loop 

If Err <> 0 Then
  HandleError Err, "Unable to gather data."
End If

WScript.Echo "Writing data to spreadsheet ..."

WriteSpreadsheet strOutputFile

WScript.Echo "Data written to " & strOutputFile

'******************************************************************************

'Write data to spreadsheet.
Sub WriteSpreadsheet(strFileName)

'On Error Resume Next

'Create spreadsheet and open workbook.
Set objExcel = CreateObject("Excel.Application")
objExcel.Workbooks.Add

'Write data to spreadsheet.
objExcel.Cells(1,1).Value = "Inventory of Windows XP Service Packs"
objExcel.Cells(1,1).Font.Bold = True
objExcel.Cells(1,1).Font.Size = 13
objExcel.Cells(2,1).Value = "Time: " & Now
objExcel.Cells(2,1).Font.Bold = True
objExcel.Cells(2,5).Value = "Container: " & g_strContainer
objExcel.Cells(2,5).Font.Bold = True
objExcel.Cells(3,1).Value = "Total Computers: "
objExcel.Cells(3,1).Font.Bold = True
objExcel.Cells(3,3).Value = g_intSP2 + g_intSP1 + g_intSP0 + g_intNotXP
objExcel.Cells(3,3).Font.Bold = True

objExcel.Cells(4,1).Value = "Computers Running Windows XP"
objExcel.Cells(4,1).Font.Bold = True
objExcel.Cells(4,1).Font.Size = 12
objExcel.Cells(5,1).Value = "Number"
objExcel.Cells(5,1).Font.Bold = True
objExcel.Cells(6,1).Value = g_intSP2 + g_intSP1 + g_intSP0

objExcel.Cells(8,1).Value = "Service Pack 2"
objExcel.Cells(8,1).Font.Bold = True
objExcel.Cells(8,1).Font.Size = 11
objExcel.Cells(9,1).Value = "Number"
objExcel.Cells(9,1).Font.Bold = True
objExcel.Cells(10,1).Value = g_intSP2
objExcel.Cells(9,2).Value = "Names"
objExcel.Cells(9,2).Font.Bold = True

arrSP2 = Split(g_strSP2, ",")
x = 10
For Each strSP2 In arrSP2
  objExcel.Cells(x,2).Value = strSP2
  x = x + 1
Next
Set objRange = objExcel.Range("B1")
objRange.Activate
Set objRange = objExcel.ActiveCell.EntireColumn
objRange.AutoFit()
Set objSortRange = objExcel.Range("B9:B" & x)
Set objSortKey = objExcel.Range("B10")
objSortRange.Sort objSortKey,xlAscending,,,,,,xlYes

objExcel.Cells(8,3).Value = "Service Pack 1"
objExcel.Cells(8,3).Font.Bold = True
objExcel.Cells(8,3).Font.Size = 11
objExcel.Cells(9,3).Value = "Number"
objExcel.Cells(9,3).Font.Bold = True
objExcel.Cells(10,3).Value = g_intSP1
objExcel.Cells(9,4).Value = "Names"
objExcel.Cells(9,4).Font.Bold = True

arrSP1 = Split(g_strSP1, ",")
x = 10
For Each strSP1 In arrSP1
  objExcel.Cells(x,4).Value = strSP1
  x = x + 1
Next
Set objRange = objExcel.Range("D1")
objRange.Activate
Set objRange = objExcel.ActiveCell.EntireColumn
objRange.AutoFit()
Set objSortRange = objExcel.Range("D9:D" & x)
Set objSortKey = objExcel.Range("D10")
objSortRange.Sort objSortKey,xlAscending,,,,,,xlYes

objExcel.Cells(8,5).Value = "No Service Pack"
objExcel.Cells(8,5).Font.Bold = True
objExcel.Cells(8,5).Font.Size = 11
objExcel.Cells(9,5).Value = "Number"
objExcel.Cells(9,5).Font.Bold = True
objExcel.Cells(10,5).Value = g_intSP0
objExcel.Cells(9,6).Value = "Names"
objExcel.Cells(9,6).Font.Bold = True

arrSP0 = Split(g_strSP0, ",")
x = 10
For Each strSP0 In arrSP0
  objExcel.Cells(x,6).Value = strSP0
  x = x + 1
Next
Set objRange = objExcel.Range("F1")
objRange.Activate
Set objRange = objExcel.ActiveCell.EntireColumn
objRange.AutoFit()
Set objSortRange = objExcel.Range("F9:F" & x)
Set objSortKey = objExcel.Range("F10")
objSortRange.Sort objSortKey,xlAscending,,,,,,xlYes

objExcel.Cells(4,7).Value = "Not Running Windows XP"
objExcel.Cells(4,7).Font.Bold = True
objExcel.Cells(4,7).Font.Size = 12
objExcel.Cells(5,7).Value = "Number"
objExcel.Cells(5,7).Font.Bold = True
objExcel.Cells(6,7).Value = g_intNotXP
objExcel.Cells(5,8).Value = "Names"
objExcel.Cells(5,8).Font.Bold = True

arrNotXP = Split(g_strNotXP, ",")
x = 6
For Each strNotXP In arrNotXP
  objExcel.Cells(x,8).Value = strNotXP
  x = x + 1
Next
Set objRange = objExcel.Range("H1")
objRange.Activate
Set objRange = objExcel.ActiveCell.EntireColumn
objRange.AutoFit()
Set objSortRange = objExcel.Range("H5:H" & x)
Set objSortKey = objExcel.Range("H6")
objSortRange.Sort objSortKey,xlAscending,,,,,,xlYes

'Move active cell back to A1.
Set objRange = objExcel.Range("A1")
objRange.Activate

'Force save and close spreadsheet.
objExcel.DisplayAlerts = False
Set objWorkbook = objExcel.ActiveWorkbook
objWorkbook.SaveAs strFileName
objWorkbook.Close
objExcel.Quit

End Sub

'******************************************************************************

'Handle errors.
Sub HandleError(Err, strMsg)

On Error Resume Next

WScript.Echo "  " & strMsg
WScript.Echo "    Error Number: " & Err.Number
WScript.Echo "    Source: " & Err.Source
WScript.Echo "    Description: " & Err.Description
WScript.Quit

End Sub

Pour récompenser nos efforts, nous obtenons un magnifique rapport présenté dans une feuille de calcul Excel avec des listes alphabétiques de tous les clients Windows XP du conteneur et de ses conteneurs enfants, classés par Service Pack. Si vous aviez réellement voulu impressionner votre patron, vous auriez pu améliorer la mise en forme, mettre en surbrillance le travail à réaliser et ajouter des graphiques, des animations et de la musique. Nous n'avons ici qu'exploré une infime partie des vastes possibilités offertes par Excel. Si vous souhaitez aller plus loin, reportez-vous aux deux articles rédigés par Monsieur Script Greg Stemp sur MSDN (consultez les liens à la fin de l'article).

feuille de calcul

Pendant que le script est encore en cours d'exécution, souvenez-vous que nous avons en mémoire une liste de tous les clients Windows XP sur lesquels le Service Pack 2 n'a pas encore été installé : il ne nous reste plus qu'à concaténer les deux variables globales g_strSP1 et g_strSP0. Dans notre prochain article, le dernier de cette série de trois articles sur le Service Pack 2 (mais pas du magasin de scripts du Docteur Scripto), nous vous expliquerons comment utiliser ces informations pour installer le Service Pack 2 sur ces ordinateurs. Le code que nous allons explorer peut être intégré dans ce script (pour les environnements Active Directory) ou dans le script présenté dans le premier article de cette série (pour les réseaux qui n'utilisent pas Active Directory).

Pour en savoir plus

ADSI

ADO

Excel

Windows XP Service Pack 2