none
PowerShell - пользователь-невидимка в AD RRS feed

  • Вопрос

  • Всем привет!

    Уже лет 5 использую свои WSH-скрипты для администрирования AD. Например, есть скрипт получения (нужной мне) информации о пользователе.

    Выглядит он примерно так (весь код из-за объема приводить не буду, тем более, что когда то я его сюда кидал):

    ///////////////////////////////////////////////
    // Создание ADO-подключения		    //
    ///////////////////////////////////////////////
    var objConnection = WScript.CreateObject("ADODB.Connection");	//объект ADO-соединения
    var objCommand = WScript.CreateObject("ADODB.Command");	//объект запроса
    objConnection.Provider = "ADsDSOObject";
    objConnection.Open("Active Directory Provider");
    objCommand.ActiveConnection = objConnection;
    objCommand.Properties("Page Size")=1000;
    ////////////////////////////////////////////////////////////////////////
    // Выбор всех имен пользователей из AD c обработкой ошибок	   //
    ////////////////////////////////////////////////////////////////////////
    try
    {
    	objCommand.CommandText = "SELECT distinguishedName from 'LDAP://DC=local,DC=domain' Where objectClass='user' and objectClass<>'computer'";
    	objADCRes = objCommand.Execute;
    }
    catch(e)
    {
    	WScript.Echo("ADODB error. " + e.description);
    	WScript.Quit(1);
    }
    var IsUserFound = false;	//Флаг "Пользователь найден/не найден"
    objADCRes.MoveFirst;	//Переход к первому элементу коллекции выборки учетных записей пользователей
    while(!objADCRes.EOF)	//Цикл перебора всех учетных записей пользователей
    {
    	//Получение LDAP-объекта пользователя
    	objLDAPUser = GetObject("LDAP://" + objADCRes.Fields("distinguishedName").Value);
    	//WScript.Echo("LDAP://" + objADCRes.Fields("distinguishedName").Value);
    	
     //Поиск заданного имени в составе свойства displayName и sAMAccountName
     .............
     .............
     GetUserInfo();
    }
    ////////////////////////////////////////////////////////
    //  	Функция считывания параметров пользователя	 //
    ////////////////////////////////////////////////////////
    function GetUserInfo()
    {
     ............
     ............
    }
    
    
    Поскольку сейчас активно повышаю свои скилзы в PowerShell, решил переписать данный скрипт на PS. Получилось примерно следующее:
    #Определение параметров скрипта
    param ($ln, $dn)
    
    # Функция получения и вывода информации о пользователе #
    function GetUserInfo
    {
      .....
      .....
    }
    
    #Считывание параметров Active Directory             
    $AD=[ADSI]'LDAP://dc=local,dc=domain'
    $ADSearch = New-Object System.DirectoryServices.DirectorySearcher($AD)
    $ADSearch.Filter = "(&(objectClass=user)(!(objectClass=computer)))"
    $SearchResult = $ADSearch.FindAll()
    $IsUserFound = $false
    
    #Анализ входных параметров скрипта
    # 1.Все параметры нулевые
    if(($ln -eq $null) -and ($dn -eq $null))
    {
    	Write-Host "Необходимо ввести login name пользователя."
    	Write-Host "Формат коммандной строки: .\userinfo2.ps1 [-ln] LoginName [-dn DisplayName]"
    }
    # 2.Указано login name пользователя
    elseif($ln -ne $null)
    {
    	$SearchResult | ForEach {
    		$ObjectConnectionString = ($_.Path).ToString()
    		$ObjectConnection = [adsi]$ObjectConnectionString
    		$MatchResult = $ObjectConnection.sAMAccountName -match $ln
    		if($MatchResult -ne "")
    		{
    			$IsUserFound = $true 
    			GetUserinfo 
    		}
    	}
    	if($IsUserFound -eq $false)
    	{
    		Write-Host "Пользователь " $ln " отсутствует"	
    	}
    }
    # 3.Указано display name пользователя
    elseif(($ln -eq $null) -and ($dn -ne $null))
    {
    	$SearchResult | ForEach {
    		$ObjectConnectionString = ($_.Path).ToString()
    		$ObjectConnection = [adsi]$ObjectConnectionString
    		$MatchResult = $ObjectConnection.displayName -match $dn
    		if($MatchResult -ne "")
    		{
    			$IsUserFound = $true 
    			GetUserinfo 
    		}
    	}
    	if($IsUserFound -eq $false)
    	{
    		Write-Host "Пользователь " $dn " отсутствует"	
    	}
    }
    
    PoSH-сркипт вроде работает как надо, но сегодня наткнулся на одного пользователя которого он упорно не видит - выдает, что пользователь не найден, хотя пользователь на первый взгляд ничем не отличается от других, есть в AD и WSH-скрипт его прекрасно находит. Может апологеты PowerShell подскажут в чем может быть проблема (предполагаю, что в регулярных выражениях)?
    Пока такое заметил только на одном пользователе. На всех остальных результат у PS и WSH одинаковый.
    Andrew Mishechkin
    2 ноября 2010 г. 12:04

Ответы

  • Так и есть, господа.
    Видимо у меня специфичный AD (возможно что он претерпел уже столько апгрейдов NT4->2000->2003->2008)

    Переписал скрипт с использованием ADO-подключения к AD - все заработало. И работает, кстати, быстрее чем с традиционным ADSearcher.
    Привожу сам скрипт (может кому-нить пригодится):

    #Определение параметров скрипта
    param ($ln, $dn)
    
    ########################################################
    # Функция получения и вывода информации о пользователе #
    ########################################################
    function GetUserInfo
    {
    	Write-Host
    	Write-Host "-----------------------------------------------------------"
    	Write-Host "Имя вохода пользователя: " $ObjectConnection.sAMAccountName
    	Write-Host "Отображаемое имя пользователя: " $ObjectConnection.displayName
    	Write-Host "Описание: " $ObjectConnection.Description
    	Write-Host "Относительное LDAP-имя: " $ObjectConnection.distinguishedName
    	Write-Host "Сценарий входа: " $ObjectConnection.scriptPath
    	Write-Host "Дирекция: " $ObjectConnection.department
    	Write-Host "Должность: " $ObjectConnection.title
    	Write-Host "Телефон: " $ObjectConnection.telephoneNumber
    	Write-Host "E-mail: " $ObjectConnection.mail
    	Write-Host "Предприятие: " $ObjectConnection.company
    	Write-Host "Время создания учетной записи: " $ObjectConnection.whenCreated
    	if($ObjectConnection.IsAccountLocked -eq 1)
    	{
    		Write-Host "Учетная запись заблокирована"
    	}
    	elseif($ObjectConnection.AccountDisabled -eq 1)
    	{
    		Write-Host "Учетная запись отключена"
    	}
    	Write-Host "Время отключения учетной записи: " $ObjectConnection.accountExpirationdate
    	$strWQL = "SELECT SID FROM Win32_UserAccount WHERE Domain='DOMAIN' AND Name='" + $ObjectConnection.sAMAccountName + "'" 
    	$WMIUser = get-wmiobject -query $strWQL
    	Write-Host "SID: " $WMIUser.SID
    	Write-Host "----------------------------------------"
    	Write-Host "Пользователь входит в следующие группы: "
    	Write-Host
    	$enumGroups = $ObjectConnection.memberOf
    	$enumGroups | ForEach-Object {
    	  $IsDescError = $false
    		$groupcn = ([ADSI]"LDAP://$_").Get("CN")
    		try
    		{
    			$groupdesc = ([ADSI]"LDAP://$_").Get("description")
    		}
    		catch
    		{
    			$IsDescError = $true
    		}
    		Write-Host "Имя: " $groupcn
    		if($IsDescError -eq $false)
    		{
    			Write-Host "Описание: " $groupdesc
    		}
    		else
    		{
    			Write-Host "Описание отсутствует"
    		}
    		Write-Host 
    	}	
    	Write-Host
    }
    
    $IsUserFound = $false
    
    #Cоздание ADO-подключений к AD
    $objConnection = New-Object -ComObject ADODB.Connection
    $objCommand = New-Object -ComObject ADODB.Command
    $objConnection.Provider = "ADsDSOObject"
    $objConnection.Open("Active Directory Provider")
    $objCommand.ActiveConnection = $objConnection
    $objCommand.Properties.Item("Page Size").Value = 1000
    $objCommand.CommandText = "SELECT distinguishedName from 'LDAP://DC=local,DC=domain' Where objectClass='user' and objectClass<>'computer'"
    $objADCRes = $objCommand.Execute()
    $objADCRes.MoveFirst()
    #Анализ входных параметров скрипта
    # 1.Все параметры нулевые
    if(($ln -eq $null) -and ($dn -eq $null))
    {
    	Write-Host "Необходимо ввести login name пользователя."
    	Write-Host "Формат коммандной строки: .\userinfo.ps1 [-ln] LoginName [-dn DisplayName]"
    }
    # 2.Указано login name пользователя
    elseif($ln -ne $null)
    {
    	$strMatch = "*" + $ln + "*"
    	while($objADCRes.EOF -ne $true)
    	{
    		$ObjectConnectionString = "LDAP://" + ($objADCRes.Fields.Item("distinguishedName").Value).ToString()
    		$ObjectConnection = [adsi]$ObjectConnectionString
    		$MatchResult = $ObjectConnection.sAMAccountName -like $strMatch
    		if($MatchResult -ne $null)
    		{
    			$IsUserFound = $true 
    			GetUserinfo 
    		}
    		$objADCRes.MoveNext()
    	}
    	if($IsUserFound -eq $false)
    	{
    		Write-Host "Пользователь " $ln " не найден"	
    	}
    }
    # 3.Указано display name пользователя
    elseif(($ln -eq $null) -and ($dn -ne $null))
    {
    	$strMatch = "*" + $dn + "*"
    	while($objADCRes.EOF -ne $true)
    	{
    		$ObjectConnectionString = "LDAP://" + ($objADCRes.Fields.Item("distinguishedName").Value).ToString()
    		$ObjectConnection = [adsi]$ObjectConnectionString
    		$MatchResult = $ObjectConnection.displayName -like $strMatch
    		if($MatchResult -ne $null)
    		{
    			$IsUserFound = $true 
    			GetUserinfo 
    		}
    		$objADCRes.MoveNext()
    	}
    	if($IsUserFound -eq $false)
    	{
    		Write-Host "Пользователь " $dn " не найден"	
    	}
    }
    

    Andrew Mishechkin
    • Помечено в качестве ответа Andy Mishechkin 18 ноября 2010 г. 6:26
    18 ноября 2010 г. 6:26

Все ответы

  • Укажите имя и DN проблемного пользователя. Секретные фрагменты можно на что то заменить, интересуют лишь необычные символы :)

    А вообще, перед тем как использовать имя пользователя в качестве регулярного выражения, надо хотя бы замаскировать символы которые считаются управляющими для регекспов:
    PS C:\> $username = "А. И. Иванов-Петров (Сидоров)"
    PS C:\> "А. И. Иванов-Петров (Сидоров)" -match $username
    False
    PS C:\> $username = [regex]::Escape($username)
    PS C:\> $username
    А\.\ И\.\ Иванов-Петров\ \(Сидоров\)
    PS C:\> "А. И. Иванов-Петров (Сидоров)" -match $username
    True

    А в вашем случаее, регулярные выражения вообще не нужны, и можно обойтись простым -like:

    $MatchResult = $ObjectConnection.sAMAccountName -like "*$ln*"


    AKA Xaegr, MCSE: Security, Messaging; MCITP: Server\Enterprise Administrator; Блог: http://xaegr.wordpress.com
    • Предложено в качестве ответа Vasily GusevModerator 2 ноября 2010 г. 13:22
    2 ноября 2010 г. 13:21
    Модератор
  • Я заменил match на like - результат тот же.


    Нет никаких необчных символов
    ln: grishinaa dn: Гришин Андрей Андреевич

    Еще один момент: когда задаю явно имя в скрипте таким образом:

    $ADSearch.Filter = "(&(objectClass=user)(displayName=$name)(!(objectClass=computer)))" 
    $SearchResult = $ADSearch.FindAll()
    $SearchResult | ForEach {
    	$ObjectConnectionString = ($_.Path).ToString()
    	$ObjectConnection = [adsi]$ObjectConnectionString 
    	GetUserInfo
    }
    

    все работает. Пользователь прекрасно находится по displayName с пробелами внутри его.


    Andrew Mishechkin
    3 ноября 2010 г. 11:36
  • Добавьте такую строчку перед сравнением, и покажите вывод для этой учетки:

    write-host "DisplayName='$($ObjectConnection.displayName)', dn='$dn'"


    AKA Xaegr, MCSE: Security, Messaging; MCITP: Server\Enterprise Administrator; Блог: http://xaegr.wordpress.com
    8 ноября 2010 г. 5:23
    Модератор
  • По ходу проблема с провайдером ADSI LDAP://.... и этой учеткой. Когда перебираешь весь список пользователей - ее там нет.
    Кстати, это не единственная трабла. Почему-то в свойствах учетной записи не отображаются некоторые стандартные группы типа Domain Users. Я эту проблему заметил еще в скриптах WSH:

    http://social.technet.microsoft.com/Forums/ru-RU/scrlangru/thread/0487b4de-07e5-49b0-9d6a-58b8803f8072

    Поэтому в кое-каких скриптах использовал WinNT://


    Andrew Mishechkin
    9 ноября 2010 г. 7:46
  • Так и есть, господа.
    Видимо у меня специфичный AD (возможно что он претерпел уже столько апгрейдов NT4->2000->2003->2008)

    Переписал скрипт с использованием ADO-подключения к AD - все заработало. И работает, кстати, быстрее чем с традиционным ADSearcher.
    Привожу сам скрипт (может кому-нить пригодится):

    #Определение параметров скрипта
    param ($ln, $dn)
    
    ########################################################
    # Функция получения и вывода информации о пользователе #
    ########################################################
    function GetUserInfo
    {
    	Write-Host
    	Write-Host "-----------------------------------------------------------"
    	Write-Host "Имя вохода пользователя: " $ObjectConnection.sAMAccountName
    	Write-Host "Отображаемое имя пользователя: " $ObjectConnection.displayName
    	Write-Host "Описание: " $ObjectConnection.Description
    	Write-Host "Относительное LDAP-имя: " $ObjectConnection.distinguishedName
    	Write-Host "Сценарий входа: " $ObjectConnection.scriptPath
    	Write-Host "Дирекция: " $ObjectConnection.department
    	Write-Host "Должность: " $ObjectConnection.title
    	Write-Host "Телефон: " $ObjectConnection.telephoneNumber
    	Write-Host "E-mail: " $ObjectConnection.mail
    	Write-Host "Предприятие: " $ObjectConnection.company
    	Write-Host "Время создания учетной записи: " $ObjectConnection.whenCreated
    	if($ObjectConnection.IsAccountLocked -eq 1)
    	{
    		Write-Host "Учетная запись заблокирована"
    	}
    	elseif($ObjectConnection.AccountDisabled -eq 1)
    	{
    		Write-Host "Учетная запись отключена"
    	}
    	Write-Host "Время отключения учетной записи: " $ObjectConnection.accountExpirationdate
    	$strWQL = "SELECT SID FROM Win32_UserAccount WHERE Domain='DOMAIN' AND Name='" + $ObjectConnection.sAMAccountName + "'" 
    	$WMIUser = get-wmiobject -query $strWQL
    	Write-Host "SID: " $WMIUser.SID
    	Write-Host "----------------------------------------"
    	Write-Host "Пользователь входит в следующие группы: "
    	Write-Host
    	$enumGroups = $ObjectConnection.memberOf
    	$enumGroups | ForEach-Object {
    	  $IsDescError = $false
    		$groupcn = ([ADSI]"LDAP://$_").Get("CN")
    		try
    		{
    			$groupdesc = ([ADSI]"LDAP://$_").Get("description")
    		}
    		catch
    		{
    			$IsDescError = $true
    		}
    		Write-Host "Имя: " $groupcn
    		if($IsDescError -eq $false)
    		{
    			Write-Host "Описание: " $groupdesc
    		}
    		else
    		{
    			Write-Host "Описание отсутствует"
    		}
    		Write-Host 
    	}	
    	Write-Host
    }
    
    $IsUserFound = $false
    
    #Cоздание ADO-подключений к AD
    $objConnection = New-Object -ComObject ADODB.Connection
    $objCommand = New-Object -ComObject ADODB.Command
    $objConnection.Provider = "ADsDSOObject"
    $objConnection.Open("Active Directory Provider")
    $objCommand.ActiveConnection = $objConnection
    $objCommand.Properties.Item("Page Size").Value = 1000
    $objCommand.CommandText = "SELECT distinguishedName from 'LDAP://DC=local,DC=domain' Where objectClass='user' and objectClass<>'computer'"
    $objADCRes = $objCommand.Execute()
    $objADCRes.MoveFirst()
    #Анализ входных параметров скрипта
    # 1.Все параметры нулевые
    if(($ln -eq $null) -and ($dn -eq $null))
    {
    	Write-Host "Необходимо ввести login name пользователя."
    	Write-Host "Формат коммандной строки: .\userinfo.ps1 [-ln] LoginName [-dn DisplayName]"
    }
    # 2.Указано login name пользователя
    elseif($ln -ne $null)
    {
    	$strMatch = "*" + $ln + "*"
    	while($objADCRes.EOF -ne $true)
    	{
    		$ObjectConnectionString = "LDAP://" + ($objADCRes.Fields.Item("distinguishedName").Value).ToString()
    		$ObjectConnection = [adsi]$ObjectConnectionString
    		$MatchResult = $ObjectConnection.sAMAccountName -like $strMatch
    		if($MatchResult -ne $null)
    		{
    			$IsUserFound = $true 
    			GetUserinfo 
    		}
    		$objADCRes.MoveNext()
    	}
    	if($IsUserFound -eq $false)
    	{
    		Write-Host "Пользователь " $ln " не найден"	
    	}
    }
    # 3.Указано display name пользователя
    elseif(($ln -eq $null) -and ($dn -ne $null))
    {
    	$strMatch = "*" + $dn + "*"
    	while($objADCRes.EOF -ne $true)
    	{
    		$ObjectConnectionString = "LDAP://" + ($objADCRes.Fields.Item("distinguishedName").Value).ToString()
    		$ObjectConnection = [adsi]$ObjectConnectionString
    		$MatchResult = $ObjectConnection.displayName -like $strMatch
    		if($MatchResult -ne $null)
    		{
    			$IsUserFound = $true 
    			GetUserinfo 
    		}
    		$objADCRes.MoveNext()
    	}
    	if($IsUserFound -eq $false)
    	{
    		Write-Host "Пользователь " $dn " не найден"	
    	}
    }
    

    Andrew Mishechkin
    • Помечено в качестве ответа Andy Mishechkin 18 ноября 2010 г. 6:26
    18 ноября 2010 г. 6:26