Blog/Windows/dhcp_check.ps1

690 lines
26 KiB
PowerShell
Raw Permalink Normal View History

2023-12-17 01:11:55 +01:00
#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 "(?<ip>\d+.\d+.\d+.\d+)\W+-\W+(?<mac>\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 "(?<ip>\d+.\d+.\d+.\d+.)\W+-\W+(?<masque>\d+.\d+.\d+.\d+.).*?[^\w+](?<mac>\w+-\w+-\w+-\w+-\w+-\w+).*?[^\d+](?<bail>\d+/\d+/\d+\s+\d+:\d+:\d+).*?[^\w+](?<type>\w+).*?[^\w+](?<pc>\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
}
}
}