Com muito obrigado a ErikE e aos outros aqui, segui um caminho ... Não direi que é o caminho certo, mas o script do Powershell que inventei faz o truque.
O código está abaixo se alguém quiser. Basta executá-lo apontando manualmente para cada servidor DHCP ou agendá-lo (novamente apontando para cada servidor DHCP no script).
O que o script faz:
- Obtém informações de concessão do servidor DHCP (concessões ipv4)
- Envia as concessões para um arquivo csv
- Lê novamente esse arquivo CSV para consultar o AD
- Consulta o AD ao computador
- Se não encontrado, gera um novo arquivo txt
- Cria um arquivo txt final de lista exclusivo do criado em # 5 acima (pois pode haver enganos se o cliente registrar mais de uma vez ou com mais de um adaptador)
- envia por e-mail o conteúdo do arquivo final para um administrador
O que você precisará:
O script usa o módulo AD ( import-module activedirectory
), portanto, é melhor executá-lo em um AD DC executando DHCP. Se esse não for o seu caso, você pode instalar o módulo AD powershell: http://blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory- module-in-powershell-in-windows-7.aspx
Você também precisará dos cmdlets do AD Powershell da Quest encontrados aqui: http://www.quest.com/powershell/activeroles-server.aspx . Instale ESTES ANTES de executar o script ou ele falhará.
O script em si (higienizado, você precisará configurar algumas das variáveis para atender às suas necessidades, como os nomes dos arquivos de entrada, o domínio ao qual se conectar, o servidor dhcp ao qual se conectar, as configurações de e-mail no final etc.):
# Get-nonADclientsOnDHCP.ps1
# Author : TheCleaner http://serverfault.com/users/7861/thecleaner with a big thanks for a lot of the lease grab code to Assaf Miron on code.google.com
# Description : This Script grabs the current leases on a Windows DHCP server, outputs it to a csv
# then takes that csv file as input and determines if the lease is from a non-AD joined computer. It then emails
# an administrator notification. Set it up on a schedule of your choosing in Task Scheduler.
# This helps non-802.1X shops keep track of rogue DHCP clients that aren't part of the domain.
#
# Input : leaselog.csv
# Output: Lease log = leaselog.csv
# Output: Rogue Clients with dupes = RogueClients.txt
# Output: Rogue Clients - unique = RogueClientsFinal.txt
$DHCP_SERVER = "PUT YOUR SERVER NAME OR IP HERE" # The DHCP Server Name
$LOG_FOLDER = "C:\DHCP" # A Folder to save all the Logs
# Create Log File Paths
$LeaseLog = $LOG_FOLDER+"\LeaseLog.csv"
#region Create Scope Object
# Create a New Object
$Scope = New-Object psobject
# Add new members to the Object
$Scope | Add-Member noteproperty "Address" ""
$Scope | Add-Member noteproperty "Mask" ""
$Scope | Add-Member noteproperty "State" ""
$Scope | Add-Member noteproperty "Name" ""
$Scope | Add-Member noteproperty "LeaseDuration" ""
# Create Each Member in the Object as an Array
$Scope.Address = @()
$Scope.Mask = @()
$Scope.State = @()
$Scope.Name = @()
$Scope.LeaseDuration = @()
#endregion
#region Create Lease Object
# Create a New Object
$LeaseClients = New-Object psObject
# Add new members to the Object
$LeaseClients | Add-Member noteproperty "IP" ""
$LeaseClients | Add-Member noteproperty "Name" ""
$LeaseClients | Add-Member noteproperty "Mask" ""
$LeaseClients | Add-Member noteproperty "MAC" ""
$LeaseClients | Add-Member noteproperty "Expires" ""
$LeaseClients | Add-Member noteproperty "Type" ""
# Create Each Member in the Object as an Array
$LeaseClients.IP = @()
$LeaseClients.Name = @()
$LeaseClients.MAC = @()
$LeaseClients.Mask = @()
$LeaseClients.Expires = @()
$LeaseClients.Type = @()
#endregion
#region Create Reserved Object
# Create a New Object
$LeaseReserved = New-Object psObject
# Add new members to the Object
$LeaseReserved | Add-Member noteproperty "IP" ""
$LeaseReserved | Add-Member noteproperty "MAC" ""
# Create Each Member in the Object as an Array
$LeaseReserved.IP = @()
$LeaseReserved.MAC = @()
#endregion
#region Define Commands
#Commad to Connect to DHCP Server
$NetCommand = "netsh dhcp server \\$DHCP_SERVER"
#Command to get all Scope details on the Server
$ShowScopes = "$NetCommand show scope"
#endregion
function Get-LeaseType( $LeaseType )
{
# Input : The Lease type in one Char
# Output : The Lease type description
# Description : This function translates a Lease type Char to it's relevant Description
Switch($LeaseType){
"N" { return "None" }
"D" { return "DHCP" }
"B" { return "BOOTP" }
"U" { return "UNSPECIFIED" }
"R" { return "RESERVATION IP" }
}
}
function Check-Empty( $Object ){
# Input : An Object with values.
# Output : A Trimmed String of the Object or '-' if it's Null.
# Description : Check the object if its null or not and return it's value.
If($Object -eq $null)
{
return "-"
}
else
{
return $Object.ToString().Trim()
}
}
function out-CSV ( $LogFile, $Append = $false) {
# Input : An Object with values, Boolean value if to append the file or not, a File path to a Log File
# Output : Export of the object values to a CSV File
# Description : This Function Exports all the Values and Headers of an object to a CSV File.
# The Object is recieved with the Input Const (Used with Pipelineing) or the $inputObject
Foreach ($item in $input){
# Get all the Object Properties
$Properties = $item.PsObject.get_properties()
# Create Empty Strings - Start Fresh
$Headers = ""
$Values = ""
# Go over each Property and get it's Name and value
$Properties | %{
$Headers += $_.Name + ","
$Values += $_.Value
}
# Output the Object Values and Headers to the Log file
If($Append -and (Test-Path $LogFile)) {
$Values | Out-File -Append -FilePath $LogFile -Encoding Unicode
}
else {
# Used to mark it as an Powershell Custum object - you can Import it later and use it
# "#TYPE System.Management.Automation.PSCustomObject" | Out-File -FilePath $LogFile
$Headers | Out-File -FilePath $LogFile -Encoding Unicode
$Values | Out-File -Append -FilePath $LogFile -Encoding Unicode
}
}
}
#region Get all Scopes in the Server
# Run the Command in the Show Scopes var
$AllScopes = Invoke-Expression $ShowScopes
# Go over all the Results, start from index 5 and finish in last index -3
for($i=5;$i -lt $AllScopes.Length-3;$i++)
{
# Split the line and get the strings
$line = $AllScopes[$i].Split("-")
$Scope.Address += Check-Empty $line[0]
$Scope.Mask += Check-Empty $line[1]
$Scope.State += Check-Empty $line[2]
# Line 3 and 4 represent the Name and Comment of the Scope
# If the name is empty, try taking the comment
If (Check-Empty $line[3] -eq "-") {
$Scope.Name += Check-Empty $line[4]
}
else { $Scope.Name += Check-Empty $line[3] }
}
# Get all the Active Scopes IP Address
$ScopesIP = $Scope | Where { $_.State -eq "Active" } | Select Address
# Go over all the Adresses to collect Scope Client Lease Details
Foreach($ScopeAddress in $ScopesIP.Address){
# Define some Commands to run later - these commands need to be here because we use the ScopeAddress var that changes every loop
#Command to get all Lease Details from a specific Scope - when 1 is amitted the output includes the computer name
$ShowLeases = "$NetCommand scope "+$ScopeAddress+" show clients 1"
#Command to get all Reserved IP Details from a specific Scope
$ShowReserved = "$NetCommand scope "+$ScopeAddress+" show reservedip"
#Command to get all the Scopes Options (Including the Scope Lease Duration)
$ShowScopeDuration = "$NetCommand scope "+$ScopeAddress+" show option"
# Run the Commands and save the output in the accourding var
$AllLeases = Invoke-Expression $ShowLeases
$AllReserved = Invoke-Expression $ShowReserved
$AllOptions = Invoke-Expression $ShowScopeDuration
# Get the Lease Duration from Each Scope
for($i=0; $i -lt $AllOptions.count;$i++)
{
# Find a Scope Option ID number 51 - this Option ID Represents the Scope Lease Duration
if($AllOptions[$i] -match "OptionId : 51")
{
# Get the Lease Duration from the Specified line
$tmpLease = $AllOptions[$i+4].Split("=")[1].Trim()
# The Lease Duration is recieved in Ticks / 10000000
$tmpLease = [int]$tmpLease * 10000000; # Need to Convert to Int and Multiply by 10000000 to get Ticks
# Create a TimeSpan Object
$TimeSpan = New-Object -TypeName TimeSpan -ArgumentList $tmpLease
# Calculate the $tmpLease Ticks to Days and put it in the Scope Lease Duration
$Scope.LeaseDuration += $TimeSpan.TotalDays
# After you found one Exit the For
break;
}
}
# Get all Client Leases from Each Scope
for($i=8;$i -lt $AllLeases.Length-4;$i++)
{
# Split the line and get the strings
$line = [regex]::split($AllLeases[$i],"\s{2,}")
# Check if you recieve all the lines that you need
$LeaseClients.IP += Check-Empty $line[0]
$LeaseClients.Mask += Check-Empty $line[1].ToString().replace("-","").Trim()
$LeaseClients.MAC += $line[2].ToString().substring($line[2].ToString().indexOf("-")+1,$line[2].toString().Length-1).Trim()
$LeaseClients.Expires += $(Check-Empty $line[3]).replace("-","").Trim()
$LeaseClients.Type += Get-LeaseType $(Check-Empty $line[4]).replace("-","").Trim()
$LeaseClients.Name += Check-Empty $line[5]
}
# Get all Client Lease Reservations from Each Scope
for($i=7;$i -lt $AllReserved.Length-5;$i++)
{
# Split the line and get the strings
$line = [regex]::split($AllReserved[$i],"\s{2,}")
$LeaseReserved.IP += Check-Empty $line[0]
$LeaseReserved.MAC += Check-Empty $line[2]
}
}
#endregion
#region Create a Temp Scope Object
# Create a New Object
$tmpScope = New-Object psobject
# Add new members to the Object
$tmpScope | Add-Member noteproperty "Address" ""
$tmpScope | Add-Member noteproperty "Mask" ""
$tmpScope | Add-Member noteproperty "State" ""
$tmpScope | Add-Member noteproperty "Name" ""
$tmpScope | Add-Member noteproperty "LeaseDuration" ""
#endregion
#region Create a Temp Lease Object
# Create a New Object
$tmpLeaseClients = New-Object psObject
# Add new members to the Object
$tmpLeaseClients | Add-Member noteproperty "IP" ""
$tmpLeaseClients | Add-Member noteproperty "Name" ""
$tmpLeaseClients | Add-Member noteproperty "Mask" ""
$tmpLeaseClients | Add-Member noteproperty "MAC" ""
$tmpLeaseClients | Add-Member noteproperty "Expires" ""
$tmpLeaseClients | Add-Member noteproperty "Type" ""
#endregion
#region Create a Temp Reserved Object
# Create a New Object
$tmpLeaseReserved = New-Object psObject
# Add new members to the Object
$tmpLeaseReserved | Add-Member noteproperty "IP" ""
$tmpLeaseReserved | Add-Member noteproperty "MAC" ""
#endregion
# Go over all the Client Lease addresses and export each detail to a temporary var and out to the log file
For($l=0; $l -lt $LeaseClients.IP.Length;$l++)
{
# Get all Scope details to a temp var
$tmpLeaseClients.IP = $LeaseClients.IP[$l] + ","
$tmpLeaseClients.Name = $LeaseClients.Name[$l] + ","
$tmpLeaseClients.Mask = $LeaseClients.Mask[$l] + ","
$tmpLeaseClients.MAC = $LeaseClients.MAC[$l] + ","
$tmpLeaseClients.Expires = $LeaseClients.Expires[$l] + ","
$tmpLeaseClients.Type = $LeaseClients.Type[$l]
# Export with the Out-CSV Function to the Log File
$tmpLeaseClients | out-csv $LeaseLog -append $true
}
#Continue on figuring out if the DHCP lease clients are in AD or not
#Import the Active Directory module
import-module activedirectory
#import Quest AD module
Add-PSSnapin Quest.ActiveRoles.ADManagement
#connect to AD
Connect-QADService PUTTHEFQDNOFYOURDOMAINHERE_LIKE_DOMAIN.LOCAL | Out-Null
# get input CSV
$leaselogpath = "c:\DHCP\LeaseLog.csv"
Import-csv -path $leaselogpath |
#query AD for computer name based on csv log
foreach-object `
{
$NameResult = Get-QADComputer -DnsName $_.Name
If ($NameResult -eq $null) {$RogueSystem = $_.Name}
$RogueSystem | Out-File C:\DHCP\RogueClients.txt -Append
$RogueSystem = $null
}
Get-Content C:\DHCP\RogueClients.txt | Select-Object -Unique | Out-File C:\DHCP\RogueClientsFinal.txt
Remove-Item C:\DHCP\RogueClients.txt
#send email to netadmin
$smtpserver = "SMTP SERVER IP"
$from="DHCPSERVER@domain.com"
$to="TheCleaner@domain.com"
$subject="Non-AD joined DHCP clients"
$body= (Get-Content C:\DHCP\RogueClientsFinal.txt) -join '<BR> <BR>'
$mailer = new-object Net.Mail.SMTPclient($smtpserver)
$msg = new-object Net.Mail.MailMessage($from,$to,$subject,$body)
$msg.IsBodyHTML = $true
$mailer.send($msg)
Espero que ajude alguém!