AD Magic Restore: outil de restauration Active Directory

Sommaire

 

 

Introduction

AD Magic Restore est un outil qui va vous permettre de restaurer rapidement et en toute simplicité vos objets Active Directory de manière complète ou partielle. Il est publie sur Codeplex depuis le lien suivant: http://admagicrestore.codeplex.com/

L’avantage de mon outil par rapport à Corbeille AD (pour ceux qui ont la Corbeille AD…) : la fonctionnalité de comparaison et de restauration granulaire.

L’outil est un script développé en PowerShell. Il dispose d’une interface graphique pour faciliter son usage.

Il s’appuie essentiellement sur les composants et/ou outils suivants:

  • DSAMAIN pour l’instanciation du fichier ntds.dit
  • Les clichés instantanés de volume (volume shadow copy) ou sauvegarde locale réalisé avec Windows Server Backup (le remplaçant de NTBackup)
  • Module PowerShell Active Directory de Microsoft
  • Web Services Active Directory ou service passerelle de gestion Active Directory
  • Esentutil, diskpart…

 

 

Pre-requis et limitations

L’outil exige qu’il soit exécuté à minima sur un serveur membre Windows Server 2008 avec la fonctionnalité Windows ‘AD-Domain-Services’ (module Active Directory et outil DSAMAIN) et que vous disposiez du web service Active Directory sur au moins un de vos contrôleurs de domaine (disponible dès Windows Server 2003 R2). Si vous respectez ces pré-requis, vous pourrez au moins instancier votre base de données depuis un fichier NTDS.DIT récupéré depuis un de vos contrôleurs de domaine restaurer vos objets de votre fichier vers la base de production.

Si vous voulez exploiter les fonctionnalités « Snaphots » ou/et « Backups », il sera impératif d’utiliser l’outil directement depuis un contrôleur de domaine.

L’outil ne fait tous les miracles. Il ne peut pas restaurer un objet purgé. En effet, si votre objet a été purgé de la base, l’outil ne sera pas en mesure de restaurer des attributs tel que le objectSid ou l’objectGUID. Cela signifie que la restauration de vos objets ne pourra se faire une fois que l’objet supprimé à dépasser la valeur de rétention définie par l’attribut « tombstonelifetime » (60 jours ou 180 jours par défaut selon votre environnement).

L’outil a été pas mal éprouvé mais j’ai rajouté au fur et à mesure certaines nouvelles fonctionnalités qui peuvent générer des bugs. Utilisez-le Donc avec précautions. D’ailleurs toute remontée sera la bienvenue (ajout de fonctionnalités, bugs…).

Pour le moment, je n’ai pas pu tout testé mai j’ai déjà quelques autres mises en garde et/ou limitations :

  • Restauration du mot de passe impossible
  • Restauration des clés BitLocker non-testée
  • Restauration de l’attribut « sIDHistory » non-testée
  • Identification des attributs protégés non-exhaustive (possibilité de gérer manuellement une liste d’exceptions depuis la variable « $script:CustomExcludedAttributes »)

 

 

Présentation générale


L’interface graphique est constituée de trois parties principales :

  • « Data Source » (1): sélection de la source de données utilisée par le script pour restaurer vos objets sur votre environnement de production. Vous pouvez utiliser un sauvegarde locale (sauvegarde complète sur un volume dédié avec un point de montage), un cliché instantané (via ntdsutil, mon script), en spécifiant un fichier NTDS.DIT ou en vous connectant à une instance DSAMAIN distante.
  • « Data Destination » (2): sélection de la partition Active Directory de destination. En général vous utiliserez ce qu’on appelle la  partition « default naming context » mais si vous devez restaurer, par exemple un sous-réseau, il faudra passer par la partition de « configuration », pour un enregistrement DNS vous sélectionnerez probablement la partition « domaindnszones » ou « forestdnszones ».
  • « Search » (3): sur les trois, c’est la plus importante. Depuis cette section, vous serez à même de rechercher les objets depuis votre instance AD source et de restaurer les objets ou/et attributs sur l’AD de destination. Les options de comparaison et de restauration sont accessibles à l’aide d’un menu contextuel qui s’affiche lorsque vous faites un clic droit sur l’objet sélectionné.

 

 

La zone « Data Source »

  • Connection : vous pouvez pointer à peu près sur n’importe quelle instance LDAP. La seule condition est de disposer du Web Service Active Directory.

 

  • Exploitation d’une sauvegarde : le script détecte la présence de sauvegardes complètes et stockées sur un volume dédié à l’aide de Windows Server Backup. Pour garantir le fonctionnement de cette fonctionnalité, vous devrez à minima attribuer une lettre de lecteur à ce volume (par défaut ce n’est pas le cas avec Windows Server Backup). Pour faire simple, le script monte le cliché instantané de la sauvegarde sélectionnée. Il monte ensuite le VHD de la sauvegarde à l’aide de « DISKPART »  afin de pouvoir en parcourir le contenu. Enfin, il instancie le fichier « ntds.dit » à l’aide de « DSAMAIN ».

 

  • Exploitation d’un clic instantané : liste les clichés instantanés du volume dédié à votre base Active Directory, monte le cliché sélectionné et l’instancie à l’aide de « DSAMAIN ».

 

  • Exploitation d’un fichier NTDS : vous pouvez spécifier un fichier NTDS de votre choix que l’outil se chargera d’instancier. Les options « Repair file » et « Upgrade database » sont impératives si vous spécifiez un fichier « ntds.dit » provenant d’un contrôleur de domaine Windows Server 2003.

 

Pour finir, vous disposez de deux options spécifiques pour l’exploitation de votre source. La première concernant la durée d’attente avant que l’instanciation soit annulée. Par défaut, elle est définie à 2 minutes mais elle devra sans doute être augmentée si votre base est volumineuse et que vous faire une réparation via l’onglet « File ».

La deuxième option concerne le port sur lequel la base sera instanciée par NTDS.  Je vous conseille de jouer avec en cas de problème pour instancier votre source. Ça peut débloquer pas mal de situations…

 

 

La zone « Data Destination »

Pas besoin de trop s’attarder sur cette partie. Vous disposez par défaut de plusieurs partitions d’application sur Active Directory. Selon le type d’objet à restaurer, il vous faudra sélectionner la bonne. De plus, ce n’est pas très explicite mais en réalité cela détermine aussi la partition source utilisée par le script.

 

 

La zone « Search »

Comme je l’ai dit plus haut, c’est la zone principale du script. C’est à partir d’elle que vous réaliserez les opérations de recherche, de comparaison et de restauration.  Je vais avant tout vous présenter les différents filtres de recherche des objets. A noter que les recherches sont réalisées systématiquement depuis la source de données.

 

  • « Filter » (1) : pour spécifier le nom ou le nom d’ouverture de session (attribut « sAMAccountName ») de l’objet à retrouver depuis votre source de données. Notez que vous pouvez utiliser un « wildcard » (une étoile), pour vos recherches.

 

  • « Container » (2) : pour retourner uniquement les objets situé dans une unité d’organisation spécifique. Vous pouvez le coupler avec les autres options de filtre.

 

  • Les cases à cocher « Login » et « Name » (3) : définis principalement l’attribut utiliser par « Filter » (1). Par défaut, la recherche sera faite exclusivement sur l’attribut Active Directory  « sAMAccountName » (cela correspond à la case à cocher « Login »). Vous pouvez également ajouter dans vos critères de recherche l’attribut Active Directory « Name ». Cela peut s’avérer particulièrement utile pour les objets ne disposant pas de l’attribut « sAMAccountName ».
  • « Result size » (4) : vous permettra de limiter le nombre de résultats retourné sur votre recherche.
  • « Class » (5) : un autre filtre qui offre la possibilité de retourner des objets d’une classe spécifique.  Il s’agit en réalité d’un filtre sur l’attribut Active Directory « objectClass ».
  • Le bouton « Search » (6) et « Cancel » (7) : Lorsque que vous avez spécifié vos critères de recherche, vous pouvez l’exécuter en cliquant sur le bouton « Search ». Pour une raison ou une autre, vous pouvez décider l’annuler à l’aide du bouton « Cancel ».

 

Une fois que la recherche vous retourne le ou les objets voulus, vous pouvez faire apparaître une liste d’actions en sélectionnant l’objet et en réalisant un clic doit depuis la zone de résultat. Un menu contextuel apparait. Les actions disponibles depuis ce menu vont varier en fonction de la nature de l’objet (par exemple s’il s’agit d’un groupe ou d’un compte utilisateur) ou en fonction de son état (objet supprimé ou présent dans l’annuaire de destination.

Exemple 1 : un objet utilisateur supprimé sur votre annuaire de destination.

 

Exemple 2 : un objet groupe existant sur votre annuaire de destination.

 

 

Liste des opérations possibles depuis le menu contextuel

Vous avez donc la possibilité de réaliser différentes actions sur un objet en fonction de son état ou de sa classe. Les actions sont disponibles depuis un menu contextuel qui apparait lorsque vous sélectionnez un objet et que vous faites un clic droit sur un des résultats obtenus dans la zone « Search ».

Les actions sont :

  • « Reanimate » : pour réanimer un objet tombstone.
  • « Restore Object » : pour restaurer de manière complète un objet. Cette action englobe l’action « Reanimate », « Compare » (restauration des attributs), « Restore Memberships » et éventuellement « Restore Members » si l’objet est un groupe ou « Restore Subtree » si l’objet dispose de sous-objet (Unité d’organisation, objet utilisateur, objet ordinateur…).
  • « Compare » : pour comparer les différences entre un objet source et un objet de destination. Il est possible de restaurer les valeurs de chaque attribut de la source vers la destination.

 

  • « Restore Memberships » : pour visualiser les appartenances éventuellement manquantes sur l’objet de destination par rapport à l’objet source. Le cas échéant, vous pouvez restaurer tout ou partiellement ces appartenances.

 

  • « Restore Members » : cette action n’est disponible que pour les objets groupe. Elle permet d’obtenir la liste des membres d’un groupe manquant entre la source et la destination. De la même manière que la fonctionnalité « Restore Memberships », elle vous permet de restaurer tout ou partiellement les membres d’un groupe.

 

  • « Restore Subtree » : Cette action permet de restaurer les objets enfants de l’objet à restaurer. L’action est uniquement disponible si l’objet dispose réellement de sous-enfants. Cela concerne principalement les unités d’organisation mais pas seulement. En effet, par exemple les objets utilisateur et ordinateur sont considérés comme des conteneurs pouvant disposer d’objets enfants.

ATTENTION : cette fonctionnalité peut être utilisée pour restaurer une hiérarchie complète  d’objets mais le script est surtout développé pour de la restauration granulaire. Pour restaurer une hiérarchie complète, je vous conseille vivement d’utiliser une restauration autoritaire.

 

 

Restaurer un objet

Je vous fais maintenant une présentation pas-à-pas d’une restauration d’un objet utilisateur.

La première étape consiste à sélectionner une source de données. Vous avez du choix… dans l’exemple ci-dessous, je choisi un cliché instantané. En cliquant sur le bouton « Connect », le script me monte le cliché instantané et me l’instancie à l’aide de « DSAMAIN ».

 

Ensuite, il faut sélectionner la partition Active Directory de destination depuis la liste déroulante et en cliquant sur le bouton « Select ».

 

Je lance ma recherche (voir la présentation de la zone « Search » pour plus de détails). Faites un clic droit sur l’objet à restaurer et sélectionner l’action « Reanimate ». Vous remarquez sans doute l’action « Restore Object » mais je vous en parlerez un peu plus tard.

 

Le script vous demande une confirmation sur la réanimation de l’objet. Une fois confirmée, le script doit vous retourner le résultat de la réanimation.

 

Pour ceux qui ne savent pas exactement ce qu’est une réanimation, je vais faire simple. Lorsque vous supprimez un objet dans l’AD, il est conservé temporairement  et pour une durée déterminée afin de faire répliquer cette demande de suppression sur l’ensemble des contrôleurs de domaine de votre domaine. Pour limiter l’espace utilisé par cet objet qui est considéré comme supprimé, il est dépourvu de la plus part de ces attributs (par exemple ces appartenances). On peut considérer cet objet comme supprimé mais pas encore purgé et on peut donc intervenir avant la purge pour qu’il redevienne un objet au sens classique du terme. On appelle cette opération une réanimation d’objet tombstone. Par contre, et c’est là que le script intervient, la réanimation ne peut restaurer les attributs qui ont été définitivement supprimés lors de la demande de suppression.

Nous allons restaurer maintenant les attributs de l’objet réanimé. Sélectionnez de nouveau l’objet et Refaites un clic droit sur la zone de résultats. Si tout se passe bien, vous devriez avoir l’action « Compare » qui apparait. Sélectionnez-là.

 

Une nouvelle fenêtre apparait. Elle vous liste l’ensemble des attributs divergents entre la source et la destination. Sélectionnez ceux que vous désirez restaurer et cliquez sur le bouton « Restore ». Le script doit vous retourner le résultat de l’opération.

 

La dernière étape consiste à restaurer les appartenances aux groupes de vos objets. C’est une fonctionnalité  que j’ai décidé de traiter séparément des autres attributs afin de pouvoir avoir une réelle granularité dans la restauration des appartenances.

Faites donc de nouveau un clic droit sur l’objet sélectionné et cliquez sur l’action « Restore Memberships ».

 

Une nouvelle fenêtre apparait. Vous obtenez la liste des groupes auxquels l’objet appartient depuis la source de données. Il vous suffit des sélectionner tout ou une partie de ces groupes et de cliquer sur le bouton « Restore ».

 

Votre objet est restauré ! Pas mal non ? Peut-être un peu fastidieux toutefois… mais vous pouvez faire plus rapide.

Je vous ai montré  dans le détail les étapes de restauration pour que vous ayez une meilleure compréhension de l’outil. Mais, en fait, en sélectionnant l’option « Restore Object », vous faites la réanimation, la restauration des attributs et des appartenances en une seule fois. Encore mieux n’est-ce pas ?

 

 

La zone « Tombstones »

Lorsque vous cliquez sur le bouton « View / Manage » en bas à gauche de la fenêtre principale, vous accédez à une nouvelle fenêtre dédiée aux objets tombstones.

 

Les deux avantages principaux sont :

  • Une recherche directe depuis votre annuaire de production
  • La restauration de plusieurs objets à la fois.

 

La fenêtre « Manage Tombstones » est assez similaire à la zone « Search ». Des fonctionnalités de filtres de recherche pour aider à retrouver rapidement le ou les objets voulus (1). La zone de résultats n’intègre pas de menu contextuel mais, par contre, vous pouvez sélectionner plusieurs objets (3). Une fois fait, vous pouvez choisir soit de réanimer, soit de restaurer (4) les objets sélectionnés.

Passer par « Manage Tombstones » peut s’avérer utile lorsque vous êtes certains de votre source de données. En fait, vous n’avez aucun contrôle sur la source donc il est possible que vous puissiez seulement réanimer vos objets sans en restaurer l’ensemble de leurs attributs. A utiliser donc avec précaution.

Identify writeable AD attributes with PowerShell

You should be able to retrieve easily the list of attributes which are writeable by using the constructed attribute allowedAttributesEffective (http://msdn.microsoft.com/en-us/library/ms675218(v=vs.85).aspx).

{codecitation style= »brush: PowerShell »}

$ADObject = New-Object system.DirectoryServices.DirectoryEntry (« LDAP://CN=alexandre augagneur,CN=Users,DC=corpnet,DC=net »)

$ADObject.RefreshCache(« allowedAttributesEffective »)

$ADObject.properties.allowedAttributesEffective

{/codecitation}

However, the constructed attribute returns some attributes which can’t be changed… So we have to do something more.

The best way I found is to retrieve all attributes which are protected by the system (http://msdn.microsoft.com/en-us/library/ms680025(v=vs.85).aspx). When it’s done, i’m just removing each of them from the list of allowed attributes returned by the constructed attribute allowedAttributesEffective.

{codecitation style= »brush: PowerShell »}

$SystemOnlyAttributes = @()

$TrulyAllowedAttributes = @()

 

 

# Get the desired object based on its distinguishedName

$ADObject = New-Object system.DirectoryServices.DirectoryEntry (« LDAP://CN=alexandre augagneur,CN=Users,DC=corpnet,DC=net »)

 

# Retrieve the constructed attribute ‘allowedAttributesEffective’

$ADObject.RefreshCache(« allowedAttributesEffective »)

 

# Store the list of allowed attributes  in a variable

$allowedAttributesEffective = $ADObject.properties.allowedAttributesEffective

 

# Retrieve the list of attributes in the schema which are protected

$ObjRootDSE = [ADSI] « LDAP://RootDSE »

$ADSearcher = new-object system.DirectoryServices.DirectorySearcher

$ADSearcher.SearchRoot = [ADSI] « LDAP://$($ObjRootDSE.schemaNamingContext) »

$ADSearcher.PropertiesToLoad.AddRange(@(« ldapdisplayname », »systemonly »))

$ADSearcher.Filter = « (systemonly=TRUE) »

$ADSearcher.FindAll() | %{ $SystemOnlyAttributes += $_.Properties.ldapdisplayname }

 

# Compare the list of allowed attributes returned by the constructed attribute

# with the list of protected attributes collected in the schema

foreach ( $Attribute in $allowedAttributesEffective )

{

if ( $SystemOnlyAttributes -notcontains $Attribute )

{

$TrulyAllowedAttributes += $Attribute

}

}

# The most efficient list of writeable attributes

$TrulyAllowedAttributes

{/codecitation}

 

If you have a better way… you are welcome !

Identifier les groupes de l’utilisateur courant avec PowerShell

Je vais vous montrer rapidement comment vérifier qu’un utilisateur appartient à un groupe en PowerShell. Cela vous sera sans doute utile pour la mise en place de login script en PowerShell et abandonner les .bat, .kix et tutti quanti.

Premièrement on récupère l’utilisateur Windows courant :

{codecitation style= »brush: PowerShell »}

$CurrentWinId = [System.Security.Principal.WindowsIdentity]::GetCurrent()

{/codecitation}

 

L’objet WindowsIndentity contient la propriété « Groups » listant l’ensemble des appartenances de l’utilisateur. Par contre,  la liste retourne uniquement les groupes par leur SID.

 

Du coup, vous avez deux solutions. La première solution consiste à réaliser une translation SID/Nom à l’aide de la méthode Translate et de stocker le résultat dans un tableau.

{codecitation style= »brush: PowerShell »}

$arrTranslatedGroups = @()

Foreach ($Group in $CurrentWinId.Groups)

{

$arrTranslatedGroups += $Group.translate([System.type]::GetType(« System.Security.Principal.NTAccount »)).Value

}

{/codecitation}

 

Il suffit de vérifier si un groupe est présent à partir du tableau généré à l’aide de l’opérateur « -contains » qui retournera « True » ou « False » :

{codecitation style= »brush: PowerShell »}

$arrTranslatedGroups -contains « NT AUTHORITY\Authenticated Users »

{/codecitation}

 

La deuxième solution consiste a converti dans un premier temps notre objet WindowsIdentity en WindowsPrincipal.

{codecitation style= »brush: PowerShell »}

$CurrentWindowsPrincipal = new-object System.Security.Principal.WindowsPrincipal($CurrentWinId)

{/codecitation}

 

L’objectif étant de pouvoir utiliser la méthode « IsInRole ».

{codecitation style= »brush: PowerShell »}

$CurrentWindowsPrincipal.IsInRole(« CORPNET\group123 »)

$CurrentWindowsPrincipal.IsInRole(« NT AUTHORITY\Authenticated Users »)

{/codecitation}

Manipuler Excel avec PowerShell : Part 4 – Ajouter un graphique

Dans l’article précédent, je vous ai montré comment mettre en forme votre tableau sous Excel à l’aide de PowerShell.

Nous avions obtenu le résultat ci-dessous.

 

Dans cet article, je vais vous montrer comment ajouter un graphique.

 

On commence par instancier notre objet de type « Chart ».

{codecitation style= »brush: PowerShell »}

$Chart = $WorkSheet.Shapes.AddChart().Chart

{/codecitation}

 

On va maintenant définir le type de notre graphique. Pour cela, il faut choisir parmi la longue liste que propose Excel. Ils sont énumérés depuis le lien http://msdn.microsoft.com/en-us/library/office/bb241008(v=office.12).aspx avec la valeur correspondante. Je vais partir sur un « histogramme 3D groupé ».

{codecitation style= »brush: PowerShell »}

$Chart.ChartType = 54

{/codecitation}

 

On peut spécifier également un style à notre graphique via la propriété « ChartStyle ». Il est assez dur de les identifier et d’ailleurs la documentation Microsoft parle d’une valeur possible comprise entre 1 et 48 mais j’ai pu définir mon style à une valeur bien plus élevée… J’ai donc fait un petit try-catch au cas où…

{codecitation style= »brush: PowerShell »}

try

{

$Chart.ChartStyle = 288

}

catch

{

$chart.ChartStyle = 1

}

{/codecitation}

 

On va continuer sur la mise en forme. On va désactiver les légendes à l’aide de la propriété « HasLegend ». On va activer le titre avec les propriétés « HasTitle » et « Text ».

{codecitation style= »brush: PowerShell »}

$Chart.HasLegend = $false

$Chart.HasTitle = $true

$Chart.ChartTitle.Text = « Production de bananes (en tonnes) »

{/codecitation}

 

Nous en avons terminé avec la mise en forme… On va s’attaquer aux données. On va ajouter une nouvelle série avec les années en abscisse (propriété « XValues ») et les tonnes en ordonnée (propriété « Values »). Pour cela on va utiliser des étendues.

{codecitation style= »brush: PowerShell »}

$Chart.SeriesCollection(1).XValues = $WorkSheet.Range($WorkSheet.Cells.Item(4,1),$WorkSheet.Cells.Item(7,1))

$Chart.SeriesCollection(1).Values = $WorkSheet.Range($WorkSheet.Cells.Item(4,2),$WorkSheet.Cells.Item(7,2))

{/codecitation}

 

Il y a différentes solutions en fonction des données à traiter, le nombre de séries ou le type de graphique mais vous avez déjà un bon exemple à votre disposition.

La toute dernière étape est le positionnement du graphique… A ma connaissance, la meilleure solution est de s’appuyer sur une étendue pour définir le positionnement et la taille. Ça pourrait paraitre plus compliqué mais au final vous verrez que s’est nettement plus pratique, surtout si vous êtes maniaque…

On créée donc une nouvelle étendue.

{codecitation style= »brush: PowerShell »}

$RangePositionChart = $WorkSheet.Range($WorkSheet.Cells.Item(10,1),$WorkSheet.Cells.Item(25,4))

{/codecitation}

 

On va ensuite utiliser l’objet parent contenant le graphique et définir la taille et le positionnement par rapport à notre étendue définie préalablement.

{codecitation style= »brush: PowerShell »}

$ChartObj = $Chart.Parent

$ChartObj.Height = $RangePositionChart.Height

$ChartObj.Width = $RangePositionChart.Width

$ChartObj.Top = $RangePositionChart.Top

$ChartObj.Left = $RangePositionChart.Left

{/codecitation}

 

Et voila le résultat !

 

Et le script au complet:

{codecitation style= »brush: PowerShell »}

[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo] « en-US »

 

$Excel = New-Object -ComObject « Excel.Application »

 

$WorkBook = $Excel.Workbooks.Add()

 

$WorkSheet = $WorkBook.WorkSheets.Add()

$WorkSheet.Name = « Production de bananes »

$WorkSheet.Select()

 

$WorkSheet.Cells.Item(1,1) = « Production annuelle de bananes »

 

$ArrProduction = @((‘2010’,’4.5’),(‘2011’,’12’),(‘2012’,’11.5’),(‘2013’,’15’))

 

$WorkSheet.Cells.Item(3,1) = « Année »

$WorkSheet.Cells.Item(3,2) = « Production (en tonnes) »

 

$InitialRow = 4

 

for ( $i=0; $i -lt $ArrProduction.Count; $i++ )

{

$WorkSheet.Cells.Item($InitialRow,1) = $ArrProduction[$i][0]

$WorkSheet.Cells.Item($InitialRow,2) = [decimal] $ArrProduction[$i][1]

$InitialRow++

}

 

$RangeTitle = $WorkSheet.Range($WorkSheet.Cells.Item(1,1),$WorkSheet.Cells.Item(1,2))

$RangeTitle.MergeCells = $true

$RangeTitle.Style = ($WorkBook.Styles.Item(43)).Name

$RangeTitle.HorizontalAlignment = [Microsoft.Office.Interop.Excel.XlHAlign]::xlHAlignCenter

$RangeTitle.ColumnWidth = 20

 

$RangeTable = $WorkSheet.Range($WorkSheet.Cells.Item(3,1),$WorkSheet.Cells.Item(7,2))

$ListObject = $WorkSheet.ListObjects.Add(1,$RangeTable,$null,1,$null)

$ListObject.TableStyle = « TableStyleLight6 »

 

$ListObject.ShowTotals = $true

$ListObject.ShowHeaders = $true

$ListObject.ShowAutoFilterDropDown = $false

 

$RangeSort = $WorkSheet.Range($WorkSheet.Cells.Item(4,1).Address($False,$False))

$WorkSheet.Sort.SortFields.Add($RangeSort,0,1) | Out-Null

$WorkSheet.Sort.SetRange($RangeTable)

$WorkSheet.Sort.Header = 1

$WorkSheet.Sort.Apply()

 

$Chart = $WorkSheet.Shapes.AddChart().Chart

$Chart.ChartType = 54

 

try

{

$Chart.ChartStyle = 288

}

catch

{

$chart.ChartStyle = 1

}

 

$Chart.HasLegend = $false

$Chart.HasTitle = $true

$Chart.ChartTitle.Text = « Production de bananes (en tonnes) »

 

$Chart.SeriesCollection(1).XValues = $WorkSheet.Range($WorkSheet.Cells.Item(4,1),$WorkSheet.Cells.Item(7,1))

$Chart.SeriesCollection(1).Values = $WorkSheet.Range($WorkSheet.Cells.Item(4,2),$WorkSheet.Cells.Item(7,2))

 

$RangePositionChart = $WorkSheet.Range($WorkSheet.Cells.Item(10,1),$WorkSheet.Cells.Item(25,4))

$ChartObj = $Chart.Parent

$ChartObj.Height = $RangePositionChart.Height

$ChartObj.Width = $RangePositionChart.Width

$ChartObj.Top = $RangePositionChart.Top

$ChartObj.Left = $RangePositionChart.Left

 

$WorkBook.SaveAs(« c:\temp\MaProductionDeBananes.xlsx »)

$Excel.Visible = $true

{/codecitation}

 

Grâce à cette séries d’articles, v devriez avoir bonnes bases pour commencer à générer vos tableaux et graphiques sur Excel à l’aide de Powershell.

Si vous voulez un exemple plus complexe, vous pouvez également consulter le code d’un de mes scripts depuis le lien technet suivant : http://gallery.technet.microsoft.com/scriptcenter/Generate-Excel-report-48c3f3b4

Il y a également de très nombreux exemples en VBA sur Internet qui vous permettra de vous guider sur les différentes méthodes et propriétés à utiliser.

Enfin, vous avez également le site MSDN dont j’ai fait assez souvent référence et qui est plutôt bien documenté !

Pour les autres articles sur le sujet:

Manipuler Excel avec PowerShell : Part 3 – Mettre en forme un tableau

Dans l’article précédent, je vous ai montré comment appliquer un style à une cellule pour obtenir le fichier Excel suivant:

 

Maintenant, on va aller un peu plus loin et on va manipuler le tableau et lui appliquer un style.  On va définir l’étendue de notre tableau (dans notre exemple de A3 à B7) :

{codecitation style= »brush: PowerShell »}

$RangeTable = $WorkSheet.Range($WorkSheet.Cells.Item(3,1),$WorkSheet.Cells.Item(7,2))

{/codecitation}

 

On va définir notre tableau en un type « ListObjects » (http://msdn.microsoft.com/en-us/library/ff196851(v=office.14).aspx)  et en utilisant la méthode « add() » (http://msdn.microsoft.com/en-us/library/office/bb211863(v=office.12).aspx). Ceci va nous permettre de profiter de l’ensemble des fonctions.

{codecitation style= »brush: PowerShell »}

$ListObject = $WorkSheet.ListObjects.Add(1,$RangeTable,$null,1,$null)

{/codecitation}

Nous n’avons plus qu’à appliquer un style de tableau Excel existant. Pour obtenir la liste complète, vous pouvez utiliser la commande « $WorkBook.TableStyles | ft name ». Il s’agit de la liste disponible depuis « Mettre sous forme de tableau » sous Excel.

 

Je vais appliquer le style « TableStyleLight6 »:

{codecitation style= »brush: PowerShell »}

$ListObject.TableStyle = « TableStyleLight6 »

{/codecitation}

Comme c’était le cas pour les styles de cellules, on peut appeler un style de tableau par son nom ou par son index. Il semblerait toutefois que les noms des styles de tableau ne soient pas traduits dans la langue locale… Par contre, certains styles peuvent avoir le même nom et être différents entre les versions d’Excel. C’est le cas du style « TableStyleLight6 » qui n’est pas le même entre Excel 2007 et Excel 2013… Je n’ai pas de solution miracle sachant que la création d’un style de tableau est beaucoup complexe que la création d’un style de cellules.

 

On pourrait affiner la mise en forme en permettant, par exemple, l’affichage de la ligne des totaux :

{codecitation style= »brush: PowerShell »}

$ListObject.ShowTotals = $true

{/codecitation}

…ou en désactivant le filtrage automatique :

{codecitation style= »brush: PowerShell »}

$ListObject.ShowAutoFilterDropDown = $false

{/codecitation}

C’est un exemple parmi tant d’autres… mais voici ce que l’on peut déjà obtenir visuellement.

 

Pour conclure cet article, on va corser un peu tout cela et on va essayer de trier les données. L’objectif est de classer par ordre croissant la production de manière automatique. Je ne vous cache que j’ai eu pas mal de difficultés pour y arriver et qu’il y a peut-être plus simple…

Premièrement, je défini l’étendue sur laquelle portera le tri. Je désigne simplement la première cellule concernée soit la cellule B4.

{codecitation style= »brush: PowerShell »}

$RangeSort = $WorkSheet.Range(« B4 »)

{/codecitation}

J’aurai pu très bien utiliser la valeur numérique pour identifier la position de la cellule. Ça peut être très pratique lorsque l’on veut améliorer l’automatisation. Par contre, comme la fonction « Range » ne peut pas interpréter la position d’une cellule par sa valeur numérique seule, il faut la convertir.

{codecitation style= »brush: PowerShell »}

$RangeSort = $WorkSheet.Range($WorkSheet.Cells.Item(4,2).Address($False,$False))

{/codecitation}

On utilise ensuite la méthode SortFields.Add() pour la création de notre tri (http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.sortfields.add.aspx). La syntaxe est .Add(MonEtendue, SortOn, Order, CustomOrder, DataOption) (consulter les liens pour obtenir les différentes valeurs possibles).

{codecitation style= »brush: PowerShell »}

$WorkSheet.Sort.SortFields.Add($RangeSort,0,1) | Out-Null

{/codecitation}

On définit l’étendue affectée par le tri. En l’occurrence il s’agit de notre tableau.

{codecitation style= »brush: PowerShell »}

$WorkSheet.Sort.SetRange($RangeTable)

{/codecitation}

Il nous reste plus qu’à exclure l’entête du triage à l’aide la propriété « Header » de l’objet « Sort ». Pour la liste des valeurs possibles consulter le lien http://msdn.microsoft.com/fr-fr/library/office/ff838812.aspx.

{codecitation style= »brush: PowerShell »}

$WorkSheet.Sort.Header = 1

{/codecitation}

Nous achevons le tri via la méthode Apply().

{codecitation style= »brush: PowerShell »}

$WorkSheet.Sort.Apply()

{/codecitation}

Et voila le résultat!

 

Et le script au complet:

{codecitation style= »brush: PowerShell »}

[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo] « en-US »

 

$Excel = New-Object -ComObject « Excel.Application »

 

$WorkBook = $Excel.Workbooks.Add()

 

$WorkSheet = $WorkBook.WorkSheets.Add()

$WorkSheet.Name = « Production de bananes »

$WorkSheet.Select()

 

$WorkSheet.Cells.Item(1,1) = « Production annuelle de bananes »

 

$ArrProduction = @((‘2010’,’4.5’),(‘2011’,’12’),(‘2012’,’11.5’),(‘2013’,’15’))

 

$WorkSheet.Cells.Item(3,1) = « Année »

$WorkSheet.Cells.Item(3,2) = « Production (en tonnes) »

 

$InitialRow = 4

 

for ( $i=0; $i -lt $ArrProduction.Count; $i++ )

{

$WorkSheet.Cells.Item($InitialRow,1) = $ArrProduction[$i][0]

$WorkSheet.Cells.Item($InitialRow,2) = [decimal] $ArrProduction[$i][1]

$InitialRow++

}

 

$RangeTitle = $WorkSheet.Range($WorkSheet.Cells.Item(1,1),$WorkSheet.Cells.Item(1,2))

$RangeTitle.MergeCells = $true

$RangeTitle.Style = ($WorkBook.Styles.Item(43)).Name

$RangeTitle.HorizontalAlignment = [Microsoft.Office.Interop.Excel.XlHAlign]::xlHAlignCenter

$RangeTitle.ColumnWidth = 20

 

$RangeTable = $WorkSheet.Range($WorkSheet.Cells.Item(3,1),$WorkSheet.Cells.Item(7,2))

$ListObject = $WorkSheet.ListObjects.Add(1,$RangeTable,$null,1,$null)

$ListObject.TableStyle = « TableStyleLight6 »

 

$ListObject.ShowTotals = $true

$ListObject.ShowHeaders = $true

$ListObject.ShowAutoFilterDropDown = $false

 

$RangeSort = $WorkSheet.Range($WorkSheet.Cells.Item(4,2).Address($False,$False))

$WorkSheet.Sort.SortFields.Add($RangeSort,0,1) | Out-Null

$WorkSheet.Sort.SetRange($RangeTable)

$WorkSheet.Sort.Header = 1

$WorkSheet.Sort.Apply()

 

$WorkBook.SaveAs(« c:\temp\MaProductionDeBananes.xlsx »)

$Excel.Visible = $true

{/codecitation}

Nous avons donc vu dans cette troisième partie comme manipuler et mettre en forme notre tableau Excel. Dans la quatrième partie, on va s’attaquer à la création du graphique.

Pour les autres articles sur le sujet:

Manipuler Excel avec PowerShell : Part 2 – Appliquer un style à vos cellules

Dans le premier article, je vous ai montré comment créer votre premier fichier Excel et l’alimenter avec PowerShell pour obtenir le résultat suivant :

 

Dans cet article, on va s’attarder un peu plus sur la mise en forme.

On va commencer par fusionner les cellules A1 et B1. Pour cela, il va falloir définir une étendue ou un « Range » correspondant à ces deux cellules :

{codecitation style= »brush: PowerShell »}

$RangeTitle = $WorkSheet.Range($WorkSheet.Cells.Item(1,1),$WorkSheet.Cells.Item(1,2))

{/codecitation}

 

Une fois l’étendue définie, on va faire appel à la propriété « MergeCells » pour fusionner les deux cellules :

{codecitation style= »brush: PowerShell »}

$RangeTitle.MergeCells = $true

{/codecitation}

 

On peut également appliquer un style à une cellule ou à une étendue. Pour cela, il suffit simplement d’associer un style Excel existant à la propriété « Style » de notre étendue. Par contre, cela nécessite d’avoir la liste des styles existants dans Excel et de savoir à quoi ils correspondent…

Pour obtenir la liste des styles, il suffit simplement de saisir la commande « $WorkBook.Styles | ft name » qui nous retourne la liste des styles à disposition et qui est disponible également depuis « Styles de Cellules » dans Excel.

 

On va appliquer le style « Titre 2 ». Pour cela deux solutions :

{codecitation style= »brush: PowerShell »}

#Solution 1

$RangeTitle.Style = « Titre 2 »

#Solution 2

$RangeTitle.Style = ($WorkBook.Styles.Item(43)).Name

{/codecitation}

 

La première solution est la plus simple, par contre, vous aurez sans doute noté que le nom du style est en français dans mon exemple. En fait, le titre du style est traduit, il n’y a aucune solution pour y remédier (en tout cas je n’ai pas trouvé !).

La deuxième solution est donc plus adaptée sauf que les styles n’ont pas la même indexation entre les différentes versions d’Excel…

Troisième solution… Créer son propre style en s’appuyant sur un style existant. Si dessous, un nouveau style s’appuyant sur le style Excel « Titre 2 ».

{codecitation style= »brush: PowerShell »}

$Style = $WorkBook.Styles.Add(« MonTitre »)

$Style.IncludeAlignment = $false

$Style.IncludeNumber = $false

$Style.IncludePatterns = $false

$Style.IncludeProtection = $false

 

# Style de police

$Style.Font.Bold = $true

$Style.Font.Color = 6968388

$Style.Font.ColorIndex = 50

$Style.Font.Size = 11

$Style.Font.ThemeColor = 4

 

# style des bordures

$Style.Borders.item(4).Color = 15123099

$Style.Borders.item(4).ColorIndex = 37

$Style.Borders.item(4).ThemeColor = 5

$Style.Borders.item(4).LineStyle = 1

$Style.Borders.item(4).Weight = -4138

$Style.Borders.item(4).TintAndShade = +0.4

 

$Style.IncludeBorder = $true

{/codecitation}

 

Vous pouvez vous appuyer sur cet exemple de style personnalisé pour appliquer une police ou une bordure particulière directement à une cellule ou à une étendue. Les propriétés sont les mêmes.

 

On peut également centrer le titre (voir la liste des valeurs possibles depuis http://msdn.microsoft.com/en-us/library/office/bb241313(v=office.12).aspx) :

{codecitation style= »brush: PowerShell »}

$RangeTitle.HorizontalAlignment = -4108

{/codecitation}

 

Si vous avez consulté le lien ci-dessus, nous aurions pu également appeler la valeur par son nom pour un résultat équivalent.

{codecitation style= »brush: PowerShell »}

$RangeTitle.HorizontalAlignment = [Microsoft.Office.Interop.Excel.XlHAlign]::xlHAlignCenter

{/codecitation}

 

C’est un point intéressant car de nombreuses méthodes la classe Office Interop Excel s’appuie sur ce genre d’énumération. Vous en avez la liste complète depuis http://msdn.microsoft.com/en-us/library/office/bb259481(v=office.12).aspx.

 

Pour conclure, on va définir la taille de nos colonnes sur notre étendue, histoire que le titre ne soit pas trop écrasé.

{codecitation style= »brush: PowerShell »}

$RangeTitle.ColumnWidth = 20

{/codecitation}

 

Et voila le résultat!

 

Et le script au complet:

{codecitation style= »brush: PowerShell »}

[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo] « en-US »

 

$Excel = New-Object -ComObject « Excel.Application »

 

$WorkBook = $Excel.Workbooks.Add()

 

$WorkSheet = $WorkBook.WorkSheets.Add()

$WorkSheet.Name = « Production de bananes »

$WorkSheet.Select()

 

$WorkSheet.Cells.Item(1,1) = « Production annuelle de bananes »

 

$ArrProduction = @((‘2010’,’4.5’),(‘2011’,’12’),(‘2012’,’11.5’),(‘2013’,’15’))

 

$WorkSheet.Cells.Item(3,1) = « Année »

$WorkSheet.Cells.Item(3,2) = « Production (en tonnes) »

 

$InitialRow = 4

 

for ( $i=0; $i -lt $ArrProduction.Count; $i++ )

{

$WorkSheet.Cells.Item($InitialRow,1) = $ArrProduction[$i][0]

$WorkSheet.Cells.Item($InitialRow,2) = [decimal] $ArrProduction[$i][1]

$InitialRow++

}

 

$RangeTitle = $WorkSheet.Range($WorkSheet.Cells.Item(1,1),$WorkSheet.Cells.Item(1,2))

$RangeTitle.MergeCells = $true

$RangeTitle.Style = ($WorkBook.Styles.Item(43)).Name

$RangeTitle.HorizontalAlignment = [Microsoft.Office.Interop.Excel.XlHAlign]::xlHAlignCenter

$RangeTitle.ColumnWidth = 20

 

$WorkBook.SaveAs(« c:\temp\MaProductionDeBananes.xlsx »)

$Excel.Visible = $true

{/codecitation}

 

On a vu dans ce deuxième article comment appliquer un style Excel existant et même créer une étendue. On verra dans le prochain article comment mettre en forme un tableau.

 

Vous pouvez consulter également mes autres articles sur le sujet:

Manipuler Excel avec PowerShell : Part 1 – Créer un classeur, un feuillet et un tableau

Dans cette série d’articles, je vais vous montrer les bases pour générer un fichier Excel à l’aide de PowerShell.

Au préalable et surtout afin d’éviter d’obtenir une erreur de type « Old format or invalid type library » (http://support.microsoft.com/kb/320369), nous allons définir les informations culturelles sur l’anglais avant d’instancier Excel :

{codecitation style= »brush: PowerShell »}

[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo] « en-US »

{/codecitation}

 

Nous pouvons, dès-lors, instancier notre objet Excel :

{codecitation style= »brush: PowerShell »}

$Excel = New-Object -ComObject « Excel.Application »

{/codecitation}


Avec notre objet Excel, nous allons créer notre classeur:

{codecitation style= »brush: PowerShell »}

$WorkBook = $Excel.Workbooks.Add()

{/codecitation}


Un classeur étant constitué de feuillets, ajoutons maintenant un feuillet:

{codecitation style= »brush: PowerShell »}

$WorkSheet = $WorkBook.WorkSheets.Add()

{/codecitation}

 

Donnons-lui un nom :

{codecitation style= »brush: PowerShell »}

$WorkSheet.Name = « Production de bananes »

{/codecitation}

 

Définissons le titre de notre futur tableau en position A1 :

{codecitation style= »brush: PowerShell »}

$WorkSheet.Cells.Item(1,1) = « Production annuelle de bananes »

{/codecitation}

 

Remarque : Le premier chiffre correspond au numéro de ligne et le deuxième chiffre au numéro de colonne.

 

Maintenant, nous allons créer notre tableau Excel. Mais, pour cela, on va déjà commencer par stocker les éléments de notre tableau:

{codecitation style= »brush: PowerShell »}

$ArrProduction = @((‘2010’,’4.5’),(‘2011’,’12’),(‘2012’,’11.5’),(‘2013’,’15’))

{/codecitation}

 

Nous déclarons les entêtes de notre tableau :

{codecitation style= »brush: PowerShell »}

$WorkSheet.Cells.Item(3,1) = « Année »

$WorkSheet.Cells.Item(3,2) = « Production (en tonnes) »

{/codecitation}

 

… et les données :

{codecitation style= »brush: PowerShell »}

$WorkSheet.Cells.Item(4,1) = $ArrProduction[0][0]

$WorkSheet.Cells.Item(4,2) = [decimal] $ArrProduction[0][1]

$WorkSheet.Cells.Item(5,1) = $ArrProduction[1][0]

$WorkSheet.Cells.Item(5,2) = [decimal] $ArrProduction[1][1]

$WorkSheet.Cells.Item(6,1) = $ArrProduction[2][0]

$WorkSheet.Cells.Item(6,2) = [decimal] $ArrProduction[2][1]

$WorkSheet.Cells.Item(7,1) = $ArrProduction[3][0]

$WorkSheet.Cells.Item(7,2) = [decimal] $ArrProduction[3][1]

{/codecitation}


Un autre méthode pour alimenter les cellules plus efficacement, dirons-nous…

{codecitation style= »brush: PowerShell »}

$InitialRow = 4

for ( $i=0; $i -lt $ArrProduction.Count; $i++ )

{

$WorkSheet.Cells.Item($InitialRow,1) = $ArrProduction[$i][0]

$WorkSheet.Cells.Item($InitialRow,2) = [decimal] $ArrProduction[$i][1]

$InitialRow++

}

{/codecitation}

 

Pour visualiser le fichier Excel, il faut simplement saisir la commande suivante:

{codecitation style= »brush: PowerShell »}

$Excel.Visible = $true

{/codecitation}

 

Si vous voulez sauvegarder le classeur Excel :

{codecitation style= »brush: PowerShell »}

$WorkBook.SaveAs(« c:\temp\MaProductionDeBananes.xlsx »)

{/codecitation}


Et voila le résultat!

 

Et le script au complet:

{codecitation style= »brush: PowerShell »}

[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo] « en-US »

$Excel = New-Object -ComObject « Excel.Application »

$WorkBook = $Excel.Workbooks.Add()

$WorkSheet = $WorkBook.WorkSheets.Add()

$WorkSheet.Name = « Production de bananes »

$WorkSheet.Select()

$WorkSheet.Cells.Item(1,1) = « Production annuelle de bananes »

$ArrProduction = @((‘2010’,’4.5’),(‘2011’,’12’),(‘2012’,’11.5’),(‘2013’,’15’))

 

$WorkSheet.Cells.Item(3,1) = « Année »

$WorkSheet.Cells.Item(3,2) = « Production (en tonnes) »


$InitialRow = 4

for ( $i=0; $i -lt $ArrProduction.Count; $i++ )

{

$WorkSheet.Cells.Item($InitialRow,1) = $ArrProduction[$i][0]

$WorkSheet.Cells.Item($InitialRow,2) = [decimal] $ArrProduction[$i][1]

$InitialRow++

}


$WorkBook.SaveAs(« c:\temp\MaProductionDeBananes.xlsx »)

$Excel.Visible = $true

{/codecitation}

 

On a vu dans ce premier article comment créer notre premier classeur, un feuillet et comment l’alimenter. Dans l’article suivant, nous nous attarderons un peu plus longuement sur la mise en forme.

Ci-dessous, les autres articles sur le sujet:

Manipulate IPv6 with PowerShell

 

For some needs related to the management of Active Directory subnets, I spent some times to manipulate IPv6 addresses with PowerShell. Since I’m not a guru in PowerShell, I have started to search some useful stuffs over Internet but without any success. So I have done it by my own. It is not really the cleanest way but it is almost working!

The objective was to manipulate a IPv6 subnet such as “2001:4cf8:0:220B::/64” to obtain something like that:

InputAddress: 2001:4cf8:0:220B::
Prefix: 64
NumberOfIPs: 18446744073709551616
NetworkAddress: 2001:4CF8:0000:220B:0000:0000:0000:0000
RangeStart: 42542049273690184588432901994040000512
RangeEnd: 42542049273690184606879646067749552127
IpStart: 2001:4CF8:0000:220B:0000:0000:0000:0000
IpEnd:

2001:4CF8:0000:220B:FFFF:FFFF:FFFF:FFFF

 

 

The script is using the class .Net IPAddress (http://msdn.microsoft.com/en-us/library/system.net.ipaddress.aspx).  I had to use also the System.Numerics.BigInteger. Unfortunatly, BigInteger is available only from the .Net Framework  4 and, moreover, PowerShell doesn’t use the lastest CLR available. So you have to install the framework 4 then indicate explicitly to PowerShell to use it You will just have to run the following from a “Command prompt”:

reg add hklm\software\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

reg add hklm\software\wow6432node\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

 

The complete code:

{codecitation style= »brush: PowerShell »}

 

#========================================================================

# Created on:   18/04/2013

# Created by:   AUGAGNEUR Alexandre

# Website:                         www.alexwinner.com

# URL:

# Filename:     Compute-IPv6Address.ps1

#

# Description:    Create an object from a string « IPv6Address/Prefix »

#               Members:

#               – InputAddress

#               – Prefix

#               – NumberOfIPs

#               – NetworkAddress

#               – RangeStart

#               – RangeEnd

#               – IpStart

#               – IpEnd

#========================================================================

param (

[Parameter(mandatory=$true)]

[string] $InputAddress

)

$Message =

@ »

The script needs to use the CLR .Net Framework 4.0.

If you have already installed it, you have to indicate to PowerShell to use it.

Commands to run:

reg add hklm\software\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

reg add hklm\software\wow6432node\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

« @

#Region Functions

####################################################

# Functions

####################################################

#—————————————————

# Converting an array of bytes to IPv6 format

#—————————————————

function Convert-BytesToIPv6 ( $arrayBytes )

{

$String = $null

$j = 0

foreach ( $Item in $arrayBytes )

{

if ( $j -eq 2)

{

$String += « : »+[system.bitconverter]::Tostring($Item)

$j = 1

}

else

{

$String += [system.bitconverter]::Tostring($Item)

$j++

}

}

Return $String

}

#—————————————————

# Compute the network address

#—————————————————

function Compute-NetworkAddress ( $Address )

{

#$Address = [System.Net.IPAddress] $Obj.InputAddress

$ArrBytesAddress = $Address.GetAddressBytes()

[array]::Reverse($ArrBytesAddress)

# Sets a Block-Size to 0 if it is a part of the network length

for ( $i=0; $i -lt $script:BlockBytes; $i++ )

{

$ArrBytesAddress[$i] = 0

}

# Returns the remaining bits of the prefix

$Remaining =  $obj.Prefix % 8

if ( $Remaining -gt 0 )

{

$Mask = ([Math]::Pow(2,$Remaining)-1)*([Math]::Pow(2,8-$Remaining))

$BlockBytesValue = $ArrBytesAddress[$i] -band $Mask

$ArrBytesAddress[$i] = $BlockBytesValue

}

[array]::Reverse($ArrBytesAddress)

$NetworkAddress = Convert-BytesToIpv6 $ArrBytesAddress

Return $NetworkAddress

}

#—————————————————

# Create the IPv6 object

#—————————————————

function Compute-IPv6 ( $Obj, $ObjInputAddress )

{

try

{

# Compute available IPs based on the IPv6 Prefix

[System.Numerics.BigInteger] $NumberOfIPs = [System.Math]::Pow(2, $script:IntIPLength)

$Obj | Add-Member -type NoteProperty -name NumberOfIPs -value $NumberOfIPs

# Compute the network address

$NetworkAddress = Compute-NetworkAddress $ObjInputAddress

$Obj | Add-Member -type NoteProperty -name NetworkAddress -value $NetworkAddress

# Convert NetworkAddress to decimal value

$ObjNetworkAddress = [System.Net.IPAddress] $NetworkAddress

$ArrBytesNetworkAddress = $ObjNetworkAddress.GetAddressBytes()

[array]::Reverse($ArrBytesNetworkAddress)

$BigIntRangeStart = [System.Numerics.BigInteger] $ArrBytesNetworkAddress

$Obj | Add-Member -type NoteProperty -name RangeStart -value $BigIntRangeStart

# Compute the lastest available IP in decimal value

[System.Numerics.BigInteger] $BigIntRangeEnd = ($BigIntRangeStart + $NumberOfIPs) – 1

$Obj | Add-Member -type NoteProperty -name RangeEnd -value $BigIntRangeEnd

# Convert the decimal value of the range start to IPv6 format

$ArrBytesRangeStart = $BigIntRangeStart.ToByteArray()

[array]::Reverse($ArrBytesRangeStart)

$IpStart = Convert-BytesToIpv6 $ArrBytesRangeStart

$Obj | Add-Member -type NoteProperty -name IpStart -value $IpStart

# Convert the lastest available IP in IPv6 format

$ArrBytesRangeEnd = $BigIntRangeEnd.ToByteArray()

[array]::Reverse($ArrBytesRangeEnd)

$IpEnd = Convert-BytesToIpv6 $ArrBytesRangeEnd

$Obj | Add-Member -type NoteProperty -name IpEnd -value $IpEnd

return $Obj

}

catch

{

Write-Host $_.Exception.Message

}

}

#EndRegion

#Region Main

####################################################

# Main

####################################################

# Check the CLR version (only version 4 can be used)

if ( ([environment]::Version).Major -ne 4 )

{

Write-Host $Message -Foreground ‘Red’

}

else

{

try

{

# Add dll System.Numerics to use the BigInteger structure

Add-Type -Path « C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Numerics.dll »

# Construct the subnet object

$ObjSubnet = New-Object -TypeName PsObject

$ObjSubnet | Add-Member -type NoteProperty -name InputAddress -value ($InputAddress.split(« / »))[0]

$ObjSubnet | Add-Member -type NoteProperty -name Prefix -value ($InputAddress.split(« / »))[1]

# Compute IP length

[int] $script:IntIPLength = 128 – $ObjSubnet.Prefix

# Returns the number of block-size

[int] $BlockBytes = [Math]::Floor($script:IntIPLength / 8)

# Construct System.Net.IPAddress

$ObjInputAddress = [System.Net.IPAddress] $ObjSubnet.InputAddress

# Check if IP is a IPv6

if ( $ObjInputAddress.AddressFamily -match « InterNetworkV6 » )

{

# Compute network address and IP ranges

$ObjSubnet = Compute-IPv6 $ObjSubnet $ObjInputAddress

Return $ObjSubnet

}

else

{

Write-Host « The IP address is not an IPv6. »

}

}

catch

{

Write-Host « $($_.Exception.Message) »

}

}

#EndRegion

 

{/codecitation}

 

You can download the full script from the Microsoft TechNet Script Library: http://gallery.technet.microsoft.com/scriptcenter/Manipulate-IPv6-Address-041661e4.

Its usage is simple. You just have to call the script with parameter “inputaddress” and a value “IPv6/Prefix”.

Example: .\Compute-IPv6Address.ps1 –InputAddress 2001:4cf8:0:220B::/64

#========================================================================

# Created on: 18/04/2013

# Created by: AUGAGNEUR Alexandre

# Website: www.alexwinner.com

# URL:

# Filename: Compute-IPv6Address.ps1

#

# Description: Create an object from a string « IPv6Address/Prefix »

# Members:

# – InputAddress

# – Prefix

# – NumberOfIPs

# – NetworkAddress

# – RangeStart

# – RangeEnd

# – IpStart

# – IpEnd

#========================================================================

param (

[Parameter(mandatory=$true)]

[string] $InputAddress

)

$Message =

@ »

The script needs to use the CLR .Net Framework 4.0.

If you have already installed it, you have to indicate to PowerShell to use it.

Commands to run:

reg add hklm\software\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

reg add hklm\software\wow6432node\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

« @

#Region Functions

####################################################

# Functions

####################################################

#—————————————————

# Converting an array of bytes to IPv6 format

#—————————————————

function Convert-BytesToIPv6 ( $arrayBytes )

{

$String = $null

$j = 0

foreach ( $Item in $arrayBytes )

{

if ( $j -eq 2)

{

$String += « : »+[system.bitconverter]::Tostring($Item)

$j = 1

}

else

{

$String += [system.bitconverter]::Tostring($Item)

$j++

}

}

Return $String

}

#—————————————————

# Compute the network address

#—————————————————

function Compute-NetworkAddress ( $Address )

{

#$Address = [System.Net.IPAddress] $Obj.InputAddress

$ArrBytesAddress = $Address.GetAddressBytes()

[array]::Reverse($ArrBytesAddress)

# Sets a Block-Size to 0 if it is a part of the network length

for ( $i=0; $i -lt $script:BlockBytes; $i++ )

{

$ArrBytesAddress[$i] = 0

}

# Returns the remaining bits of the prefix

$Remaining = $obj.Prefix % 8

if ( $Remaining -gt 0 )

{

$Mask = ([Math]::Pow(2,$Remaining)-1)*([Math]::Pow(2,8-$Remaining))

$BlockBytesValue = $ArrBytesAddress[$i] -band $Mask

$ArrBytesAddress[$i] = $BlockBytesValue

}

[array]::Reverse($ArrBytesAddress)

$NetworkAddress = Convert-BytesToIpv6 $ArrBytesAddress

Return $NetworkAddress

}

#—————————————————

# Create the IPv6 object

#—————————————————

function Compute-IPv6 ( $Obj, $ObjInputAddress )

{

try

{

# Compute available IPs based on the IPv6 Prefix

[System.Numerics.BigInteger] $NumberOfIPs = [System.Math]::Pow(2, $script:IntIPLength)

$Obj | Add-Member -type NoteProperty -name NumberOfIPs -value $NumberOfIPs

# Compute the network address

$NetworkAddress = Compute-NetworkAddress $ObjInputAddress

$Obj | Add-Member -type NoteProperty -name NetworkAddress -value $NetworkAddress

# Convert NetworkAddress to decimal value

$ObjNetworkAddress = [System.Net.IPAddress] $NetworkAddress

$ArrBytesNetworkAddress = $ObjNetworkAddress.GetAddressBytes()

[array]::Reverse($ArrBytesNetworkAddress)

$BigIntRangeStart = [System.Numerics.BigInteger] $ArrBytesNetworkAddress

$Obj | Add-Member -type NoteProperty -name RangeStart -value $BigIntRangeStart

# Compute the lastest available IP in decimal value

[System.Numerics.BigInteger] $BigIntRangeEnd = ($BigIntRangeStart + $NumberOfIPs) – 1

$Obj | Add-Member -type NoteProperty -name RangeEnd -value $BigIntRangeEnd

# Convert the decimal value of the range start to IPv6 format

$ArrBytesRangeStart = $BigIntRangeStart.ToByteArray()

[array]::Reverse($ArrBytesRangeStart)

$IpStart = Convert-BytesToIpv6 $ArrBytesRangeStart

$Obj | Add-Member -type NoteProperty -name IpStart -value $IpStart

# Convert the lastest available IP in IPv6 format

$ArrBytesRangeEnd = $BigIntRangeEnd.ToByteArray()

[array]::Reverse($ArrBytesRangeEnd)

$IpEnd = Convert-BytesToIpv6 $ArrBytesRangeEnd

$Obj | Add-Member -type NoteProperty -name IpEnd -value $IpEnd

return $Obj

}

catch

{

Write-Host $_.Exception.Message

}

}

#EndRegion

#Region Main

####################################################

# Main

####################################################

# Check the CLR version (only version 4 can be used)

if ( ([environment]::Version).Major -ne 4 )

{

Write-Host $Message -Foreground ‘Red’

}

else

{

try

{

# Add dll System.Numerics to use the BigInteger structure

Add-Type -Path « C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Numerics.dll »

# Construct the subnet object

$ObjSubnet = New-Object -TypeName PsObject

$ObjSubnet | Add-Member -type NoteProperty -name InputAddress -value ($InputAddress.split(« / »))[0]

$ObjSubnet | Add-Member -type NoteProperty -name Prefix -value ($InputAddress.split(« / »))[1]

# Compute IP length

[int] $script:IntIPLength = 128 – $ObjSubnet.Prefix

# Returns the number of block-size

[int] $BlockBytes = [Math]::Floor($script:IntIPLength / 8)

# Construct System.Net.IPAddress

$ObjInputAddress = [System.Net.IPAddress] $ObjSubnet.InputAddress

# Check if IP is a IPv6

if ( $ObjInputAddress.AddressFamily -match « InterNetworkV6 » )

{

# Compute network address and IP ranges

$ObjSubnet = Compute-IPv6 $ObjSubnet $ObjInputAddress

Return $ObjSubnet

}

else

{

Write-Host « The IP address is not an IPv6. »

}

}

catch

{

Write-Host « $($_.Exception.Message) »

}

}

#EndRegion

Normal 0 21 false false false FR X-NONE X-NONE MicrosoftInternetExplorer4

How to pass an array or multidimensional array as arguments to a scriptblock

As the MS PowerShell Team explained in the article How to pass arguments for remote commands, if you are invoking remote commands (invoke-command cmdlet) or starting a background job (start-job cmdlet) you want probably to use local values as arguments.

To manage this, both cmdlets have the option –argumentlist which is permitting to precise an array of arguments to be accessed inside your scriptblock using $args variable.

Example 1:

 

Code:

{codecitation style= »brush:PowerShell »}

$ProcessName = « winlogon »

Invoke-Command -ComputerName DC02 -ScriptBlock { Get-Process -Name $args[0] } -ArgumentList $ProcessName

{/codecitation}

 

 

Example 2:

 

Code:

{codecitation style= »brush:PowerShell »}

$ScriptBlock = {

Import-Module ActiveDirectory

Get-ADUser -Filter « sAMAccountName -eq ‘$($args[0])' » -SearchBase $args[1]

}

Invoke-Command -Computer DC02 -ScriptBlock -ArgumentList « aaugagneur », »OU=CORPNET,DC=corpnet,DC=net »

{/codecitation}

 

 

$args is a variable created automatically by PowerShell which handles any parameters passed as arguments with the option –Argumentlist.

Since the variable $args is an array, you will have issues to pass an array as an argument. Basically, It treats each element of your array as a specific argument.

 

If you want to pass your complete array as a specific argument, you will need to use the syntax (,$MyArray) for –Argumentlist parameter.

Example 3:

 

Code:

{codecitation style= »brush:PowerShell »}

$Array = @(« red », »blue », »green »)

Invoke-Command -ComputerName DC02 -ScriptBlock { Write-Host « 1st item: $($args[0]) »; Write-Host « 2nd item: $($args[1]) »; Write-Host « 3nd item: $($args[2]) » } -argumentList (,$Array)

{/codecitation}

 

 

Example 4:

 

Code:

{codecitation style= »brush:PowerShell »}

$Array = @(« red », »blue », »green »)

Invoke-Command -ComputerName DC02 -ScriptBlock { Write-Host « 1st item: $($args[0][0]) »; Write-Host « 2nd item: $($args[0][1]) »; Write-Host « 3nd item: $($args[0][2]) » } -argumentList (,$array)

{/codecitation}