#Script créé par Nicolas Lang - Sous licence CC-BY-SA #https://nicolaslang.fr $scriptPath = $null $answer = "z" if ((Get-Host).Version.Major -le 2) { $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition } else { $scriptPath = $PSScriptRoot } $configfile = Join-Path $scriptPath "config.ini" try { $configuration = Get-Content $configfile -ErrorAction Stop } catch { #region CONFIGFILECREATION While($answer.tolower()[0] -ne "y" -and $answer.tolower()[0] -ne "n") { $answer = Read-host "Impossible d'accéder au fichier $configfile `nVoulez vous le créer? (Y/N)" } if ($answer.ToLower()[0] -eq "n") { throw("Fin du script") } else { #region DHCPPOOLS $answer = "z" while($answer.tolower()[0] -ne "y") { $ippoolarray = @() $answer = "z" Write-Host "Saisissez les plages DHCP, une par ligne. Entrez une ligne vide pour terminer." do { $ippool = "Add_pools" while($ippool -as [System.Net.IPAddress] -isnot [System.Net.IPAddress] -and $ippool -ne "") { $ippool = Read-Host "=>" } if ($ippool -ne "") { Write-host "$(([System.Net.IPAddress]$ippool).Ipaddresstostring) ajouté!" $ippoolarray += $ippool } }while ($ippool -ne "") $ippoolarray = $ippoolarray | Sort-Object -Unique Write-Host "Vous avez choisi les pools suivants :`n" $ippoolarray while ($answer.tolower()[0] -ne "y" -and $answer.tolower()[0] -ne "n") { $answer = Read-Host "Est-ce correct? (Y/N)" } } #endregion #region DHCPSERVERS $answer = "z" while($answer.tolower()[0] -ne "y") { $dhcpserversarray = @() $answer = "z" Write-Host "Saisissez les noms ou ips des serveurs DHCP, un par ligne. Entrez une ligne vide pour terminer." do { $dhcpserver = "Add_server" while($dhcpserver -ne "") { $dhcpserver = Read-Host "=>" if ($dhcpserver -ne "") { Write-host "$dhcpserver ajouté!" $dhcpserversarray += $dhcpserver } } }while ($dhcpserver -ne "") $dhcpserversarray = $dhcpserversarray | Sort-Object -Unique Write-Host "Vous avez choisi les serveurs suivants :`n" $dhcpserversarray while ($answer.tolower()[0] -ne "y" -and $answer.tolower()[0] -ne "n") { $answer = Read-Host "Est-ce correct? (Y/N)" } $answertotest = "z" while ($answertotest.tolower()[0] -ne "y" -and $answertotest.tolower()[0] -ne "n" -and $answer -ne "n") { $answertotest = Read-Host "voulez vous tester la connexion aux serveurs? (Y/N)" } if ($answertotest.ToLower()[0] -eq "y") { $iserror = $false foreach ($server in $dhcpserversarray) { $message = "Succès!" $color = "Green" if (!(Test-Connection $server -Count 1 -Quiet -ErrorAction Stop)) { $iserror = $true $message = "Echec! :c" $color = "Red" } Write-host $server " => " $message -ForegroundColor $color } if ($iserror -eq $false) { Write-host "Tous les tests sont passés :)" } else { Write-Host "Une ou plusieurs erreurs sont arrivées." do { $answer = Read-host "Voulez vous continuer malgré tout? (Y/N)" }while ($answer.tolower()[0] -ne "y" -and $answer.tolower()[0] -ne "n") if ($answer.tolower()[0] -eq "n") { throw("Vous avez arrêté le script") } } } } #endregion #region MAIL $answertomail = "z" while($answertomail.ToLower()[0] -ne "y") { do { Write-Host "Renseignez le nom du SMTP de messagerie" $mailserver = Read-Host "=>" Write-Host "Renseignez l'adresse de l'EXPEDITEUR" $from = Read-Host "=>" Write-Host "Renseignez l'adresse du DESTINATAIRE" $to = Read-Host "=>" $mailconfiguration = [pscustomobject]@{ "Mailserver"=$mailserver "From" = $from "To" = $to } $mailconfiguration | ft $answertomail = Read-Host "Est-ce correct? (Y/N)" }while($answertomail.ToLower()[0] -ne "y" -and $answertomail.ToLower()[0] -ne "n") $answertosendmail = "z" $mailreceived = "z" if ($answertomail.ToLower()[0] -eq "y") { while($answertosendmail -ne "n") { do { $answertosendmail = Read-Host "Voulez vous envoyer un mail de test? (Y/N)" } while($answertosendmail.ToLower()[0] -ne "y" -and $answertosendmail.ToLower()[0] -ne "n") if ($answertosendmail.ToLower()[0] -eq "y") { $sendmailerror = $false try { Send-MailMessage -From $from -To $to -Subject "Test mail du script Powershell" -SmtpServer $mailserver } catch { $sendmailerror = $true Write-host "Une erreur s'est produite durant l'envoi du mail!" } if ($sendmailerror -eq $false) { do { $mailreceived = read-host "Le mail a été envoyé. L'avez vous recu? (Y/N)." }while ($mailreceived.ToLower()[0] -ne "y" -and $mailreceived.ToLower()[0] -ne "n") if ($mailreceived.ToLower()[0] -eq "n") { do { $answertomail = Read-Host "Voulez vous continuer et reconfigurer? (Y/N)" }while ($answertomail.ToLower()[0] -ne "y" -and $answertomail.ToLower()[0] -ne "n") switch ($answertomail.tolower()[0]) { "y" { $answertomail = "n";$answertosendmail = "n" } "n" { throw("Impossible d'envoyer le mail. Arrêt du script") } } } else { $answertosendmail = "n" } } } } } } #endregion #region CHEMIN $answer = "z" $path = $null do { do { $path = Read-Host "Entrez un chemin (local ou UNC) pour l'enregistrement des données du script`n=>" $answerpathtest = Read-Host "Voulez vous tester le chemin :`n$path`n(Y/N) =>" } until ($answerpathtest.ToLower()[0] -eq "y" -or $answerpathtest.ToLower()[0] -eq "n") if ($answerpathtest -eq "y") { $answerpathretry = "z" if (Test-Path $path) { Write-host -ForegroundColor Green "Le chemin est valide.`nTest d'écriture." $testfilename = [string](get-date).Ticks+".test" $answer = "y" try { New-Item -ItemType File (Join-Path $path $testfilename) -Force } catch { Write-Host "Impossible d'écrire dans $path `nVeuillez selectionner un autre dossier." $answer = "z" } Remove-Item (Join-Path $path $testfilename) -Force -ErrorAction SilentlyContinue } else { do { Write-Host "La destination n'existe pas. Voulez-vous la créer? (Y/N)" $answerpathretry = Read-Host "=>" } until ($answerpathretry.ToLower()[0] -eq "y" -or $answerpathretry.ToLower()[0] -eq "n") if ($answerpathretry.ToLower()[0] -eq "n") { throw("Arrêt du script") } $answer = "z" } } } until ($answer.ToLower()[0] -eq "y" -or $answer.ToLower()[0] -eq "n") $answer = "z" do { $answer = Read-Host "Voulez vous surveiller les réservations DHCP également? (Y/N)" } until($answer.ToLower()[0] -eq "y" -or $answer.ToLower()[0] -eq "n") #endregion $dhcprescheck = $true if ($answer.ToLower()[0] -eq "n") { $dhcprescheck = $false } } try { $configurationarray = New-Object PSCUSTOMOBJECT $configurationarray | Add-Member -Name pools -MemberType NoteProperty -Value $ippoolarray $configurationarray | Add-Member -name servername -MemberType NoteProperty -Value $dhcpserversarray $configurationarray | Add-Member -Name mailconfiguration -MemberType NoteProperty -Value $mailconfiguration $configurationarray | Add-Member -Name path -MemberType NoteProperty -Value $path $configurationarray | Add-Member -Name rescheck -MemberType NoteProperty -Value $dhcprescheck } catch { Write-Host -ForegroundColor Red "Une erreur s'est produite durant l'assemblage des infos de configuration. Voici l'état actuel de l'objet représentant la configuration :`n$configurationarray" } try { $configurationarray | Export-Clixml $configfile -ErrorAction Stop } catch { Write-Host -ForegroundColor Red "Impossible d'enregistrer le fichier de configuration. Le script va s'arrêter" } #endregion } #end catch $configurationarray = Import-Clixml $configfile -ErrorAction Stop #region fichiers #Précisez le chemin du fichier ou seront sauvegardées les données des PC détectés $path = $configurationarray.path $filename = "outsidedomain.xml" #Nom a donner au fichier. $staticfile = "staticaddress.xml" #Nom a donner au fichier. $exceptionfile = "exceptions.txt" #Nom du fichier d'exceptions $exceptionpath = Join-Path $path $exceptionfile $fullpath = Join-Path $path $filename $staticpath = Join-Path $path $staticfile $dateforcsv = Get-Date -Format "yyyy_MM" #endregion #region DHCP $plages = $configurationarray.pools #Plage(s) à surveiller $servername = $configurationarray.servername #nom de serveur(s) DHCP #endregion #region MAIL $from = $configurationarray.mailconfiguration.From #Adresse mail de l'expéditeur $to = $configurationarray.mailconfiguration.To #Adresse mail du destinataire $smtpserver = $configurationarray.mailconfiguration.Mailserver #Serveur SMTP $encoding = [System.Text.UTF8Encoding]::UTF8 #Encodage en UTF8 pour éviter les problèmes d'accents, entre autre #endregion #region jobs $maximaljobperserver = 4 #endregion #region variables try { Import-module ActiveDirectory } catch { Write-host "Impossible de charger le module Active Directory pour Powershell. Ce module est-il bien installé et activé?" Start-Sleep -Seconds 5 throw("No AD Module") } $computerlist = Get-ADComputer -filter * $arraylist = New-Object System.Collections.ArrayList $notindomainarraylist = New-Object System.Collections.ArrayList $resultingarray = New-Object System.Collections.ArrayList $staticresults = New-Object System.Collections.ArrayList $staticmodified = New-Object System.Collections.ArrayList $monitorstatic = $true #endregion function Report-StaticChange { param( [string]$Raison = "non spécifiée", [string]$NewIP = $null, [string]$OldIP = $null, [string]$NewMAC = $null, [string]$OldMAC = $null) $staticmodified.Add([pscustomobject]@{ "Raison" = $Raison "Nouvelle_IP" = $NewIP "Ancienne_IP" = $OldIP "Nouvelle_MAC" = $NewMAC "Ancienne_MAC" = $OldMAC}) | Out-Null } try { $staticresultsprevious = Import-Clixml $staticpath } catch { "Aucun fichier précedent d'IP reservées" } try{ $exceptions = Get-content $exceptionpath -erroraction stop } catch { Try { New-Item -ItemType File $exceptionpath -Force } catch { "Impossible de créer le fichier d'exception. Arrêt du script" Start-Sleep -Seconds 5 throw("STOP FICHIER EXCEPT.") } } try { $fichiernondomaine = Import-Clixml $fullpath } catch { Write-Output "Aucun fichier de précedence à $fullpath, vérification du chemin." if (!(Test-Path $fullpath)) { Write-Output "Chemin inexistant, création du fichier" try { New-Item -ItemType File $fullpath -Force -ErrorAction Stop } catch { Write-Output "Impossible de créer le fichier, arret du script." Start-Sleep -Seconds 5 throw($error[0].Exception) } } } foreach($plage in $plages) { foreach ($server in $servername) { if ($monitorstatic -eq $true) { if ((get-job -Name "$server*" | Where-Object {$_.state -eq "Running"}).count -ge $maximaljobperserver) { Write-Output "Plus de $maximaljobperserver jobs actifs sur $server. En attente de la libération de jobs." do { Start-Sleep -Seconds 1 } while ((get-job -Name "$server*" | Where-Object {$_.state -eq "Running"}).count -ge $maximaljobperserver) } Start-Job -Name $($server+"_"+$plage+"_static") -ScriptBlock { param($plage,$server) $jobstaticresults = New-Object System.Collections.ArrayList $staticstats = Invoke-Command -computername $server -scriptblock {powershell.exe "chcp 1252;Netsh dhcp server scope $($args[0]) show reservedip"} -ArgumentList $plage foreach ($entry in $staticstats) { if ($entry.trim() -match "(?\d+.\d+.\d+.\d+)\W+-\W+(?\w+-\w+-\w+-\w+-\w+-\w+)") { $jobstaticresults.Add([PSCUSTOMOBJECT]@{ "IP" = $Matches.IP "MAC" = $Matches.MAC }) | Out-Null } } return $jobstaticresults } -ArgumentList $plage,$server #end start-job } #end if ($monitostatic -eq $true) if ((get-job -Name "$server*" | Where-Object {$_.state -eq "Running"}).count -ge $maximaljobperserver) { Write-Output "Plus de $maximaljobperserver jobs actifs sur $server. En attente de la libération de jobs. (leases)" do { Start-Sleep -Seconds 1 } while ((get-job -Name "$server*" | Where-Object {$_.state -eq "Running"}).count -ge $maximaljobperserver) } Start-Job -Name $($server+"_"+$plage+"_leases") -ScriptBlock { param($plage,$server) $jobarraylist = New-Object System.Collections.ArrayList $stats = Invoke-Command -computername $server -scriptblock {powershell.exe "chcp 1252;Netsh dhcp server scope $($args[0]) show clients 1"} -ArgumentList $plage foreach ($entry in $stats) { if ($entry.trim() -match "(?\d+.\d+.\d+.\d+.)\W+-\W+(?\d+.\d+.\d+.\d+.).*?[^\w+](?\w+-\w+-\w+-\w+-\w+-\w+).*?[^\d+](?\d+/\d+/\d+\s+\d+:\d+:\d+).*?[^\w+](?\w+).*?[^\w+](?\w+.*)$") { $jobarraylist.Add([pscustomobject]@{ "IP" = $Matches.ip "Masque" = $Matches.MASQUE "MAC" = $Matches.MAC "PC" = $Matches.PC "Type" = $Matches.TYPE "Date" = (get-date $Matches.Bail ) }) | Out-Null } } return $jobarraylist } -ArgumentList $plage,$server #end start-job } } Get-job | Wait-Job $joblist = Get-Job | Where-Object {$_.State -eq "Completed"} Write-Output "Obtention des résultats d'IP statiques" foreach ($job in ($joblist |Where-Object {$_.name -like "*static"})) { $value = Receive-Job $job if ($value -ne $null) { try { $staticresults.AddRange($value) | Out-Null } catch { try { $staticresults.Add($value) | Out-Null } catch { Write-Output "Erreur lors de l'ajout des données du job $($job.name) avec comme valeur $($value)" } } } Remove-Job $job } $joblist = Get-Job foreach ($job in $joblist) { $value = Receive-Job $job if ($value -ne $null) { try{ $arraylist.AddRange($value) | Out-Null } catch { try { $arraylist.Add($value) | Out-Null } catch { Write-Output "Erreur sur l'ajout de données du job $($job.name) avec comme valeur $($value)" } } } Remove-Job $job -ErrorAction stop } foreach ($job in Get-Job) { Write-Output "Erreur sur $job. $Job.state" Receive-Job $job } foreach ($computer in $arraylist) { $results = $arraylist | Where-Object {$_.pc -eq $computer.PC} foreach ($result in $results) { if ($computerlist.dnshostname -notcontains $result.pc) { "$($result.pc) N'EST PAS CONNU DANS L'AD." $notindomainarraylist.Add($result) | Out-Null } } } #Filtrage en fonction des exceptions. Ne garder que ce qui n'a pas d'adresse mac et de nom correspondant à la liste. $notindomainwithexceptions = $notindomainarraylist | Where-Object {$_.MAC -notin $exceptions -and $_.PC -notin $exceptions} if ($fichiernondomaine -ne $null) { foreach ($computer in $notindomainwithexceptions) { if ($fichiernondomaine.pc.contains($computer.pc)) { #Recherche du pc X avec l'adresse mac Y $results = $fichiernondomaine | Where-Object {$_.pc -eq $computer.pc -and $_.MAC -eq $computer.MAC} | Sort-Object -property ip,date -Unique if ($results -eq $null) { "results eq null" #L'ordinateur est trouvé, mais sous une autre carte réseau $index = $notindomainwithexceptions.pc.IndexOf($computer.pc) #changé $_.pc en $computer.pc $item = $notindomainwithexceptions[$index] $item | Add-Member -MemberType NoteProperty -Name "Raison" -Value "Nouvelle carte réseau" -force $resultingarray.Add($item) | Out-Null } elseif ($results.ip -contains $computer.ip) { #L'ordinateur est trouvé dans le DHCP, on vérifie si le bail expire à la même date. #On filtre uniquement sur l'objet ayant la même adresse que l'ordinateur $item = $results | Where-Object {$_.ip -eq $computer.ip} if ($computer.Date -gt $item.Date) { #La valeur date est plus grande, il a donc eu un renouvellement de bail $item | Add-Member -MemberType NoteProperty -Name "Raison" -Value "Renouvellement du bail DHCP" -Force $resultingarray.Add($item) | Out-Null } #Sinon, on enregistre pas, on avertit pas. } } else { #Si le fichier xml importé ne comporte pas l'ordinateur, c'est donc une nouvelle connexion $item = $computer $item | Add-Member -MemberType NoteProperty -Name "Raison" -Value "Nouvelle connexion" -Force $resultingarray.Add($item) | Out-Null } } $notindomainarraylist | Export-Clixml $fullpath if ($resultingarray -ne $null) { $resultingarray = $resultingarray | Select-object -property IP,Masque,Mac,PC,Type,Date,Raison Send-MailMessage -From $from -To $to -Subject "Nouvelles connexions détectées" -Bodyashtml $([string]($resultingarray | ConvertTo-Html -PreContent "Type : N - AUCUN, D - DHCP B - BOOTP, U - NON SPÉCIFIÉ, R - RÉSERVATION IP" -Head "")) -Encoding $encoding -SmtpServer $smtpserver $resultingarray | Export-Csv (join-path $configurationarray.path $("out_domain_"+ $dateforcsv +".csv")) -NoTypeInformation -Append -Delimiter ";" -Encoding UTF8 } } else { Write-Output "Aucun fichier de précedence du DHCP trouvé. Enregistrement des données actuelles en tant que reference" $notindomainarraylist | Export-Clixml $fullpath Send-MailMessage -From $from -To $to -Subject "Première analyse faite. Surveillance de ces entrées." -Bodyashtml $([string]($notindomainwithexceptions | ConvertTo-Html -PreContent "Type : N - AUCUN, D - DHCP B - BOOTP, U - NON SPÉCIFIÉ, R - RÉSERVATION IP" -Head "")) -Encoding $encoding -SmtpServer $smtpserver } #Gestion des IP Statiques if ($monitorstatic -eq $true) { $staticresults = $staticresults | Select-object -property IP,MAC | Sort-Object -Property "ip","mac" -Unique if ($staticresultsprevious -eq $null) { Write-Output "Aucun fichier de précedence des réservations trouvé. Enregistrement des données actuelles en tant que reference" $staticresults | Export-Clixml $staticpath } else { foreach ($entry in $staticresults) { #Si l'ip est bien dans la liste des résultats précedents... if ($entry.ip -in $staticresultsprevious.ip) { #On reprends l'objet de la fois précedente... $check = $staticresultsprevious | Where-Object {$_.ip -eq $entry.ip} #Si leurs macs différent, l'adresse mac a été changée if ($entry.MAC -ne $check.MAC) { "Mac." Report-StaticChange -Raison "Mac Changée" -OldIP $check.ip -NewIP $entry.ip -OldMAC $check.mac -NewMAC $entry.MAC } } #Si l'ip n'est pas dans la liste des résultats précédents #MAIS que la MAC y est... L'IP a donc changé elseif ($entry.mac -in $staticresultsprevious.mac) { "ip" $check = $staticresultsprevious | Where-Object {$_.mac -eq $entry.mac} Report-StaticChange -Raison "IP Changée" -OldIP $check.ip -NewIP $entry.ip -OldMAC $check.mac -NewMAC $entry.MAC } #Si ni MAC ni IP, alors c'est une nouvelle entrée else { "new" Report-StaticChange -Raison "Nouvelle entrée" -OldIP $null -NewIP $entry.ip -NewMAC $entry.MAC } } #On loop et vérifie que chaque ancien résultat est toujours présent pour s'assurer que rien n'est supprimé foreach ($staticresultprevious in $staticresultsprevious) { #Si l'IP n'est pas dans la liste actuelle, et que la MAC n'est pas dans la liste actuelle if ($staticresultprevious.ip -notin $staticresults.ip -and $staticresultprevious.mac -notin $staticresults.mac) { #On regarde si l'adresse et la mac ne sont pas dans les dernières détections de changement MAC / IP #afin de ne pas marquer une entrée comme retirée alors qu'elle est simplement modifiée if ($staticresultprevious.ip -notin $staticmodified.ip -or $staticresultprevious.mac -notin $staticmodified.mac) { Report-StaticChange -Raison "Entrée retirée" -OldIP $null -NewIP $staticresultprevious.ip -NewMAC $staticresultprevious.MAC } } } $staticresults | Export-Clixml $staticpath if ($staticmodified.Count -gt 0) { Send-MailMessage -From $from -To $to -Subject "Modifications des reservations DHCP" -Bodyashtml $([string]($staticmodified | ConvertTo-Html -PreContent "Liste des modifications détectées sur les réservations du DHCP" -Head "")) -Encoding $encoding -SmtpServer $smtpserver $staticmodified | Export-Csv (join-path $configurationarray.path $("reserved_changed_"+ $dateforcsv +".csv")) -NoTypeInformation -Append -Delimiter ";" -Encoding UTF8 } } }