AD和DHCP数据整合——(一)导出dhcp数据到text文件

2011年3月22日 | 分类: 脚本 | 标签: , , , ,

目前绝大多数的企业内部都会采用域环境进行用户及电脑管理,这里以我自身的实际工作经验为例介绍一下将AD和DHCP数据整合的思路和步骤,本文先介绍一下如何将dhcp数据导出到文件,我介绍的方法其实只是我能想到或能查到的方法,并不一定是最好的方法,所以欢迎交流和指教。大多数做dhcp维护或域管理的人可能都知道用netsh命令可以进行dhcp维护,但我这里要用PowerShell来进行导出,因为用它可以实现我想要的规矩的输出格式。针对使用PowerShell导出dhcp数据,我在网上只找到了两个完整可用的脚本,一个是http://poshcode.org/1477 下的List DHCP Clients, 一个是http://gallery.technet.microsoft.com/ScriptCenter/eaabc935-da22-4a03-967a-f47d8f895e22/view/description 下的Gather Information for all clients registered in DHCP scopes,其中第二个脚本就是牛人根据第一个脚本改的。第一个脚本只会返回一个vlan下的5条记录,而第二个脚本在vlan多的情况下也不能显示所有vlan下的client信息,而我在实际使用中又只需要一部分vlan下的client信息,所以我根据第二个人的脚本做了一点点修改,代码如下文,注意标红字体需要根据实际情况进行修改, $DHCPServerIP要设置成dhcp服务器的IP地址, $scopeArray为要查询的vlan数组,这里注意要将vlan网络地址转换成十进制数据,如vlan是10.6.29.0,则对应的十进制是10*256*256*256+6*256*256+29*256+0=168172800,原因请参见http://msdn.microsoft.com/en-us/library/aa363381(v=VS.85).aspx,将以下文本保存成.ps1文件,并以带有dhcp管理员权限的用户运行,则能导出所要vlan的clients信息到文本文件。具体代码:

$DHCP_EnumSubnets = @’         
    [DllImport("dhcpsapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern uint DhcpEnumSubnets(
        string ServerIpAddress,
        ref uint ResumeHandle,
        uint PreferredMaximum,
        out IntPtr EnumInfo,
        ref uint ElementsRead,
        ref uint ElementsTotal
    );
‘@

$DHCP_EnumSubnetClients = @’         
   [DllImport("dhcpsapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern uint DhcpEnumSubnetClients(
       string ServerIpAddress,
       uint SubnetAddress,
       ref uint ResumeHandle,
       uint PreferredMaximum,
       out IntPtr ClientInfo,
       ref uint ElementsRead,
       ref uint ElementsTotal);
‘@
 
$DHCP_Structs = @’
namespace mystruct {
    using System;
    using System.Runtime.InteropServices;

   public struct CUSTOM_CLIENT_INFO
   {
       public string ClientName;
       public string IpAddress;
       public string MacAddress;
   }
 
   [StructLayout(LayoutKind.Sequential)]
   public struct DHCP_CLIENT_INFO_ARRAY
   {
       public uint NumElements;
       public IntPtr Clients;
   }
 
   [StructLayout(LayoutKind.Sequential)]
   public struct DHCP_CLIENT_UID
   {
       public uint DataLength;
       public IntPtr Data;
   }
  
    [StructLayout(LayoutKind.Sequential)]
    public struct DHCP_IP_ARRAY
    {
        public uint NumElements;
        public IntPtr IPAddresses;
    }
       
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class DHCP_IP_ADDRESS
    {
        public UInt32 IPAddress;
    }

   [StructLayout(LayoutKind.Sequential)]
   public struct DATE_TIME
   {
       public uint dwLowDateTime;
       public uint dwHighDateTime;
  
       public DateTime Convert()
       {
           if (dwHighDateTime== 0 && dwLowDateTime == 0)
           {
           return DateTime.MinValue;
           }
           if (dwHighDateTime == int.MaxValue && dwLowDateTime == UInt32.MaxValue)
           {
           return DateTime.MaxValue;
           }
           return DateTime.FromFileTime((((long) dwHighDateTime) << 32) | (UInt32) dwLowDateTime);
       }
   }
  
   [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
   public struct DHCP_HOST_INFO
   {
       public uint IpAddress;
       public string NetBiosName;
       public string HostName;
   }
 
   [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
   public struct DHCP_CLIENT_INFO
   {
       public uint ClientIpAddress;
       public uint SubnetMask;
       public DHCP_CLIENT_UID ClientHardwareAddress;
       [MarshalAs(UnmanagedType.LPWStr)]
       public string ClientName;
       [MarshalAs(UnmanagedType.LPWStr)]
       public string ClientComment;
       public DATE_TIME ClientLeaseExpires;
       public DHCP_HOST_INFO OwnerHost;
   }
}
‘@

function uIntToIP {
    param ($intIP)
    $objIP = new-object system.net.ipaddress($intIP)
    $arrIP = $objIP.IPAddressToString.split(“.”)
    return $arrIP[3] + “.” + $arrIP[2] + “.” + $arrIP[1] + “.” + $arrIP[0]
}

$resumeHandle = 0
$clientInfo = 0
$subnetInfo = 0
$subnetElementsRead = 0
$subnetElementsTotal = 0
$clientElementsRead = 0
$clientElementsTotal = 0
 
Add-Type $DHCP_Structs
Add-Type  -MemberDefinition $DHCP_EnumSubnetClients -Name GetDHCPInfo -Namespace Win32DHCP
Add-Type  -MemberDefinition $DHCP_EnumSubnets -Name GetDHCPSubnets -Namespace Win32DHCP

# Add your DHCP servers to array below
#$DHCPServers = @(“10.6.1.5″)

$outputstr = “ClientIP” + “ ” + “SubnetMask” + “ ” + “MacAddress” + “ ” + “ComputerName”
#for ($k=0;$k -lt $DHCPServers.Count;$k++)
#{

    $DHCPServerIP = “10.6.1.5″
    $resumeHandle = 0

    # Generate List of Subnets defined in DHCP Scopes   
    [void][Win32DHCP.GetDHCPSubnets]::DhcpEnumSubnets($DHCPServerIP,[ref]$resumeHandle,65536,[ref]$subnetInfo,[ref]$subnetElementsRead,[ref]$subnetElementsTotal)
    $subnets = [system.runtime.interopservices.marshal]::PtrToStructure($subnetInfo,[mystruct.DHCP_IP_ARRAY])

    [int]$subSize = $subnets.NumElements
    $outArray = $subnets.IPAddresses
    $sub_ptr_array = new-object mystruct.DHCP_IP_ADDRESS[]($subSize)
    [IntPtr]$subCurrent = $outArray
    for ($i = 0; $i -lt $subSize; $i++)
    {
        $sub_ptr_array[$i] = new-object -TypeName mystruct.DHCP_IP_ADDRESS
        [system.runtime.interopservices.marshal]::PtrToStructure($subCurrent, $sub_ptr_array[$i])
        $subCurrent = [IntPtr]([int]$subCurrent + [system.runtime.interopservices.marshal]::SizeOf([system.IntPtr]));
        #echo “$(uIntToIP($sub_ptr_array[$i].IPAddress))”
    }

    #Customize the subnet scope and subSize, edited by Nile 2010.9.22
    $scopeArray=168172800,168173312,168173568,168173824

    # Iterate through subnets to gather clients registered in each scope
    for ($j=0;$j -lt $scopeArray.length;$j++)
    {
        $resumeHandle = 0
       
        # Generate list of clients registered in subnet
        [void][Win32DHCP.GetDHCPInfo]::DhcpEnumSubnetClients($DHCPServerIP,$scopeArray[$j],[ref]$resumeHandle,65536,[ref]$clientInfo,[ref]$clientElementsRead,[ref]$clientElementsTotal)
        $clients = [system.runtime.interopservices.marshal]::PtrToStructure($clientInfo,[mystruct.DHCP_CLIENT_INFO_ARRAY])
        [int]$size = $clients.NumElements
        [int]$current = $clients.Clients
        $ptr_array = new-object system.intptr[]($size)
        $current = new-object system.intptr($current)

        for ($i=0;$i -lt $size;$i++)
        {
            $ptr_array[$i] = [system.runtime.interopservices.marshal]::ReadIntPtr($current)
            $current = $current + [system.runtime.interopservices.marshal]::SizeOf([system.IntPtr])
        }

        [array]$clients_array = new-object mystruct.CUSTOM_CLIENT_INFO
       
        #Gather client info
        for ($i=0;$i -lt $size;$i++)
        {
            $objDHCPInfo = New-Object psobject
            $current_element = [system.runtime.interopservices.marshal]::PtrToStructure($ptr_array[$i],[mystruct.DHCP_CLIENT_INFO])
            add-member -inputobject $objDHCPInfo -memberType noteproperty -name ClientIP -value $(uIntToIP $current_element.ClientIpAddress)
            add-member -inputobject $objDHCPInfo -memberType noteproperty -name ClientName -value $current_element.ClientName
            add-member -inputobject $objDHCPInfo -memberType noteproperty -name OwnerIP -value $(uIntToIP $current_element.Ownerhost.IpAddress)
            add-member -inputobject $objDHCPInfo -memberType noteproperty -name OwnerName -value $current_element.Ownerhost.NetBiosName
            add-member -inputobject $objDHCPInfo -memberType noteproperty -name SubnetMask -value $(uIntToIP $current_element.SubnetMask)
            add-member -inputobject $objDHCPInfo -memberType noteproperty -name LeaseExpires -value $current_element.ClientLeaseExpires.Convert()
            
            $mac = [System.String]::Format(
                “{0:x2}-{1:x2}-{2:x2}-{3:x2}-{4:x2}-{5:x2}”,
                [system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data),
                [system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 1),
                [system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 2),
                [system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 3),
                [system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 4),
                [system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 5)
            )
            
            add-member -inputobject $objDHCPInfo -memberType noteproperty -name MacAddress -value $mac
           
            # Output client data
            #$objDHCPInfo
            # Below line can be used to output data to CSV file when uncommented
            # “$($objDHCPInfo.ClientIP) $($objDHCPInfo.MacAddress) $($objDHCPInfo.ClientName)” | out-file “dhcpreport3.txt” -Encoding ASCII -Append
     $outputstr +=”`n” + “$($objDHCPInfo.ClientIP)” +” ”+ “$($objDHCPInfo.SubnetMask)” + “ ” + “$($objDHCPInfo.MacAddress)” +” ” + “$($objDHCPInfo.ClientName)”
        }
    } 
#}
$outputstr| out-file “dhcpclients.txt” -Encoding ASCII

Written by Nile Jiang
无猖狂以自彰,当阴沉以自深。

  1. Andy.deng
    2016年3月28日11:32

    我在运行此命令的时候,报以下错误:
    表达式或语句中出现意外标记“$(”
    所在位置 c:\1.ps1:150 字符:18
    + #echo “$(<<<<uinttoip($sub_ptr_array[$i].ipaddress))"

    不知怎么回事,我目前环境是手动建立了两个不同区域网段。

  2. 2016年3月28日13:34

    @Andy.deng
    请看一下是不是括号(或双引号”变成中文标点符号了。

    另外,现在应该不用这么复杂的PowerShell脚本了,在Windows Server 2012下,使用自带的PowerShell命令即可轻松的导出DHCP数据,比如:

    Get-DhcpServerv4Lease -ScopeId 192.168.1.0 -AllLeases | Export-Csv -Encoding utf8 -Path d:\dhcp.csv -NoTypeInformation

    所以可以先指定或Get出想要导出的ScopeID,再进行导出就可以了。

    具体命令可参考:https://technet.microsoft.com/en-us/library/jj590737(v=wps.630).aspx

  3. Andy.deng
    2016年3月28日15:37

    HI Nile,
    多谢您的回复,我把括号和“号全部改了还是报同样的错误。
    我现在的环境是windows2008R2,不知是怎么回事?您那边运行此命令没有任何错误吗?
    期待您的回复。

  4. 2016年4月20日11:01

    Hi Andy,

    我把脚本邮件回复给你了,你试一下,至少不会有中文符号的问题,另外建议测试确认一下2008下是否有我说的命令。

    Nile

注意: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。使用'@all ',将会将评论发送给之前所有其它评论者。请务必注意user必须和评论者名相匹配(大小写一致)。