Powershell : listing d’enregistrements DNS

J’ai développé un (plutôt deux, à vrai dire) script(s) Powershell permettant de remplacer la fonction de filtre présente sur la console DNS. En effet, celle-ci est un peu pénible à utiliser car il faut attendre que toute la zone soit chargée dans la console si le filtre n’est pas encore actif, puis il faut rafraîchir… et il est persistant après fermeture, ce qui fait qu’il faut réinitialiser le filtre et actualiser lorsqu’on souhaite revenir à une vision d’ensemble.

A l’exécution, le script demande le nom ou l’IP du serveur DNS à interroger, ainsi que la chaîne de caractères à trouver dans le nom de l’enregistrement. Le joker * est utilisable, ce qui permet à la fois de rechercher une expression ou une chaîne stricte.

La version basique du script renvoie ses résultats dans la console avec uniquement le nom de l’enregistrement :

Write-Host "=========================================="
Write-Host "DNS records listing script - Basic Version"
Write-Host "=========================================="
$dnsnode = Read-Host "DNS Server to query"
$lfseq = Read-Host "Character sequence to look for ('*' tokens accepted)"
Write-Host "Connecting to DNS Server and retrieving DNS zones"
$dnszones = Get-DNSServerZone -ComputerName $dnsnode | Where-Object {$_.IsReverseLookupZone -eq $false}
foreach($dnszone in $dnszones){
Write-Host "Zone currently parsed:" $dnszone.ZoneName
$dnsrecords = Get-DnsServerResourceRecord -ComputerName $dnsnode -ZoneName $dnszone.ZoneName | Where-Object {$_.HostName -like "$lfseq"} | ft HostName
if($dnsrecords.length -gt 0) { $dnsrecords }
else { Write-Host "No matching records found." }
Write-Host "`r"
}
Write-Host "Parsing over."
Write-Host "Bye."

La version améliorée renvoie les résultats dans la console, mais génère un fichier CSV offrant plus d’informations (type d’enregistrement et données de l’enregistrement) et effectue une suppression des doublons : nécessaire car la commande Powershell peut renvoyer plusieurs fois le même enregistrement si il est dans une sous-zone. Par exemple, en considérant la zone infra.local, si un enregistrement s’appelle sql dans la sous-zone prod, il apparaîtra alors deux fois : une fois en tant que sql.prod et une fois en tant que sql.prod.infra.local.

Write-Host "============================================="
Write-Host "DNS records listing script - Enhanced Version"
Write-Host "============================================="
$dnsnode = Read-Host "DNS Server to query"
$lfseq = Read-Host "Character sequence to look for ('*' tokens accepted)"
$today = Get-Date -f yyyyMMdd
$path = "dns-$today.csv"
$csvheader = "Hostname,Zone,RecordType,RecordData"
Add-Content -Value $csvheader -Path $path
Write-Host "Connecting to DNS Server and retrieving DNS zones"
$dnszones = Get-DNSServerZone -ComputerName $dnsnode | Where-Object {$_.IsReverseLookupZone -eq $false}
foreach($dnszone in $dnszones){
Write-Host "Zone currently parsed:" $dnszone.ZoneName
$dnsrecords = Get-DnsServerResourceRecord -ComputerName $dnsnode -ZoneName $dnszone.ZoneName | Where-Object {$_.HostName -like "$lfseq"}
if($dnsrecords.length -gt 0){
foreach($dnsrecord in $dnsrecords){
Write-Host "Record: "$dnsrecord.Hostname
switch($dnsrecord.RecordType){
"A" {$recorddata = $dnsrecord.RecordData.IPv4Address.IpAddressToString}
"CNAME" {$recorddata = $dnsrecord.RecordData.HostNameAlias}
}
if($dnsrecord.HostName.SubString($dnsrecord.HostName.Length-$dnszone.ZoneName.Length,$dnszone.ZoneName.Length) -ne $dnszone.ZoneName) {
$row = $dnsrecord.Hostname+","+$dnszone.ZoneName+","+$dnsrecord.RecordType+","+$recorddata
Add-Content -Value $row -Path $path
}
}
Write-Host "DNS records found:"$dnsrecords.length
}
else { Write-Host "No matching records found." }
Write-Host "`r"
}
Write-Host "Parsing over, cleaned duplicates, hence the difference between the number of lines in the CSV file and the number displayed in console. Please find the output CSV file named $path in $pwd."
Write-Host "Bye."
explorer $pwd

Veeam et Powershell : état des jobs de sauvegardes sur les dernières 24 heures

Afin de me faciliter la tâche sur certains contrôles quotidiens, j’ai développé un script Powershell pour Veeam Backup & Replication me permettant d’afficher rapidement si il y a eu des échecs de sauvegarde durant les dernières 24 heures.

Ce script est plutôt simple et contrôle les sauvegardes de machines virtuelles VMware (supporte également Hyper-V il me semble) ainsi que les agents Veeam déployés sur des machines Windows. Veeam possède diverses commandes Powershell pour les divers types de sauvegarde qu’il propose (sauvegarde sur bande, par exemple) ; si il y a besoin de contrôler d’autres types de sauvegarde, alors il suffit simplement de réaliser une requête supplémentaire Get-VBRxSessionx sera évidemment remplacé par le type de sauvegarde.

J’ai testé ce script sur Veeam B&R version 9.5.

Add-PSSnapin VeeamPSSnapIn
Disconnect-VBRServer
$SrvList = @("backupsrv1","backupsrv2","veeamdev1","veeamdev2")
Write-Host "Veeam B&R Daily Checks Jobs Script"

function process{
param($type)
if($session.Result -eq "Failed"){
switch ($type){
"std" { $name = $session.Name ; $RetryList = $sessionList | Where { $_.Name -eq $name } }
"agent" { $job = Get-VBREPJob -ID $session.JobId ; $name = $job.Name ; $RetryList = $sessionAgentList | Where { $_.JobId -eq $session.JobId } }
}
$status = $session.Result
$st = $session.CreationTime
$et = $session.EndTime
$global:count+=1
echo "Job: $name"
echo "Status: $status"
echo "Start Time: $st"
echo "End Time: $et"
foreach($Retry in $RetryList){
if ($Retry.Result -ne "Failed"){
if ($Retry.State -ne "Running"){
$st = $Retry.CreationTime
$et = $Retry.EndTime
echo "Job $name finally succeeded."
echo "Start Time: $st"
echo "End Time: $et"
}
if ($Retry.State -eq "Running"){
echo "Job $name currently running."
}
}
}
}
}

foreach($UniqueSrv in $SrvList){
Write-Host "Processing server $UniqueSrv"
Connect-VBRServer -Server $UniqueSrv
$SessionList = Get-VBRBackupSession | ?{($_.EndTime -ge (Get-Date).AddHours(-24) -or $_.CreationTime -ge (Get-Date).AddHours(-24))}
$SessionAgentList = Get-VBREPSession | ?{($_.EndTime -ge (Get-Date).AddHours(-24) -or $_.CreationTime -ge (Get-Date).AddHours(-24))}
$global:count = 0
foreach($session in $SessionList){
process("std")
}
foreach($session in $SessionAgentList){
process("agent")
}
if ($global:count -eq 0){ Write-Host "No errors detected in the jobs for this server." }
Disconnect-VBRServer
}

Merci à smasterson d’avoir partagé un script complet de reporting Veeam en HTML, dont j’ai repris le pipe pour la condition des 24 dernières heures, mon premier développement de script fonctionnant autrement, cette syntaxe est plus propre. La documentation de Veeam étant ni claire ni complète et l’interface avec Powershell particulièrement catastrophique (propriétés, variables et retours différents entre deux objets pour rien, d’où l’usage de switch et gestion des états de fin de traitement anarchique : certaines sauvegardes s’arrêtent sans retour, d’autres en succès en fonction de leur type), j’ai donc dû m’y prendre à plusieurs fois avant de proposer un script fonctionnel.

Powershell : duplication de groupe ActiveDirectory

Aussi surprenant que cela puisse paraître, il n’est pas possible de dupliquer nativement dans ActiveDirectory un groupe via l’interface graphique. Bien qu’il existe des outils comme Quest permettant de faire ce genre de tâches, il est possible de réaliser une duplication de groupe avec Powershell sans trop se prendre la tête.

Ce script va donc demander à l’utilisateur le nom du groupe d’origine puis le nom du nouveau groupe. Il va copier les informations de base (type de groupe, sécurité, nom, description et membres) pour les réinjecter à l’identique dans le nouveau groupe qui sera créé au même endroit.

Write-Host "ActiveDirectory Group Duplication Script"
Write-Host "========================================"
Write-Host "This script will make a copy of an AD group."
$origgrpname = Read-Host "Input the original group you want to duplicate"
$newgrpname = Read-Host "Input the new group's name"
Write-Host "Working... Depending on the size of your domain or the group, it might take some time..."
$origgrpdtl = Get-ADGroup $origgrpname -Properties Description, DisplayName
$origgrpmbr = Get-ADGroupMember $origgrpname
$path = $origgrpdtl.DistinguishedName.SubString(4+$origgrpname.length,$origgrpdtl.DistinguishedName.Length-4-$origgrpname.Length)
New-ADGroup -Name $newgrpname -GroupScope $origgrpdtl.GroupScope -Description $origgrpdtl.Description -DisplayName $origgrpdtl.DisplayName -GroupCategory $origgrpdtl.GroupCategory -Path $path
foreach($member in $origgrpmbr) {
Add-ADGroupMember $newgrpname $member.samAccountName
}
Write-Host "Duplication process over."
echo "Group $origgrpname has been duplicated to $newgrpname. The new group is located in $path along the original one."
Write-Host "Bye"

J’ai réalisé ce script dans le cadre de création de nouveaux groupes de partages pour un nouveau serveur, il me fallait donc un moyen rapide de recréer plusieurs groupes sans avoir à ajouter à la main les dizaines d’utilisateurs présents. Naturellement, on prendra soin d’exécuter ce script avec un compte ayant suffisamment de privilèges et en faisant attention au nom des groupes puisque je n’ai inclus aucun contrôle de robustesse.

Powershell : listing des objets ordinateur n’ayant pas donné signe de vie depuis un laps de temps

Dans la série « J’entretiens proprement mon ActiveDirectory », je vais vous proposer aujourd’hui un script vous permettant de lister dans un fichier CSV les objets Computer qui n’ont pas donné de signe de vie sur le domaine depuis un certain nombre de jours spécifié (par défaut dans ce script, 60).

Il est important de bien faire attention aux OU sur lesquelles nous souhaitons effectuer la recherche, afin d’éviter de supprimer des stations qui ne servent que ponctuellement ou qui ne sont positionnées sur le réseau que lors des campagnes de patchs par exemple. Il conviendra donc de modifier dans le script le paramètre SearchBase qui est passé lors de la requête ActiveDirectory.

$csvoutput = "$env:temp\orphan-computers.csv"
Write-Host "Orphan Computer objects listing script"
Write-Host "======================================"
Write-Host ""
Write-Host "This script will output to a file a list of Computer objects in the ActiveDirectory that haven't logged onto the domain for a specified number of days."
[int]$nbdays = Read-Host "Number of days before considering a Computer object orphan ? (default: 60)"
if ($nbdays -eq "") { $nbdays = 60 }
$today = Get-Date
$complist = Get-AdComputer -filter * -Properties LastLogonDate -SearchBase "OU=serveurs,DC=superdomaine,DC=com"
foreach ($computer in $complist) {
$lastlogondate = $computer.LastLogonDate
$fqdn = $computer.DNSHostName
if ($lastlogondate -eq $null) { Add-Content $csvoutput "$fqdn,'Object never logged on'" }
elseif ($lastlogondate.AddDays($nbdays) -lt $today) {
Add-Content $csvoutput "$fqdn,$lastlogondate"
}
}
Write-Host "All done. File is located in $env:temp."
explorer $env:temp

Les stations ont leur nom DNS et leur dernière date de communication avec le domaine de renseignées dans un fichier CSV écrit dans le répertoire des fichiers temporaires.