Création des GG
Introduction
Nous allons attaquer la partie des groupes globaux, les créer automatiquement via un script, et y ajouter des membres avec le même principe.
Tableau modèle
Il nous faut d'abord le fichier qui servira de modèle, que l'on mettra à jour, pour faire les modifications.
Le gros avantage est que ce fichier peut-être divisé par service, et distribué aux responsables et directeurs de services pour avoir des infos toujours à jour, sans donner accès à une console d'administration.
GG_Direction
Il faut ensuite exporter, ou convertir ce tableau en csv, encodage UTF8, séparateur ";" point virgule.
On obtient alors ce fichier :
Groupe;Prenom;NomGG;Membre
GG_Direction;Rozenn;LUCASORG_GG_DIRECTION;
GG_Direction;Philippe;PALASORG_GG_FINANCE;
GG_Comptablité;Bertrand;KANMEMORG_GG_RH;
GG_Comptablité;Céline;DUPUIORG_GG_IT;
GG_Comptablité;Dylan;BLOTORG_GG_DIRECTION;Alice GG_Comptablité;Anthony;VORPALMARTIN
GG_RH;Stéphanie;ORG_GG_DIRECTION;Thomas ROBERT
GG_RH;Paul;MORINIEREORG_GG_FINANCE;Julie GG_RH;Francois;PARLATLAMBERT
ORG_GG_FINANCE;Nicolas PETIT
ORG_GG_RH;Sophie DURAND
ORG_GG_RH;Marc LEFEVRE
ORG_GG_IT;Lucas MOREAU
ORG_GG_IT;Emma GARNIER
ORG_GG_IT;Hugo CHEVALIER
ORG_GG_COMMUNICATION;
ORG_GG_COMMUNICATION;Clara BERTRAND
ORG_GG_LOGISTIQUE;
ORG_GG_LOGISTIQUE;Antoine RENAUD
Création des GG
Voici à présent le script qui permet de créer tous les GG, dans un seul et même endroit, dans OU=GG,OU=AGDLP,DC=domain,DC=local :
Import-Module<#
ActiveDirectory===============================================================================
SCRIPT : 5.1 - Creation_GG.ps1 (Version 2-passes, robuste)
OBJET : Creation des Groupes Globaux (GG) + alignement de leurs membres
===============================================================================
PRINCIPE :
PASS A : Creer tous les groupes globaux (GG et sous-GG) avant ajout des membres
PASS B : Vider contenu de chaque GG puis injecter les membres (GG puis utilisateurs)
AVANTAGES :
- Plus aucune erreur "groupe introuvable"
- Les sous-GG peuvent apparaitre apres les parents dans le CSV
- Nettoyage des caracteres parasites (nbsp, guillemets, espaces)
- Sorties visibles sans accents (compatibilite console)
===============================================================================
#>
Param(
[string]$CSVPath = "C:\AGDLP\ORG_ARBO_GDL.csv",
[string]$OUPath = "OU=GG,OU=AGDLP,DC=domain,DC=local",
[string]$LogFile = "C:\AGDLP\AD_Group_Update_Errors.txt"
)
$ErrorActionPreference = "Stop"
# ----------------------------------------------------------------------------
# CONFIGURATION0. Module AD
# ----------------------------------------------------------------------------
try { Import-Module ActiveDirectory -ErrorAction Stop }
catch {
Write-Error "Module ActiveDirectory non disponible. Installer RSAT."
exit 1
}
# ----------------------------------------------------------------------------
# 1. Reset fichier log (UTF-8 BOM)
# ----------------------------------------------------------------------------
$CSVPathutf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($LogFile, "C:\chemin\vers\GG.csv"", $OUPath = "OU=GG,OU=AGDLP,DC=domain,DC=local"
$LogFile = "C:\chemin\vers\AD_Group_Update_Errors.txt"utf8Bom)
# Nettoyage du fichier de logs
"" | Out-File $LogFile----------------------------------------------------------------------------
# Import2. sansLecture lignesCSV videsGG
# ----------------------------------------------------------------------------
if (-not (Test-Path $CSVPath)) {
Write-Error "Fichier CSV introuvable : $CSVPath"
exit 1
}
$data = Import-Csv -Path $CSVPath -Delimiter ";" |
Where-Object { $_.GroupeGG -and $_.Groupe.GG.Trim() -ne "" }
if ($data.Count -eq 0) {
Write-Host "Aucun groupe global a traiter."
exit 0
}
# Regroupement----------------------------------------------------------------------------
par# 3. Petite fonction de nettoyage (supprime espaces HTML, nbsp, guillemets)
# ----------------------------------------------------------------------------
function Clean-Cell([string]$s) {
if (-not $s) { return "" }
$s = $s -replace " ", " "
$s = $s -replace "\u00A0", " "
$s = $s -replace "\u200B", ""
$s = $s -replace "[«»“”‘’]", ""
return ($s -replace "\s+", " ").Trim()
}
foreach ($row in $data) {
$row.GG = Clean-Cell $row.GG
$row.Membre = Clean-Cell $row.Membre
}
# ----------------------------------------------------------------------------
# 4. PASS A : CREATION DE TOUS LES GROUPES (GG + sous-GG references)
# ----------------------------------------------------------------------------
# Ensemble de tous les groupes a creer
$allGG = New-Object System.Collections.Generic.HashSet[string] ([StringComparer]::OrdinalIgnoreCase)
# Tous les GG declares
$data | ForEach-Object { $null = $allGG.Add($_.GG) }
# Tous les GG references comme Membre
foreach ($row in $data) {
if ($row.Membre -and ($row.Membre -like "*_GG_*")) {
$null = $allGG.Add($row.Membre)
}
}
# Correction : pas de .ToArray(), on convertit proprement en tableau PowerShell
$allGGList = @($allGG)
$totalA = $allGGList.Count
$indexA = 0
Write-Host "Pass A : Creation des GG (sans membres) - Total: $totalA"
foreach ($ggName in $allGGList) {
$indexA++
Write-Progress -Activity "Pass A - Creation GG" -Status "$indexA / $totalA" -PercentComplete (($indexA/$totalA)*100)
if ([string]::IsNullOrWhiteSpace($ggName)) { continue }
$exists = Get-ADGroup -Filter "SamAccountName -eq '$ggName'" `
-SearchBase $OUPath `
-ErrorAction SilentlyContinue
if ($exists) { continue }
try {
New-ADGroup -Name $ggName -SamAccountName $ggName -Path $OUPath `
-GroupScope Global -GroupCategory Security -ErrorAction Stop | Out-Null
Write-Host "Cree : $ggName"
}
catch {
$msg = "Erreur creation du groupe $groupesggName : $_`r`n"
[System.IO.File]::AppendAllText($LogFile, $msg, $utf8Bom)
Write-Warning "Creation impossible : $ggName (voir log)"
}
}
Write-Progress -Activity "Pass A - Creation GG" -Completed
# ----------------------------------------------------------------------------
# 5. PASS B : VIDER et RE-INJECTER les membres
# ----------------------------------------------------------------------------
$groups = $data | Group-Object -Property GroupeGG
$totalB = $groups.Count
$indexB = 0
Write-Host ""
Write-Host "Pass B : Injection des membres dans chaque GG"
foreach ($grp in $groupes)groups) {
$indexB++
Write-Progress -Activity "Pass B - Inj. membres" `
-Status "$indexB / $totalB - $($grp.Name)" `
-PercentComplete (($indexB/$totalB)*100)
$GroupName = Clean-Cell $grp.Name.Trim()Name
Write-Hostif "`n=== Groupe :(-not $GroupNameGroupName) ==="{ continue }
# ------------------------------------------------------
Créer# le5.1 groupeReset s'ilmembres n'existedu pasGG
# ------------------------------------------------------
try {
$ADGroupexisting = Get-ADGroup -Identity $GroupName -ErrorAction Stop
$members = Get-ADGroupMember -Identity $GroupName -ErrorAction SilentlyContinue
if ($members) {
Remove-ADGroupMember -Identity $GroupName -Members $members `
-Confirm:$false -ErrorAction SilentlyContinue
}
}
catch {
# Si pour une raison obscure il n existe pas, on tente creation tardive
try {
$ADGroup =
New-ADGroup -Name $GroupName `
-SamAccountName $GroupName ` -Path $OUPath `
-GroupScope Global -GroupCategory Security -ErrorAction Stop | Out-Null
}
catch {
$msg = "ERREURErreur créationcreation tardive du groupe '$GroupName'GroupName : $_"_`r`n"
| Out-File[System.IO.File]::AppendAllText($LogFile, $LogFilemsg, -Append$utf8Bom)
Write-Warning "Impossible de traiter $GroupName (voir log)"
continue
}
}
# ------------------------------------------------------
Ajout# des5.2 Ajouter les membres GROUPE (tous existent depuis Pass A)
# ------------------------------------------------------
foreach ($ligneline in $grp.Group) {
# Colonnes sans accents
$Prenomm = $ligne.PrenomClean-Cell $Nom = $ligne.Nom
# Vérification des champsline.Membre
if (-not $Prenom -or -not $Nom)m) {
"Ligne invalide dans CSV : '$($ligne)' (groupe : $GroupName)" | Out-File $LogFile -Append continue }
#if Construction($m DisplayName-notlike "*_GG_*") { continue }
$FullNamechild = Get-ADGroup -Identity $m -ErrorAction SilentlyContinue
if (-not $child) {
$msg = "$PrenomGroupe membre introuvable (apres Pass A) : $Nom".Trim()m (parent: $GroupName)`r`n"
[System.IO.File]::AppendAllText($LogFile, $msg, $utf8Bom)
Write-Warning "GG introuvable : $m -> ignore"
continue
}
Add-ADGroupMember -Identity $GroupName -Members $child -ErrorAction SilentlyContinue
Write-Host " →+ RechercheGG : $FullName"m"
}
# Recherche------------------------------------------------------
AD# par5.3 DisplayNameAjouter les membres UTILISATEUR
# ------------------------------------------------------
foreach ($line in $Usergrp.Group) {
$m = Clean-Cell $line.Membre
if (-not $m) { continue }
if ($m -like "*_GG_*") { continue }
$user = Get-ADUser -Filter { DisplayName -eq $FullNamem } -ErrorAction SilentlyContinue
if (!-not $User)user) {
$msg = "Utilisateur introuvable : $FullNamem (groupe : $GroupName)"`r`n"
| Out-File[System.IO.File]::AppendAllText($LogFile, $LogFilemsg, $utf8Bom)
Write-Warning "Utilisateur introuvable : $m -Append> ignore"
continue
}
# Ajout au groupe
try {
Add-ADGroupMember -Identity $GroupName -Members $User.user.SamAccountName `
-ErrorAction StopSilentlyContinue
}
catch {Write-Host "ERREUR ajout+ :USR: $FullName → $GroupName : $_" | Out-File $LogFile -Appendm"
}
}
}Write-Progress -Activity "Pass B - Inj. membres" -Completed
Write-Host ""
Write-Host "Traitement GG termine avec succes. Voir le log si necessaire : $LogFile" -ForegroundColor Green
Si les utilisateurs existes, il se retrouves dans les groupes GG qui leurs correspondant dans le tableau.
J'ai pendant un moment pensé à faire une ligne par GG et séparer les utilisateurs par une ",", mais cela n'aurait pas été pratique pour les éventuels utilisateurs d'autres services.

