AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 550653
Accepted
TheCleaner
TheCleaner
Asked: 2013-11-05 11:50:53 +0800 CST2013-11-05 11:50:53 +0800 CST 2013-11-05 11:50:53 +0800 CST

Windows DHCP 服务器 - 当未加入 AD 的设备获得 IP 地址时收到通知

  • 772

设想

将其简化为最简单的示例:

我有一个具有 DHCP 服务器角色的 Windows 2008 R2 标准 DC​​。它通过各种 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. 在这一点上,某种脚本可能必须接管下面剩余的“步骤”。
  4. 以某种方式查询这些事件 ID 10 的这个 DHCP 日志(我很想推,但我猜拉是这里唯一的办法)
  5. 解析查询以获取分配新租约的设备名称
  6. 在 AD 中查询设备名称
  7. 如果在 AD 中找不到,请发送通知电子邮件

如果有人对如何正确执行此操作有任何想法,我将不胜感激。我不是在寻找“给我代码”,而是想知道是否有上述列表的替代品,或者我是否想清楚并且存在另一种收集此信息的方法。如果您有想要共享的代码片段/PS 命令来帮助完成此任务,那就更好了。

active-directory
  • 4 4 个回答
  • 9214 Views

4 个回答

  • Voted
  1. Best Answer
    TheCleaner
    2013-11-07T14:52:54+08:002013-11-07T14:52:54+08:00

    非常感谢 ErikE 和这里的其他人,我已经走上了一条道路……我不会说这是正确的道路,但我想出的 Powershell 脚本可以解决问题。

    如果有人想要,代码如下。只需手动运行它指向每个 DHCP 服务器或安排它(再次指向脚本中的每个 DHCP 服务器)。

    脚本的作用:

    1. 从 DHCP 服务器获取租约信息(ipv4 租约)
    2. 将租约输出到 csv 文件
    3. 读回该 CSV 文件以查询 AD
    4. 查询计算机的 AD
    5. 如果未找到输出到新的 txt 文件
    6. 从上面 #5 中创建的文件创建一个唯一的列表最终 txt 文件(因为如果客户端注册不止一次或使用多个适配器,则可能存在欺骗)
    7. 将最终输出文件的内容通过电子邮件发送给管理员

    你需要什么:

    该脚本使用 AD 模块 ( import-module activedirectory),因此最好在运行 DHCP 的 AD DC 上运行。如果您不是这种情况,您可以安装 AD powershell 模块:http: //blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory-模块在-powershell-in-windows-7.aspx

    您还需要在此处找到 Quest 的 AD Powershell cmdlet: 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
  2. ErikE
    2013-11-05T17:03:06+08:002013-11-05T17:03:06+08:00

    好的,我不确定我是否在这里遵守礼仪,但我发布了第二个答案,而不是编辑我以前的答案,因为它确实包含一些可能对某人有用的信息,即使被证明与本案无关。如果这让我在这个论坛上成为白痴,请随时告诉我我的错误方式。

    问题分为几个部分,这里有一些我觉得最有趣的建议。如果没有日志中的示例,这是我能做的最好的,所以这只是建议而不是解决方案。

    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-adcomputercmdlet 将是您查询 AD ( import-module activedirectory) 的最简单方法,并且我猜错误发送邮件?

    在你的情况下,使用import-csv当然会更加优雅,但我不知道有什么方法可以跟踪它(如果有人碰巧读到这篇文章并且知道那条小巷的诡计,请分享)。

    • 3
  3. ErikE
    2013-11-05T12:24:03+08:002013-11-05T12:24:03+08:00

    假设您确定事件 ID,并且在 DHCP 日志中没有其他事件记录到此 ID,但您感兴趣的事件,推送确实是一个选项。

    1) 打开服务器管理器,进入事件查看器中的 DHCP 日志。

    2) 找到您希望将您的操作附加到的代表条目。选择它并右键单击。

    3) 选择“将任务附加到此事件”。

    4)任务创建向导打开,把它从那里拿走......

    实际上有一个明确的电子邮件选项,但是如果您需要更多的逻辑,您当然可以自由地使用 start-a-program 选项来启动 powershell.exe 并将脚本附加到它。如果您需要指导,有很多关于如何让任务管理器运行 powershell 脚本的优秀 googleable howtos。

    我看到的直接替代方法是通过按预定时间间隔使用 powershell 解析事件日志来使用 pull。“Microsoft Scripting Guy”,又名 Ed Wilson,写了一些很棒的博客文章,介绍如何使用不同版本的 powershell 中可用的 cmdlet 解析事件日志,因此我建议以他的博客为起点。

    至于实际的 cmdlet,我现在没有时间拿出我的方便片段,但会在一两天后再次查看,如果没有其他人参与其中一些精心挑选的片段,或者你没有不能自己解决所有问题:-)

    • 1
  4. fukawi2
    2013-11-05T14:06:53+08:002013-11-05T14:06:53+08:00

    虽然这不能解决您想要的解决方案,但可以实现您的目标的一个选项是利用arpwatch( link ) 在网络上看到新的(以前看不见的)主机时通知您。

    Windows 的替代品arpwatch似乎是不含咖啡因的,但我从未使用过它,所以不能说它的好坏。

    • 1

相关问题

  • 如果以域用户身份远程登录,PC 速度极慢

  • 如何在 Windows 2003 的 ou 级别应用策略

  • 允许用户更改其 Active Directory 密码的 Web 界面

  • MOSS 2007 无法使用 ActiveDirectoryMembershipProvider 配置表单身份验证

  • 通过 VPN 更改 Active Directory 密码

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve