From 781541c8ea62f5eabfd2d2222ec278b258ce9335 Mon Sep 17 00:00:00 2001 From: Nicolas Lang Date: Sun, 17 Dec 2023 19:52:00 +0100 Subject: [PATCH] =?UTF-8?q?Cr=C3=A9ation=20script?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Active Directory/gpo_versionning.ps1 | 564 +++++++++++++++++++++++++++ 1 file changed, 564 insertions(+) create mode 100644 Active Directory/gpo_versionning.ps1 diff --git a/Active Directory/gpo_versionning.ps1 b/Active Directory/gpo_versionning.ps1 new file mode 100644 index 0000000..ae4bf23 --- /dev/null +++ b/Active Directory/gpo_versionning.ps1 @@ -0,0 +1,564 @@ +#Script créé par Nicolas Lang - Sous licence CC-BY-SA +#https://nicolaslang.fr + +############################################### +#region VARIABLES +#Emplacement où seront sauvegardés les fichiers +$workspace = "\\serveur\GPO_check" +#nom du fichier HTML qui sera envoyé par mail +$htmlfilename = "results.html" +#informations du serveur de messagerie et d'envoi de mail +$mailserver = "serveurmail.societe.fr" +$usessl = $false +$smtpport = 25 +$from = "moi@societe.fr" +$to = "toi@societe.fr" +#Si besoin est : credentials utilisés pour la sauvegarde des fichiers HTML issus des GPO. N'oubliez pas de les sécuriser en mettant le tout dans un +#fichier haché +$username = "user" +$password = "password" +#emplacement de sauvegarde des fichiers HTML +$backuplocation = "\\serveur\GPOS_HTML\" +#Encodage du texte. UTF8 est toujours un bon choix. Ou presque. +$encoding = "UTF8" +#Nom de fichier de réference pour les anciennes GPO. Inutile de le changer. +$gpolistfilename = "oldgpo.clixml" +#nombre de lignes récupérées avant et après chaque modification trouvée dans les GPO +$maxgathering = 5 + + +#endregion + +try +{ + #Ajout de la DLL diffmatchpatch qui comparera les fichiers XML des gpo + Add-type -path '\\serveur\chemin\vers\DiffMatchPatch.dll' -ErrorAction Stop + +} +catch +{ + throw "Impossible d'ajouter la DLL diffmatchpatch" + exit 10 +} + +#On récupère toutes les GPO. Si ça ne fonctionne pas, on bloque le script. (module manquant?) +try +{ + $gpos = get-gpo -All -ErrorAction Stop +} +catch +{ + throw "Impossible de récuperer les GPOS. Module AD manquant?!" + exit 1 +} + +Set-Variable DebugPreference -Value continue +try +{ + Write-Debug "Déplacement du répertoire vers $workspace" + Set-Location $workspace -ErrorAction Stop +} +catch +{ + Write-Output "Une erreur s'est produite lors de l'accès à $workspace. $($Error[0].exception.message)" + Start-Sleep -Seconds 5 + exit 3 +} + +$date = get-date -Format "yyyyMMddHHmmss" +$errors = New-Object System.Collections.ArrayList +$infos = New-Object System.Collections.ArrayList + + +#Cette fonction est utilisée pour parser et mettre en forme les différences de l'outil fc.exe +function Highlight-Differences { + param ($originalfile,$differences) + + Begin + { + $highlightarray = New-Object System.Collections.ArrayList + try + { + Write-Debug "Récupération du contenu du fichier original" + $old = Get-Content $originalfile -ErrorAction Stop + Write-Debug "Récupération du contenu du fichier de différences" + $new = Get-Content $differences -ErrorAction Stop + } + catch + { + Write-Debug "Erreur de lecture du fichier, fin du script" + throw("Erreur lors de la lecture du fichier source") + exit + } + + } + Process { + # Creation d'un objet diffmatchpatch via la dll (générée depuis le fichier .cs) + $diff = new-object DiffMatchPatch.diff_match_patch + + #création de l'objet de différences + $differences = $diff.diff_main($old,$new,$true) + #nettoyage sémantique pour lecture plus aisée + $diff.diff_cleanupSemantic($differences) + #Création du fichier HTML qui sera parsé ensuite + $textdiffs = $diff.diff_prettyHtml($differences).replace("> <",">`n<").split("`n") + + + #parsing de l'ensemble du fichier et récupération des lignes comportant un élément ajouté par diff_match_patch + $indexarray = New-Object System.Collections.ArrayList + $temparray = New-Object System.Collections.ArrayList + $gatheringtrigger = $false + + #décompte du numéro de ligne + $index = 0 + $textdiffs | % { + if ($_ -like "**" -or $_ -like "**") + { + if ($index -notin $indexarray) + { + if (($index - $maxgathering) -lt 0) + { + $start = 0 + } + else + { + $start = $index - $maxgathering + } + $end = $index + 1 + $count = 0 + do + { + $count++ + $end++ + if ($textdiffs[$end] -like "**" -or $textdiffs[$end] -like "**") + { + $count = 0 + } + } + until ($count -gt $maxgathering -or $end -eq $textdiffs.Length) + for($start=$start;$start -le $end;$start++) + { + $null = $indexarray.Add($start) + $null = $temparray.Add("$($start+$maxgathering) : $($textdiffs[$start])
") + } + $null = $temparray.Add("******************************************************
") + } + } + $index++ + + } + + return $temparray + + } + + +} + +#On récupère tous les fichiers de GPO marqués _new.xml (faits lors de la dernière exécution du script) pour les renommer en date du jour +foreach ($item in (Get-ChildItem $workspace -Filter "*_new.xml")) +{ + try + { + Write-Debug "Renommage du fichier de $($item.name) vers $($item.name.replace("_new.xml","_$date.xml"))" + Move-Item $item.Name ($item.Name.Replace("_new.xml","_$date.xml")) -ErrorAction Stop -Force + } + catch [System.Management.Automation.ItemNotFoundException] + { + $errors.Add([pscustomobject]@{ + "Fichier" = $item.Name + "Type" = [System.Management.Automation.ItemNotFoundException].name + "Action" = "Déplacement" + "Erreur" = "Impossible de le renommer en date du jour, $($Error[0].exception.message)" + }) + } + catch + { + $errors.Add([pscustomobject]@{ + "Fichier" = $item.Name + "Type" = [System.Management.Automation.ItemNotFoundException].name + "Action" = "Déplacement" + "Erreur" = "Impossible de le renommer en date du jour, $($Error[0].exception.message)" + }) + } +} + +$sendmail = $false +$checkoldgpos = $true +try +{ + Write-Debug "Import du fichier CLIXML de la liste des noms de gpo" + $oldgpolist = Import-Clixml $gpolistfilename -ErrorAction Stop +} +catch +{ + $checkoldgpos = $false +} + +if ($checkoldgpos -eq $true) +{ + #On vérifie les gpo crées ou disparues + $gpochangelist = Compare-Object -ReferenceObject $oldgpolist.displayname -DifferenceObject $gpos.displayname + foreach ($entry in $gpochangelist) + { + Write-Debug "Recherche de création ou suppression de $($entry.inputobject)" + if ($entry.sideindicator -eq "=>") + { + Write-Debug "GPO $($entry.inputobject) crée" + $infos.Add([pscustomobject]@{ + "GPO" = $entry.inputobject + "Information" = "La GPO a été créée" + }) | Out-Null + } + else + { + Write-Debug "GPO $($entry.inputobject) supprimée" + $infos.Add([pscustomobject]@{ + "GPO" = $entry.inputobject + "Information" = "La GPO a été supprimée" + }) | Out-Null + } + } +} +Write-Debug "Export de la liste des GPO" +$gpos | Export-Clixml $gpolistfilename +Write-Debug "Filtrage de la liste des GPO qui ne se terminent pas par _new.xml et suppression de la date en fin de nom de fichier" +$gpolist = (Get-ChildItem -Filter "*.xml" | Where-Object {$_.name -notlike "*_new.xml"}).Name -Replace("_\d{$($date.Length)}.xml$","") | Sort-Object -Unique + +foreach ($gpo in $gpos) +{ + Write-Debug "Création d'un rapport HTML pour $($gpo.displayname)" + $gpo.GenerateReportToFile([Microsoft.GroupPolicy.ReportType]::Html, $(Join-Path $workspace "$($gpo.DisplayName).html")) + if ($gpo.DisplayName+"_new.xml" -notin $errors.fichier) + { + try + { + Write-Debug "Création d'un rapport XML pour $($gpo.displayname) nommé $($gpo.displayname)_new.xml" + $gpo.GenerateReportToFile([Microsoft.GroupPolicy.ReportType]::Xml, $(Join-Path $workspace "$($gpo.DisplayName)_new.xml")) + + } + catch + { + Write-Debug "Erreur lors de l'export de la GPO" + $errors.Add([pscustomobject]@{ + "Fichier" = $gpo.displayName + "Type" = $Error[0].CategoryInfo.Reason + "Action" = "Export de la GPO" + "Erreur" = "Une erreur s'est produite lors de l'export de la GPO. $($error[0].Exception.Message)" + }) + } + } + else + { + Write-Debug "Le déplacement du fichier source $($gpo.displayname) est en erreur. Pas d'export fait" + $errors.Add([pscustomobject]@{ + "Fichier" = $gpo.displayName + "Type" = "Manuel" + "Action" = "Export de la GPO" + "Erreur" = "Un déplacement du fichier source est en erreur. Cette GPO ne sera pas exportée pour préserver l'historique." + }) + } + #On vérifie si le répertoire de backup existe ou non + if (!(Test-Path $(Join-Path $workspace $gpo.DisplayName))) + { + Try + { + New-Item -ItemType Directory -Name $gpo.DisplayName -ErrorAction Stop -Verbose -Force + Backup-GPO -Guid $gpo.id -Path $(Join-Path $workspace $gpo.DisplayName) -Comment "Backup du $date" -ErrorAction Stop -Verbose + } + catch + { + $errors.Add([PSCUSTOMOBJECT]@{ + "Fichier" = $gpo.DisplayName + "Type" = $Error[0].CategoryInfo.Reason + "Action" = "Création de backup" + "Erreur" = "Une erreur s'est produite lors de la création du répertoire de backup ou du backup pour la GPO. $($Error[0].Exception.Message)" + }) + } + } + +} +#Une feuille CSS qui sera intégrée au fichier HTML afin de pouvoir avoir un affichage un peu plus sympa +$css = @" + +"@ +try +{ + $htmlfile = new-object System.Collections.Generic.List[string] +} +catch +{ + try + { + $htmlfile = [System.Collections.Generic.List[string]]::new() + } + catch + { + exit 50 + } +} +$htmlfile.Add("") +$htmlfile.Add("") +$htmlfile.Add('') +$htmlfile.Add($css) + +$htmlfile.Add("") +$htmlfile.Add("") +$htmlfile.Add("") +$htmlfile.Add("") +$htmlfile.Add("") +$htmlfile.Add("") +$htmlfile.Add("") +$htmlfile.Add("") +Write-Debug "Parcours des nouveaux fichiers extraits de GPOS" +foreach ($newitem in (Get-ChildItem $workspace -Filter "*_new.xml")) +{ + $isnewgpo = $false + try + { + Write-Debug "Récupération de $($newitem.name.replace(`"_new.xml`","_$date.xml`"))" + $olditem = Get-Item $newitem.name.replace("_new.xml","_$date.xml") -ErrorAction Stop + } + catch + { + Write-Debug "Pas de fichier en date du jour. Recherche d'un plus ancien" + $foundfiles = Get-ChildItem | Where-Object {$_.name -match "^$($newitem.Name.replace("_new.xml","_\d{$($date.length)}.xml$"))"} + #if ((get-childitem $newitem.Name.Replace("_new.xml","_*.xml") -Exclude $newitem.Name).count -ne 0) + if ($foundfiles.count -ne 0) + { + try + { + Write-Debug "Récupération du fichier le plus récent qui n'est pas $($newitem.name)" + $lastknownitem = (get-childitem -Exclude $newitem.Name -ErrorAction Stop | Where-object {$_.name -match "^$($newitem.Name.replace("_new.xml","_\d{$($date.length)}.xml$"))"}) | Sort-Object -Property name -Descending | Select-Object -First 1 + Write-Debug "Fichier trouvé : $($lastknownitem.name)" + $olditem = $lastknownitem + } + catch + { + Write-Debug "Aucun fichier plus récent. La GPO est nouvelle." + $isnewgpo = $true + } + + if ($isnewgpo -eq $true -or $olditem -eq $null) + { + $errors.Add([pscustomobject]@{ + "Fichier" = $olditem.Name + "Type" = $Error[0].CategoryInfo.Reason + "Action" = "Récupération de l'ancien fichier $($newitem.name.replace("_new.xml","_$date.xml"))" + "Erreur" = "La récupération de l'ancien fichier n'a pas pu être effectuée. $($Error[0].Exception.Message). La gpo est nouvelle" + }) + $isnewgpo = $true + } + } + else + { + Write-Debug "Aucun fichier plus ancien trouvé." + $isnewgpo = $true + } + } + + + #récupération des fichiers + Write-Debug "Lecture du contenu de $($newitem.name) pour création d'un objet XML" + [xml]$xmlnew = Get-Content $newitem + if ($isnewgpo -eq $true) + { + Write-Debug "La gpo $($newitem.name) est nouvelle, on passe ce fichier" + $errors.Add([pscustomobject]@{ + "Fichier" = $newitem.Name + "Type" = "Nouvelle GPO" + "Action" = "La GPO $($newitem.name.replace("_new.xml","_$date.xml")) a été créée en date du $($xmlnew.GPO.CreatedTime)" + "Erreur" = "GPO Non traitée car nouvelle. $($Error[0].Exception.Message)." + }) + continue + } + Write-Debug "Lecture du contenu de $($olditem.name) pour création d'un object XML de comparaison" + [xml]$xmlold = Get-Content $olditem + + if ($xmlold.GPO.ModifiedTime -ne $xmlnew.GPO.ModifiedTime) + { + Write-Debug "Les fichiers comportent des dates de modifications différentes." + $sendmail = $true + Write-Host -ForegroundColor Green "Modifications détectées entre $($newitem.name) et $($olditem.name)!" + $htmlfile.Add("") + $htmlfile.Add("") + $htmlfile.Add("") + Write-Debug "Selection de la GPO pour sauvegarde" + $thisgpo = $gpos | Where-Object {$_.DisplayName -eq $xmlnew.GPO.Name} + try + { + Backup-GPO -Guid $thisgpo.id -Path $(Join-Path $workspace $thisgpo.DisplayName) -Comment "Backup du $date" -ErrorAction Stop + } + catch + { + $errors.Add([pscustomobject]@{ + "Fichier" = $thisgpo.DisplayName + "Type" = "BackupGPO" + "Action" = "La GPO $($thisgpo.DisplayName) n'a pas pu être sauvegardée" + "Erreur" = "GPO Non sauvegardée. $($Error[0].Exception.Message)." + }) + + } + #$enc sera utilisé pour lire et convertir le fichier xml + $enc = [System.Text.Encoding]::$encoding + $xmlold.InnerXml.replace("><",">`n<")| Out-File $(join-path $workspace "__xmlold.xml") -Encoding $encoding -Force + $xmlnew.InnerXml.replace("><",">`n<")| Out-File $(join-path $workspace "__xmlnew.xml") -Encoding $encoding -Force + Write-Debug "Analyse des fichiers XML et comparaison via l'outil diff_match_patch" + $differences = Highlight-Differences -originalfile (Get-Item .\__xmlold.xml) -differences (Get-Item .\__xmlnew.xml) + if ($differences) + { + $htmlfile.Add("") + + $htmlfile.Add("") + } + else + { + #Supprimer le fichier XML fraichement crée, inutile car aucune modification par rapport à l'ancien + Remove-Item $newitem -Verbose -Force + } +} + +$htmlfile.Add("
GPODate de modificationDifférences
$($xmlold.GPO.Name)$($xmlold.GPO.ModifiedTime)") + foreach ($difference in $differences) + { + $htmlfile.add("$($difference.replace('','
').replace('','
'))") + } + } + else + { + #Encore valide? + Write-Debug "une erreur s'est produite lors de la comparaison des fichiers." + $htmlfile.Add("
") + $htmlfile.Add("ERREUR LORS DE LA COMPARAISON DES FICHIERS") + } + $htmlfile.Add("
") + +$htmlfile.Add("
") +$htmlfile.Add("
") +$htmlfile.Add("
") + +if ($infos.Count -gt 0) +{ + $sendmail = $true + $htmlfile.Add("") + $htmlfile.Add("") + $htmlfile.Add("") + $htmlfile.Add("") + $htmlfile.Add("") + foreach ($entry in $infos) + { + $htmlfile.Add("") + $htmlfile.Add("") + + if ($entry.Information -eq "La GPO a été créée") + { + $htmlfile.Add("") + } + else + { + $htmlfile.Add("") + } + $htmlfile.Add("") + } + +} +$htmlfile.Add("
GPOAction
$($entry.gpo)Creation d'une GPOSuppression d'une GPO
") +$htmlfile.Add("") +$htmlfile.Add("") +Write-Debug "Export du fichier HTML $(Join-Path $workspace "$htmlfilename")" +$htmlfile | Out-File $(Join-Path $workspace "$htmlfilename") + +$htmlcontent = "" +foreach ($ligne in $htmlfile) { $htmlcontent += $ligne} + +$accesserror = $false +if ($sendmail) +{ + Send-MailMessage -From "$from" -To $to -Subject "Modifications sur GPO" -Body $htmlcontent -BodyAsHtml -Attachments $(Join-Path $workspace "$htmlfilename") -SmtpServer "$mailserver" -Port $smtpport -UseSsl:$usessl -Encoding $encoding +} +$creds = new-object System.Management.Automation.PSCredential -ArgumentList "$username",$("$password" | ConvertTo-SecureString -AsPlainText -Force) +try +{ + $drive = New-PSDrive -PSProvider FileSystem -Name "strategies" -Root "$backuplocation" -Credential $creds -ErrorAction Stop +} +catch +{ + $accesserror = $true + Write-Host -ForegroundColor Red "Impossible de se connecter au lecteur réseau. $($error[0].exception.message)" +} +if (!$accesserror) +{ + foreach ($file in (Get-ChildItem $workspace "*.html" |Where-Object {$_.name -ne "$htmlfilename"})) + { + try + { + Move-Item $file "strategies:\" -Force -ErrorAction Stop -Verbose + #Remove-Item $file -Force -ErrorAction Stop -Verbose + } + catch + { + Write-Host "Erreur sur $($file.name) : $($error[0].exception.Message)" + } + } +} +remove-psdrive -Name "strategies" -PSProvider FileSystem \ No newline at end of file