これを解決するために、SQLiteデータベースを作成し、そのデータベースに既存のリースに関する情報を保存し、クエリを使用して、以前に取得したテーブル(たとえば、昨日または1週間前)を現在のデータと比較します。
最初は、私はプログラマーではないことに注意してください。おそらく、ここで紹介するコードはあまり効果的ではありませんが、問題は解決します。 スクリプトは、PowerShell 3.0で初めて登場したコマンドレットを使用し、Windows Server 2012 R2で作業がテストされ、Windows Server 2008 R2はDHCPサーバーとして機能しました。
SQLiteを使用するには、System.Data.SQLite.dllとSQLite.Interop.dllの2つのライブラリが必要です。 System.Data.SQLiteの一部としてここからダウンロードできます。 非バンドルバージョンが必要です。 フレームワークバージョン4.5がコンピューターにインストールされているため、sqlite-netFx45-setup-x64-2012-1.0.96.0.exeを選択しました
ライブラリを接続します。この場合、dllはスクリプトと同じフォルダーにあります。
# Adding SQLite libraries, should be two files in script directory: System.Data.SQLite.dll, SQLite.Interop.dll Add-Type -Path "$PSScriptRoot\System.Data.SQLite.dll" -ErrorAction Stop
便宜上、既存のメソッドをスクリプト関数でラップします。
# Function to execute command non query function ExecuteNonQuery($commandtext) { $SQLiteCommand.CommandText = $commandtext [void]$SQLiteCommand.ExecuteNonQuery() } # Function to execute command query function ExecuteReader($commandtext) { $SQLiteCommand.CommandText = $commandtext $DataReader = $SQLiteCommand.ExecuteReader() $Data = New-Object -TypeName System.Data.DataTable $Data.Load($DataReader) $DataReader.Close() return $Data }
現在のリースについてDHCPサーバーをポーリングします。 -IncludeReservationsパラメーターが起動時に指定されなかった場合、予約をスキップします。
# Getting DHCP scopes and leases from DHCP server, excludes reserved addresses if -IncludeReservations not specified as parameter $Scopes = Get-DhcpServerv4Scope -ComputerName $ComputerName if ($IncludeReservations) { $Leases = foreach ($Scope in $Scopes) { Get-DhcpServerv4Lease -ScopeId $Scope.ScopeId -ComputerName $ComputerName } } else { $Leases = foreach ($Scope in $Scopes) { Get-DhcpServerv4Lease -ScopeId $Scope.ScopeId -ComputerName $ComputerName | where AddressState -notlike "*Reservation" } }
データベースを作成するか、既存のデータベースとの接続を確立し、SQLite.SQLiteCommandオブジェクトのインスタンスも作成します。
# Creating and opening SQLite DB connection # If DB file does not exist, create a new file. DB file name is "<name_of_server>.sqlite" $SQLiteConnection = New-Object -TypeName System.Data.SQLite.SQLiteConnection $DBFile = "$DBDirectory\$ComputerName.sqlite" if (Test-Path $DBFile) { Write-Verbose "Opening $DBFile" $SQLiteConnection.ConnectionString = "Data Source=$DBFile;" } else { Write-Verbose "Creating $DBFile" $SQLiteConnection.ConnectionString = "Data Source=$DBFile;New=True;" } $SQLiteConnection.Open() # Create SQLite command object using previously created DB connection $SQLiteCommand = New-Object -TypeName System.Data.SQLite.SQLiteCommand $SQLiteCommand.Connection = $SQLiteConnection
クエリを使用して「今日の」テーブルを作成します。
CREATE TABLE $CurrentTableName (IPAddress PRIMARY KEY, ScopeId, AddressState, ClientId, HostName);
サーバーから受け取った値を入力します。 以前に定義されたstr()関数は、nullではなく空の文字列を返します。
INSERT OR IGNORE INTO $CurrentTableName VALUES('$(str($Lease.IPAddress))', '$(str($Lease.ScopeId))', '$(str($Lease.AddressState))', '$(str($Lease.ClientId))', '$(str($Lease.HostName))');
最後の2つを名前(同じ日付)のテーブルで選択して、内容を比較します。
SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name DESC LIMIT 2;
そして比較してください:
SELECT * FROM $LastTableName WHERE $LastTableName.ClientId NOT IN (SELECT ClientId FROM $PrevTableName);
結果をコンソールに出力します。
$Result | ft -Wrap -AutoSize
出力テーブルの例:
IPAddress ScopeId AddressState ClientId HostName --------- ------- ------------ -------- -------- 10.10.10.22 10.10.10.0 Active a5-b4-c3-d2-e1-f0 UsersOwnDevice.domain
データベースへの接続を閉じて、オブジェクトを破棄します。
# Closing and disposing SQLite objects $SQLiteCommand.Dispose() $SQLiteConnection.Close() $SQLiteConnection.Dispose()
たとえば、結果をメールに送信し、スクリプト自体をスケジュールに従って実行できます。
完全なスクリプト
[CmdletBinding()] Param( # Name of DHCP server [Parameter(Mandatory=$True)][string]$ComputerName, # Directory of SQLite database file [Parameter(Mandatory=$True)][string]$DBDirectory, # Include reserved addresses in query [switch]$IncludeReservations ) BEGIN { # Adding SQLite libraries, should be two files in script directory: System.Data.SQLite.dll, SQLite.Interop.dll Add-Type -Path "$PSScriptRoot\System.Data.SQLite.dll" -ErrorAction Stop # Function to execute command non query function ExecuteNonQuery($commandtext) { $SQLiteCommand.CommandText = $commandtext [void]$SQLiteCommand.ExecuteNonQuery() } # Function to execute command query function ExecuteReader($commandtext) { $SQLiteCommand.CommandText = $commandtext $DataReader = $SQLiteCommand.ExecuteReader() $Data = New-Object -TypeName System.Data.DataTable $Data.Load($DataReader) $DataReader.Close() return $Data } # Converting value to string, returns empty string from null value function str($value) { if ($value -ne $null) { return $value.ToString() } else { return [string]::Empty } } # Getting DHCP scopes and leases from DHCP server, excludes reserved addresses if -IncludeReservations not specified as parameter Write-Verbose "Getting leases from $ComputerName" $Scopes = Get-DhcpServerv4Scope -ComputerName $ComputerName if ($IncludeReservations) { $Leases = foreach ($Scope in $Scopes) { Get-DhcpServerv4Lease -ScopeId $Scope.ScopeId -ComputerName $ComputerName } } else { $Leases = foreach ($Scope in $Scopes) { Get-DhcpServerv4Lease -ScopeId $Scope.ScopeId -ComputerName $ComputerName | where AddressState -notlike "*Reservation" } } # Creating and opening SQLite DB connection # If DB file does not exist, create a new file. DB file name is "<name_of_server>.sqlite" $SQLiteConnection = New-Object -TypeName System.Data.SQLite.SQLiteConnection $DBFile = "$DBDirectory\$ComputerName.sqlite" if (Test-Path $DBFile) { Write-Verbose "Opening $DBFile" $SQLiteConnection.ConnectionString = "Data Source=$DBFile;" } else { Write-Verbose "Creating $DBFile" $SQLiteConnection.ConnectionString = "Data Source=$DBFile;New=True;" } $SQLiteConnection.Open() # Create SQLite command object using previously created DB connection $SQLiteCommand = New-Object -TypeName System.Data.SQLite.SQLiteCommand $SQLiteCommand.Connection = $SQLiteConnection } PROCESS { # Getting current date and choosing the name of table for current date $CurrentTableName = "_$((Get-Date -Format "yyyyMMdd").ToString())" # Check if table for current date exists. If not, create a new one if ($CurrentTableName -ne (ExecuteReader("SELECT name FROM sqlite_master WHERE type = 'table' AND name = '$CurrentTableName';") | select -ExpandProperty name)) { Write-Verbose "Creating table $CurrentTableName" ExecuteNonQuery("CREATE TABLE $CurrentTableName (IPAddress PRIMARY KEY, ScopeId, AddressState, ClientId, HostName);") } else { Write-Verbose "Table $CurrentTableName exists" } # Update the current date table with collected leases Write-Verbose "Updating table $CurrentTableName" foreach ($Lease in $Leases) { ExecuteNonQuery("INSERT OR IGNORE INTO $CurrentTableName VALUES('$(str($Lease.IPAddress))', '$(str($Lease.ScopeId))', '$(str($Lease.AddressState))', '$(str($Lease.ClientId))', '$(str($Lease.HostName))');") } # Selecting last two tables (by date) from database to compare and check difference # Compare tables and print the difference between them. New leases from previous date to current date will be printed $TablesToCompare = ExecuteReader("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name DESC LIMIT 2;") if ($TablesToCompare.Count -gt 1) { Write-Verbose "Going to compare" $LastTableName = $TablesToCompare[0] | select -ExpandProperty name $PrevTableName = $TablesToCompare[1] | select -ExpandProperty name $Result = ExecuteReader("SELECT * FROM $LastTableName WHERE $LastTableName.ClientId NOT IN (SELECT ClientId FROM $PrevTableName);") write "`r`nNew leases between $($PrevTableName.Trim("_")) and $($LastTableName.Trim("_"))" $Result | ft -Wrap -AutoSize } else { write "No new leases" Write-Verbose "Not enough data to compare. First run?" } } END { # Closing and disposing SQLite objects $SQLiteCommand.Dispose() $SQLiteConnection.Close() $SQLiteConnection.Dispose() }
実行例:patrol.ps1 -ComputerName mydhcp.domain -DBDirectory D:\ Temp