web-dev-qa-db-ja.com

Windows DHCPサーバー-ADに参加していないデバイスがIPアドレスを取得したときに通知を受け取る

シナリオ

これを最も簡単な例に簡略化するには:

私はWindows 2008 R2標準DC DHCPサーバーの役割を持っています。さまざまなIPv4スコープを介してIPを配布します。問題はありません。

私が好きなもの

デバイスがDHCPアドレスリースを取得し、そのデバイスがドメインに参加しているコンピューターにない Active Directory。カスタムのPowershellなどであるかどうかは私には関係ありません。

結論=現時点では、802.1Xを使用せずに非ドメインデバイスがネットワーク上にあることを知る方法が必要です。これが成功したことを知っています静的IPデバイスは考慮されません。ネットワークをスキャンしてデバイスを検出する監視ソフトウェアはありますが、これほど詳細ではありません。

研究完了/オプションを検討

組み込みのロギングでは、そのような可能性はありません。

はい、802.1Xを認識しており、この場所で長期的に実装することができますが、そのようなプロジェクトからしばらく時間がたっています。これにより、ネットワーク認証の問題は解決しますが、これは私にとってはまだ役に立ちます。 802.1X目標の。

役に立つと思われるスクリプトビットなどを探してみましたが、見つけたものから、現時点ではgoogle-fuが失敗していると思います。

以下のロジックは正しいと思います(既存の解決策がないと仮定すると):

  1. デバイスがDHCPアドレスを受信する
  2. イベントログエントリが記録されます(DHCP監査ログのイベントID 10は機能します(新しいリースが更新ではなく、私が最も関心を持っているためです)。 http://technet.Microsoft.com/en -us/library/dd759178.aspx
  3. この時点で、おそらく何らかのスクリプトが、以下の残りの "STEPS"を引き継ぐ必要があります。
  4. どういうわけか、これらのイベントID 10についてこのDHCPログをクエリします(私はプッシュが好きですが、ここではプルが唯一の解決策だと思います)
  5. 新しいリースが割り当てられているデバイスの名前のクエリを解析します
  6. デバイスの名前をADに問い合わせます
  7. IFでnotが見つかりません、通知メールを送信します

これを正しく行う方法について誰かがアイデアを持っているなら、私は本当にそれを感謝します。私は "gimme the codez"を探していませんが、上記のリストに代わるものがあるかどうか、または明確に考えておらず、この情報を収集するための別の方法があるかどうかを知りたいです。これを実現するために共有したいコードスニペット/ PSコマンドがある場合は、なお良いでしょう。

15
TheCleaner

ErikEと他の人たちのおかげで、私は道を進みました...それが正しい道であるとは言えませんが、私が思いついたPowershellスクリプトでうまくいきます。

誰かがそれを望むなら、コードは以下です。各DHCPサーバーを手動でポイントして実行するか、またはスケジュールします(スクリプトで各DHCPサーバーをポイントします)。

スクリプトの機能:

  1. DHCPサーバーからリース情報を取得します(ipv4リース)
  2. リースをcsvファイルに出力します
  3. ADをクエリするためにそのCSVファイルを読み戻します
  4. コンピューターのADをクエリする
  5. 見つからない場合、新しいtxtファイルへの出力
  6. 上記の#5で作成されたファイルから一意のリスト最終txtファイルを作成します(クライアントが複数回または複数のアダプターに登録すると重複が発生する可能性があるため)
  7. 最終出力ファイルの内容を管理者にメールで送信します

必要なもの:

スクリプトはADモジュール(import-module activedirectory)ADで実行するのが最適ですDC DHCPを実行しています。これが当てはまらない場合は、AD powershellモジュールをインストールできます: http:// blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory-module-in-powershell-in-windows-7.aspx

ここにあるQuestのAD Powershellコマンドレットも必要です: http://www.quest.com/powershell/activeroles-server.aspxスクリプトを実行する前にこれらをインストールしないと、スクリプトが失敗します。

スクリプト自体(サニタイズ済み、入力ファイル名、接続するドメイン、接続するdhcpサーバー、終わり近くの電子メール設定など、ニーズに合わせていくつかの変数を設定する必要があります):

# 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="[email protected]"
$to="[email protected]"
$subject="Non-AD joined DHCP clients"
$body= (Get-Content C:\DHCP\RogueClientsFinal.txt) -join '<BR>&nbsp;<BR>'
$mailer = new-object Net.Mail.SMTPclient($smtpserver)
$msg = new-object Net.Mail.MailMessage($from,$to,$subject,$body)
$msg.IsBodyHTML = $true
$mailer.send($msg)

それが他の誰かを助けることを願っています!

6
TheCleaner

わかりました。ここでエチケットをフォローしているのかわかりませんが、以前の問題を編集する代わりに2番目の回答を投稿します。この問題には無関係であることが証明されていても、誰かに役立つ情報が含まれているためです。もしそれがこのフォーラムのバカになったら、私に間違った方法を知らせてください。

問題はいくつかの部分に分かれていますが、ここで私が最も興味深いと思う部分について提案します。ログの例がなければ、これは私ができる最善の方法です。そのため、解決策ではなく単なる提案です。

ログを解析するには、get-contentパラメータを指定して-waitを使用します。私の使用例では、エラーログでエラーを見つけるだけで十分です。

これは私自身の使用例でうまくいったものであり、フォーマットを許します:

get-content E:\temp13\log.txt -tail(1) -wait | where {$_ -match "ERROR"} |
    foreach {
        send-mailmessage `
        -port 25 `
        -smtpserver my.mail.server `
        -from [email protected] `
        -to [email protected] `
        -subject "test logmonitor" `
        -body "ERROR found: $_" `
        }

$_ -match "ERROR"の代わりに、ログIDフィールドとコンピュータ名を何らかの方法で分離する必要があります。現時点でどのように最善の方法で対処するかはわかりませんが、where-object -matchが正規表現サポートを提供しているので、それはオプションかもしれません。 $ _変数を別の新しい変数に格納することから始めて、後でパイプラインのネストされたforeachループ内などで都合のよいときにそれを取得することもできます。

コンピューター名を取得できると仮定すると、get-adcomputerコマンドレットがAD(import-module activedirectory)にクエリを送信する最も簡単な方法だと思います。メール送信エラーでしょうか。

もちろんimport-csvを使用すると、はるかにエレガントになりますが、私はそれを調整する方法を知りません(誰かがこれを読んで、その裏通りのトリックを知っている場合は、共有してください)。 。

3
ErikE

イベントIDは確実であり、DHCPログではこのIDに他のイベントは記録されないが、関心のあるイベントはないという前提の下で、プッシュは確かにオプションです。

1)サーバーマネージャーを開き、イベントビューアーのDHCPログに移動します。

2)アクションを添付する代表的なエントリを見つけます。それを選択して右クリックします。

3)「このイベントにタスクを添付」を選択します。

4)タスクの作成Wizardが開き、そこから取り除きます...

実際には明示的な電子メールオプションがありますが、それ以上のロジックが必要な場合は、当然、プログラムの開始オプションを使用してpowershell.exeを起動し、スクリプトを添付できます。ガイダンスが必要な場合に、タスクマネージャーにPowerShellスクリプトを実行させる方法については、グーグル向けの優れたハウツーがたくさんあります。

私が目にするすぐに別の方法は、スケジュールされた間隔でpowershellを使用してイベントログを解析することでプルを使用することです。 "Microsoft Scripting Guy"(別名Ed Wilson)は、Powershellのさまざまなバージョンで利用可能なコマンドレットを使用してイベントログを解析する方法に関する素晴らしいブログ投稿をいくつか書いているので、彼のブログを出発点とするのが私の提案です。

実際のコマンドレットについては、今のところ私の便利なスニペットの束を引き出す時間はありませんが、1日か2日でもう一度調べて、他の誰かが適切に選択されたものを試していない場合、またはあなたが持っている場合に貢献する可能性があります自分ですべて解決したわけではありません:-)

1
ErikE

これはあなたの望ましい解決策を扱っていませんが、あなたの目標を達成するかもしれないオプションは、新しい(以前は目に見えなかった)ホストが見られたときにarpwatchlink )を利用して通知することですネットワーク。

Windowsのarpwatchの代替案は decaffeinatid のようですが、私はこれを使用したことがないので、良い点も悪い点も言えません。

1
fukawi2