Сервер вернул следующую ошибку недопустимый контекст перечисления

I posted this question the other day
Extract e-mail from grouped objects

$OuUser = @{}

$OuUser = Get-AdUser -Properties * -Filter * -SearchBase "domain"   

$Duplicates = $OuUser | Select samaccountname, mail,UserPrincipalName |
    Group-Object Mail | Where{$_.Count -gt 1}

$Duplicates | Select Name,Count,@{l='Accounts';e={($_.Group|Select -Expand samaccountname) -join ';'}} | 
    Export-CSV E:DamoDuplicates.csv -NoTypeInfo

The code works on one domain it works fine, testing it against a small set of users in a OU.

When testing on the domain I want to test against, which has a lot of users in it this code fails. The OU has email address in it which are not of an e-mail format. It points towards Get-ADUser for the error.

Get-ADUser : The server has returned the following error: invalid enumeration c
ontext.
At C:scriptsCountEmailsDup.ps1:4 char:21
+ $OuUser = Get-AdUser <<<<  -Properties * -Filter * -SearchBase 'ou=external,o
u=user accounts,dc=bizdir,dc=nzpost,dc=co,dc=nz' -SearchScope OneLevel
    + CategoryInfo          : NotSpecified: (:) [Get-ADUser], ADException
    + FullyQualifiedErrorId : The server has returned the following error: inv
   alid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.Ge
  tADUser

I am lost to why I am getting this error on one domain but not another.

Table of Contents

  • Scenario
  • Digging for the Roots
  • Solutions

I ran into a situation similar to the scenario below; although, I caught the problem during testing and not the production implementation. I was faced with a decision to rework my code, or understand the error so I could address it effectively. In this article
I will elaborate on what I found to be the cause of this error, and what situation brings it about. I will then show you what to do about it without having to revert to older methodologies to handle your task.

Scenario

If you have been working in an Active Directory environment for any length of time, there is a good possibility that you have run into a situation where you needed to run some process against all, or at least a large portion, of your Users or Computers.
Managing each account individually is very time consuming and inefficient, so you turn to the almighty PowerShell to automate this process. You start by using Get-ADUser or Get-ADComputer to retrieve the Objects you want from AD, then you use the powerful
Pipeline of PowerShell to send those AD Objects to another cmdlet to do the additional work you need. You may even take the results of the secondary cmdlet and use the pipeline to send them to tertiary, and quaternary cmdlets. The pipeline makes this all very
easy. You’ve tested your process on a few objects and are ready to process against your entire domain. Your change control is approved, you are in the driver’s seat, and you hit the enter button. This wonderful process takes off; it’s pure magic. Flying along,
PowerShell is processing each object and doing exactly what it was designed to do, but you have thousands of objects so this is going to take a while. Your part has been done, you created the process and kicked it off. PowerShell is doing its part, running
your creation. Might as well go to the kitchen and warm up some leftover pizza and grab a nice cold Dr Pepper. You glance over at your laptop on your way to the La-Z-Boy and see that everything is running smoothly, so you flip on an old rerun of Gilligan’s
Island. You watch as they put together some crazy contraption, which in the end utterly fails. If only they had you and PowerShell, they would be home sipping their own Dr Peppers by the end of the show. You glance back over at your laptop and the unthinkable
has occurred. In your PowerShell console where your process should be running, you see this:

get-aduser : The server has returned the following error: invalid enumeration context.

At line:1 char:1

+ get-aduser -Filter {msExchRecipientTypeDetails -eq «2147483648» -AND -NOT(UserAc .

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException

+ FullyQualifiedErrorId : The server has returned the following error: invalid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.GetADUser

What happened? A quick web search of «The server has returned the following error: invalid enumeration context.» returns lots of content on the topic with two prevailing recommendations.

  1. One of the attributes being queried is not indexed in AD. You can resolve this by modifying the Attribute in the AD Schema enabling the index flag.
  2. Ditch get-aduser and get-adcomputer and use ADSI DirectorySearcher instead.

IE:

https://social.technet.microsoft.com/Forums/scriptcenter/en-US/e3ae9c0d-4eed-4703-b120-14727e797df9/invalid-enumeration-context-using-powershell-script-to-check-computer-accounts?forum=ITCG

You don’t really want to manually modify your Active Directory schema, but this process is important so you give it a shot. Looking at all of the attributes you are pulling you notice that they are all already indexed, or you enable indexing but the problem
persists. You really like using native PowerShell functionality and you don’t really want to rework your code to use ADSI DirectorySearcher, but you really have no other options right? Wrong!

Digging for the Roots

In order to find a solution to this error, we have to understand why it happens in the first place. Let’s don our troubleshooting hat and see what’s going on. We will start by looking at the error message itself.

Note: for the purpose of the below illustration, I will only talk about Get-ADUser, but the same applies to Get-ADComputer.

The error message gives clear indication that the cmdlet which threw the error was Get-ADUser. Since our process is using Get-ADUser to send objects down the pipeline to other cmdlets, we run the Get-ADUser cmdlet by itself to see if we can get any new info.
Surprisingly the Get-ADUser command completes with no errors, and fairly quickly; however, every time we run the cmdlet and pass objects to other cmdlets through the pipeline, we generate the error. That is interesting because Get-ADUser is supposed to just
get the full results and hand them down the pipeline as soon as they come in right? Well, let’s see.

What can we find out about how Get-ADUser works? If we look at the help of Get-ADUser, we see two parameters that are of interest.

-ResultPageSize <int>

 Specifies the number of objects to include in one page for an Active Directory Domain Services query.

The default is 256
objects per page.

The following example shows how to set this parameter.

-ResultSetSize <System.Nullable[System.Int32]>

 Specifies the maximum number of objects to return for an Active Directory Domain Services query. If you want to receive
all of the objects, set this parameter to $null (null value). You can use Ctrl+c to stop the query and return of objects.

The following example shows how to set this parameter so that you receive
all of the returned objects.

 -ResultSetSize $null

So based on these parameters, we know that by default, Get-ADUser will return all matched objects from Active Directory, but it will do it in pages consisting of 256 objects per page.

Side Note: What exactly is paging? Simply put, it’s breaking a large query result down into smaller more manageable pieces. By default, if a Query results in 1000 objects, rather than sending all 1000 object back to the client, the server will send 256
object at a time (in separate pages) until all objects are sent. 1000 objects would result in 4 pages. It is up to the client to tell the server when it is ready for the next page of results.

(Paging Search Results: https://msdn.microsoft.com/en-us/library/aa367011(v=vs.85).aspx)

Now let’s look at Get-ADUser in action.

Using the sysinternals tool, TCPView.exe, we monitor the TCP communication of our Get-ADUser cmdlet while using Measure-Command to capture our runtime.

1

PS C:> Measure-Command -Expression {get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop}

 Days :
0

 Hours :
0

 Minutes :
0

 Seconds :
28

 Milliseconds :
904

 Ticks :
289045151

 TotalDays :
0.000334542998842593

 TotalHours :
0.00802903197222222

 TotalMinutes :
0.481741918333333

 TotalSeconds :
28.9045151

 TotalMilliseconds :
28904.5151

From the results we see that it took us 28.9 seconds to get 14.79 MBytes (15,504,491 Rcvd Bytes) of returned data from our query. We also see that we are connecting to TCP port 9389 on our domain controller. A quick web search on this TCP port reveals that
this is the port used by Active Directory Web Services (http://www.t1shopper.com/tools/port-number/9389/).

In order to see how many return objects we are dealing with, we run the command again, piping the results into Measure-Object

PS C:> get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | Measure-Object

 Count :
14163

The first thing we notice is that our process does not error out this time, even though we are using the pipeline. So maybe the problem relates to how long it takes to process our other pipeline commands. Since Measure-Object takes very little time to process,
it does not result in the error being generated. In order to test this theory we will use start-sleep to inject some processing time down the pipeline.

PS C:> get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

get-aduser : The server has returned the following error: invalid enumeration context.

 At line:1
char:1

 + get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEac ...

 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 + CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException

 + FullyQualifiedErrorId : The server has returned the following error: invalid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.GetADUser

After a while of running, we once again get our «The server has returned the following error: invalid enumeration context.» error, but why? The code runs fine without the pipeline, and it runs fine with the pipeline as long as the subsequent cmdlets complete
quickly. We know that the Get-ADUser cmdlet completes in about 30 seconds without the pipeline. Is it the same when we have slower processing further down the pipe?

Let’s do two things to see what is going on. We will use TCPView and Measure-Command, as we did before, to see what is going on from a network perspective and how long our code is running before generating the error.

PS C:>Measure-Command -Expression {get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEach-Object {Start-Sleep -Milliseconds
200; $_}}

After 5 minutes of running, we see that we have only received 2.15 Mbytes of data.

2

After 10 minutes of running, we are only sitting at 4.09 Mbytes of data.

3

Our slow data rate return continues until we finally error out after 30 minutes, and we also see that we did not receive our expected full data size of around 14.79 Mbytes.

4

get-aduser : The server has returned the following error: invalid enumeration context.

 At line:1
char:30

 + Measure-Command -Expression {get-aduser -Filter {-NOT(UserAccountControl -band
2 ...

 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 + CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException

 + FullyQualifiedErrorId : The server has returned the following error: invalid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.GetADUser

Days :
0

Hours :
0

Minutes :
30

Seconds :
49

Milliseconds :
886

Ticks :
18498861406

TotalDays :
0.0214107192199074

TotalHours :
0.513857261277778

TotalMinutes :
30.8314356766667

TotalSeconds :
1849.8861406

TotalMilliseconds :
1849886.1406

So our data is returning a lot slower, and we are erring out before receiving our full dataset. This is indicative of a timeout. Since we know that we are connecting to Active Directory Web Services, a quick web search for «Active Directory Web Services
Timeout 30 minutes» gets us here: https://technet.microsoft.com/en-us/library/dd391908(v=ws.10).aspx

Excerpt from above site:

MaxEnumContextExpiration

Specifies the maximum allowed time period during which the ADWS service processes and retrieves the results of a query request from a client computer.

 Changing the
default value of this parameter is strongly discouraged. Most of the search results are returned within
30 minutes.

Ok, lets put together what we’ve learned.

  1. Get-ADUser uses paging (256 object per page by default)
  2. It is up to the client to request new Pages
  3. When piping out AD Objects, the longer the code down the pipeline takes to process, the slower we retrieve data from Active Directory Web Services
  4. If that slower processing causes the retrieval time to run over 30 minutes, then the Enumeration Context Expires

Now lets fill in the blanks for the full, end to end process.

  1. Get-ADUser executes sending the query to Active Directory Web Services
  2. Active Directory Web Service receives the query, creates an enumeration context for the results, and returns the first page of results containing 256 objects
  3. Get-ADUser receives those results and sends them down the pipeline, but does not query Active Directory Web Services for the next page until the pipeline has finished processing all 256 objects already received.
  4. The pipeline processes each object placed in it one at a time down the entire pipeline. So all pipeline processes are completed on the first object before the second object begins it journey down the pipeline. (For 256 objects with 200 millisecond pipeline
    processing per item, that’s 51.2 seconds per page. With 14163 object, we are looking at 55.32 pages and 47.21 minutes.)
  5. Once all 256 objects have been processed for the current page, Get-ADUser requests the next page of results from Active Directory Web Services.
  6. Active Directory Web Services returns the next 256 objects.
  7. Steps 3 — 6 repeat until all objects are retrieve or the processing time goes longer than 30 minutes. After 30 minutes, Active Directory Web Services expires the enumeration context it created in step 2. Once expired, Active Directory Web Services returns
    the error, «invalid enumeration context», when Get-ADUser request the next page because the enumeration context has expired and is no longer valid.

So mystery solved.

Solutions

There are two possible solutions to this issue, the first of which I would not recommend.

  1. Increase the «MaxEnumContextExpiration» value for Active Directory Web Service. There are many reasons not to take this approach.
    1. First and foremost, Microsoft recommends again this. «Changing the default value of this parameter is strongly discouraged. Most of the search results are returned within 30 minutes.»
    2. This change would have to be made on all Domain Controllers that the script would possibly hit.
    3. The target value to increase to would be a moving target depending on number of objects returned and length of time required to process all pipeline code.
    4. Increasing this value increases the potential impact of long running processes on your Domain Controllers.
    5. If you choose this method, it can be done by modifying the «Microsoft.ActiveDirectory.WebServices.exe.config» file in the ADWS directory. By default: «C:WindowsADWS».5
  2. Retrieve your Active Directory objects to a variable first, then send it down the pipeline using the variable. This method is easy to implement in your code without lots of configuration changes in your Active Directory environment. It works because writing
    the objects to a variable is very fast so your Get-ADUser and Get-ADComputer cmdlets can quickly write all object to the variable and request the next page until all object are received.

It’s this simple:

Instead of doing this:

get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

Do this:

$adobjects = get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop

$adobjects | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

Or if you wanna keep it on a single commandline for the PowerShell console, just separate it with a semicolon:

$adobjects = get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop; $adobjects | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

My hope is that you will find this useful. Please feel free to leave a comment and let me know how it has helped. Thanks for reading!

#powershell

#powershell

Вопрос:

Я пытаюсь получить всех подрядчиков и сотрудников из нашего AD с включенным true для фильтра. Сам AD очень большой и содержит много данных, что может привести к ограничению времени ожидания при запуске сценария powershell. Я получаю эту ошибку, когда я помещаю ее в переменную, и если я выполняю прямой экспорт, я получаю ограничение по времени ожидания.

Есть ли какой-либо способ собрать все данные подрядчика и сотрудника с включенными сотрудниками с big AD без возникновения этой проблемы? Я получаю вывод, но не уверен, что вывод осуществим, поскольку я получаю сообщение об ошибке.

Спасибо

 Param(
    [Parameter(Mandatory = $true,ValueFromPipeline = $false,HelpMessage = "Specify a output file name and path.")]
    [string]
    $OutputFileNamePath = $null
)
$Storage = @()
Write-Host "**********************************************************" -ForegroundColor Green
Write-Host "*                  On Process                            *" -ForegroundColor Green
Write-Host "**********************************************************" -ForegroundColor Green
$filter = @("CONTRACTOR", "EMPLOYEE")
$Storage = Get-ADUser -Filter * -Properties EmployeeNumber,Name,SamAccountName,employeeType,PasswordLastSet,LastLogonDate | Where {($_.Enabled -eq $True) -and $filter -contains $_.employeeType} | Select EmployeeNumber,Name,SamAccountName,employeeType,PasswordLastSet,LastLogonDate 
$Storage | Export-Csv -Path $OutputFileNamePath".csv" -Force
Write-Host "**********************************************************" -ForegroundColor Green
Write-Host "*                     Done                               *" -ForegroundColor Green
Write-Host "**********************************************************" -ForegroundColor Green
 

Ответ №1:

Эта статья TechNet дает нам подсказку о том, почему может возникнуть эта ошибка, в основном:

  1. Get-ADUser использует подкачку (по умолчанию 256 объектов на страницу)
  2. Запрашивать новые страницы должен клиент
  3. При конвейерной передаче объектов AD чем дольше обрабатывается код в конвейере, тем медленнее мы извлекаем данные из веб-служб Active Directory
  4. Если из-за более медленной обработки время поиска превышает 30 минут, то контекст перечисления истекает

Скорее всего, ваш запрос выполняется в течение 30 минут, что приводит к этому исключению. Решение этой проблемы заключается в создании более эффективного запроса, чтобы он завершался до этого времени или, что не рекомендуется MS:

Увеличьте значение «MaxEnumContextExpiration» для веб-службы Active Directory. Есть много причин не использовать этот подход.


Вы можете использовать LDAPFilter для повышения производительности вашего запроса, Where-Object в этом случае использование не требуется:

 $properties = 'EmployeeNumber', 'employeeType', 'PasswordLastSet', 'LastLogonDate'
$filter = '(amp;(!userAccountControl:1.2.840.113556.1.4.803:=2)(|(employeeType=CONTRACTOR)(employeeType=EMPLOYEE)))'

Get-ADUser -LDAPFilter $filter -Properties $properties
 

Чтобы преобразовать фильтр LDAP во что-то читаемое:

  • amp; — Логический И Операторный
  • | — Логический оператор OR
  • (!userAccountControl:1.2.840.113556.1.4.803:=2) — Включен пользовательский объект
  • (|(employeeType=CONTRACTOR)(employeeType=EMPLOYEE)) employeeType является «Подрядчиком» ИЛИ «сотрудником»
  • (amp;(...)(|(...)(...))) — Все предложения должны быть выполнены. Мы можем прочитать это как:
    (Пользователь включен) И (пользователь employeeType — «Подрядчик» ИЛИ «Сотрудник»)

Для дальнейшего использования Active Directory: фильтры синтаксиса LDAP

Комментарии:

1. Спасибо! Это работает для меня!

2. @Den10102020 рад помочь 😉

Ответ №2:

Я изменил ваш скрипт, но я просто собрал всех пользователей в $Users массив, а затем запустил ForEach цикл для каждого отдельного пользователя. Внутри есть If функция, которая проверяет, включен ли пользователь и что EmployeeType является -like CONTRACTOR или EMPLOYEE . Если они есть, он добавляет их в $Storage массив, а затем экспортирует его после завершения цикла.

У меня это сработало, но дайте мне знать, если у вас возникнут какие-либо вопросы:

 Param(
    [Parameter(Mandatory = $true,ValueFromPipeline = $false,HelpMessage = "Specify a output file name and path.")]
    [string]
    $OutputFileNamePath = $null
)
$Storage = @()
Write-Host "----------------------------------------------------------" -ForegroundColor Green
Write-Host "-                  On Process                            -" -ForegroundColor Green
Write-Host "----------------------------------------------------------" -ForegroundColor Green
$Users = Get-ADUser -Filter * -Properties EmployeeNumber,Name,SamAccountName,employeeType,PasswordLastSet,LastLogonDate 
Foreach($user in $users){
If(($User.Enabled -eq $True) -and (($User.EmployeeType -like "*CONTRACTOR*") -or (($User.EmployeeType -like  "*EMPLOYEE*")))){
    $Storage  = $User  
    }

}
$Storage | Export-Csv -Path $OutputFileNamePath".csv" -NoTypeInformation -Force
Write-Host "----------------------------------------------------------" -ForegroundColor Green
Write-Host "-                     Done                               -" -ForegroundColor Green
Write-Host "----------------------------------------------------------" -ForegroundColor Green
 

Комментарии:

1. Привет, Джош! Спасибо за помощь, но у меня такая же проблема на моей стороне. Решение LDAP работает для меня

2. @Den10102020 Нет проблем! Я видел комментарий выше, рад, что он смог определить, в чем заключалась ошибка, и помочь вам ее устранить.

3. Он отличное соединение! Большинство моих проблем с PowerShell были решены г-ном Скварзоном

Мне нравятся команды каталогов get-adcomputer и quest-active для случаев, когда мне нужно много информации на сервере, но в остальном придерживаются команд активного каталога dsquery а также dsgetпотому что я нахожу get-adcomputer и особенно quest Команды излишне медленные, хотя что-то может потребовать от вас не использовать эти ds команды. Если у вас есть доступ к этим командам, это, возможно, стоит попробовать, даже если оно просто выдаст вам другое сообщение об ошибке, поскольку оно игнорирует использование get-adcomputer и существующий метод определения способности ping (вроде Микки -Мыши, но иногда этот способ дает дополнительную информацию) —

dsquery computer ou=Somewhere,dc=My,dc=AD,dc=TLD | ?{$_ -imatch "cn=([^,]+,),"} | % {
    $your_computer = $Matches[1]
    $cannot_ping_computer = $false

    # similarly for the ping command, but should be it's own little function
    ping $your_computer | ?{$_ -imatch "s*Packets: Sent = (d+), Received = (d+)" }|% {
      # or whatever conditions you find satisfactory
      if ($Matches[1] -ne $Matches[2]) $cannot_ping_computer = $true
    }
    if ( $cannot_ping_computer ) {
      #command to jump to next element in pipe, I cannot recall >.<      
    }
    # rest of your code...
}

Последние пару месяцев я не работал и не имел доступа к машине с Windows, поэтому код мне не по плечу, но я надеюсь, что он сработает для вас. Кажется правильным.

Я надеюсь, что вы решили проблему, но если нет, я надеюсь, что это может как-то помочь.

Удачи!:)

Table of Contents

  • Scenario
  • Digging for the Roots
  • Solutions

I ran into a situation similar to the scenario below; although, I caught the problem during testing and not the production implementation. I was faced with a decision to rework my code, or understand the error so I could address it effectively. In this article
I will elaborate on what I found to be the cause of this error, and what situation brings it about. I will then show you what to do about it without having to revert to older methodologies to handle your task.

Scenario

If you have been working in an Active Directory environment for any length of time, there is a good possibility that you have run into a situation where you needed to run some process against all, or at least a large portion, of your Users or Computers.
Managing each account individually is very time consuming and inefficient, so you turn to the almighty PowerShell to automate this process. You start by using Get-ADUser or Get-ADComputer to retrieve the Objects you want from AD, then you use the powerful
Pipeline of PowerShell to send those AD Objects to another cmdlet to do the additional work you need. You may even take the results of the secondary cmdlet and use the pipeline to send them to tertiary, and quaternary cmdlets. The pipeline makes this all very
easy. You’ve tested your process on a few objects and are ready to process against your entire domain. Your change control is approved, you are in the driver’s seat, and you hit the enter button. This wonderful process takes off; it’s pure magic. Flying along,
PowerShell is processing each object and doing exactly what it was designed to do, but you have thousands of objects so this is going to take a while. Your part has been done, you created the process and kicked it off. PowerShell is doing its part, running
your creation. Might as well go to the kitchen and warm up some leftover pizza and grab a nice cold Dr Pepper. You glance over at your laptop on your way to the La-Z-Boy and see that everything is running smoothly, so you flip on an old rerun of Gilligan’s
Island. You watch as they put together some crazy contraption, which in the end utterly fails. If only they had you and PowerShell, they would be home sipping their own Dr Peppers by the end of the show. You glance back over at your laptop and the unthinkable
has occurred. In your PowerShell console where your process should be running, you see this:

get-aduser : The server has returned the following error: invalid enumeration context.

At line:1 char:1

+ get-aduser -Filter {msExchRecipientTypeDetails -eq «2147483648» -AND -NOT(UserAc .

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException

+ FullyQualifiedErrorId : The server has returned the following error: invalid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.GetADUser

What happened? A quick web search of «The server has returned the following error: invalid enumeration context.» returns lots of content on the topic with two prevailing recommendations.

  1. One of the attributes being queried is not indexed in AD. You can resolve this by modifying the Attribute in the AD Schema enabling the index flag.
  2. Ditch get-aduser and get-adcomputer and use ADSI DirectorySearcher instead.

IE:

https://social.technet.microsoft.com/Forums/scriptcenter/en-US/e3ae9c0d-4eed-4703-b120-14727e797df9/invalid-enumeration-context-using-powershell-script-to-check-computer-accounts?forum=ITCG

You don’t really want to manually modify your Active Directory schema, but this process is important so you give it a shot. Looking at all of the attributes you are pulling you notice that they are all already indexed, or you enable indexing but the problem
persists. You really like using native PowerShell functionality and you don’t really want to rework your code to use ADSI DirectorySearcher, but you really have no other options right? Wrong!

Digging for the Roots

In order to find a solution to this error, we have to understand why it happens in the first place. Let’s don our troubleshooting hat and see what’s going on. We will start by looking at the error message itself.

Note: for the purpose of the below illustration, I will only talk about Get-ADUser, but the same applies to Get-ADComputer.

The error message gives clear indication that the cmdlet which threw the error was Get-ADUser. Since our process is using Get-ADUser to send objects down the pipeline to other cmdlets, we run the Get-ADUser cmdlet by itself to see if we can get any new info.
Surprisingly the Get-ADUser command completes with no errors, and fairly quickly; however, every time we run the cmdlet and pass objects to other cmdlets through the pipeline, we generate the error. That is interesting because Get-ADUser is supposed to just
get the full results and hand them down the pipeline as soon as they come in right? Well, let’s see.

What can we find out about how Get-ADUser works? If we look at the help of Get-ADUser, we see two parameters that are of interest.

-ResultPageSize <int>

 Specifies the number of objects to include in one page for an Active Directory Domain Services query.

The default is 256
objects per page.

The following example shows how to set this parameter.

-ResultSetSize <System.Nullable[System.Int32]>

 Specifies the maximum number of objects to return for an Active Directory Domain Services query. If you want to receive
all of the objects, set this parameter to $null (null value). You can use Ctrl+c to stop the query and return of objects.

The following example shows how to set this parameter so that you receive
all of the returned objects.

 -ResultSetSize $null

So based on these parameters, we know that by default, Get-ADUser will return all matched objects from Active Directory, but it will do it in pages consisting of 256 objects per page.

Side Note: What exactly is paging? Simply put, it’s breaking a large query result down into smaller more manageable pieces. By default, if a Query results in 1000 objects, rather than sending all 1000 object back to the client, the server will send 256
object at a time (in separate pages) until all objects are sent. 1000 objects would result in 4 pages. It is up to the client to tell the server when it is ready for the next page of results.

(Paging Search Results: https://msdn.microsoft.com/en-us/library/aa367011(v=vs.85).aspx)

Now let’s look at Get-ADUser in action.

Using the sysinternals tool, TCPView.exe, we monitor the TCP communication of our Get-ADUser cmdlet while using Measure-Command to capture our runtime.

1

PS C:> Measure-Command -Expression {get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop}

 Days :
0

 Hours :
0

 Minutes :
0

 Seconds :
28

 Milliseconds :
904

 Ticks :
289045151

 TotalDays :
0.000334542998842593

 TotalHours :
0.00802903197222222

 TotalMinutes :
0.481741918333333

 TotalSeconds :
28.9045151

 TotalMilliseconds :
28904.5151

From the results we see that it took us 28.9 seconds to get 14.79 MBytes (15,504,491 Rcvd Bytes) of returned data from our query. We also see that we are connecting to TCP port 9389 on our domain controller. A quick web search on this TCP port reveals that
this is the port used by Active Directory Web Services (http://www.t1shopper.com/tools/port-number/9389/).

In order to see how many return objects we are dealing with, we run the command again, piping the results into Measure-Object

PS C:> get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | Measure-Object

 Count :
14163

The first thing we notice is that our process does not error out this time, even though we are using the pipeline. So maybe the problem relates to how long it takes to process our other pipeline commands. Since Measure-Object takes very little time to process,
it does not result in the error being generated. In order to test this theory we will use start-sleep to inject some processing time down the pipeline.

PS C:> get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

get-aduser : The server has returned the following error: invalid enumeration context.

 At line:1
char:1

 + get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEac ...

 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 + CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException

 + FullyQualifiedErrorId : The server has returned the following error: invalid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.GetADUser

After a while of running, we once again get our «The server has returned the following error: invalid enumeration context.» error, but why? The code runs fine without the pipeline, and it runs fine with the pipeline as long as the subsequent cmdlets complete
quickly. We know that the Get-ADUser cmdlet completes in about 30 seconds without the pipeline. Is it the same when we have slower processing further down the pipe?

Let’s do two things to see what is going on. We will use TCPView and Measure-Command, as we did before, to see what is going on from a network perspective and how long our code is running before generating the error.

PS C:>Measure-Command -Expression {get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEach-Object {Start-Sleep -Milliseconds
200; $_}}

After 5 minutes of running, we see that we have only received 2.15 Mbytes of data.

2

After 10 minutes of running, we are only sitting at 4.09 Mbytes of data.

3

Our slow data rate return continues until we finally error out after 30 minutes, and we also see that we did not receive our expected full data size of around 14.79 Mbytes.

4

get-aduser : The server has returned the following error: invalid enumeration context.

 At line:1
char:30

 + Measure-Command -Expression {get-aduser -Filter {-NOT(UserAccountControl -band
2 ...

 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 + CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException

 + FullyQualifiedErrorId : The server has returned the following error: invalid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.GetADUser

Days :
0

Hours :
0

Minutes :
30

Seconds :
49

Milliseconds :
886

Ticks :
18498861406

TotalDays :
0.0214107192199074

TotalHours :
0.513857261277778

TotalMinutes :
30.8314356766667

TotalSeconds :
1849.8861406

TotalMilliseconds :
1849886.1406

So our data is returning a lot slower, and we are erring out before receiving our full dataset. This is indicative of a timeout. Since we know that we are connecting to Active Directory Web Services, a quick web search for «Active Directory Web Services
Timeout 30 minutes» gets us here: https://technet.microsoft.com/en-us/library/dd391908(v=ws.10).aspx

Excerpt from above site:

MaxEnumContextExpiration

Specifies the maximum allowed time period during which the ADWS service processes and retrieves the results of a query request from a client computer.

 Changing the
default value of this parameter is strongly discouraged. Most of the search results are returned within
30 minutes.

Ok, lets put together what we’ve learned.

  1. Get-ADUser uses paging (256 object per page by default)
  2. It is up to the client to request new Pages
  3. When piping out AD Objects, the longer the code down the pipeline takes to process, the slower we retrieve data from Active Directory Web Services
  4. If that slower processing causes the retrieval time to run over 30 minutes, then the Enumeration Context Expires

Now lets fill in the blanks for the full, end to end process.

  1. Get-ADUser executes sending the query to Active Directory Web Services
  2. Active Directory Web Service receives the query, creates an enumeration context for the results, and returns the first page of results containing 256 objects
  3. Get-ADUser receives those results and sends them down the pipeline, but does not query Active Directory Web Services for the next page until the pipeline has finished processing all 256 objects already received.
  4. The pipeline processes each object placed in it one at a time down the entire pipeline. So all pipeline processes are completed on the first object before the second object begins it journey down the pipeline. (For 256 objects with 200 millisecond pipeline
    processing per item, that’s 51.2 seconds per page. With 14163 object, we are looking at 55.32 pages and 47.21 minutes.)
  5. Once all 256 objects have been processed for the current page, Get-ADUser requests the next page of results from Active Directory Web Services.
  6. Active Directory Web Services returns the next 256 objects.
  7. Steps 3 — 6 repeat until all objects are retrieve or the processing time goes longer than 30 minutes. After 30 minutes, Active Directory Web Services expires the enumeration context it created in step 2. Once expired, Active Directory Web Services returns
    the error, «invalid enumeration context», when Get-ADUser request the next page because the enumeration context has expired and is no longer valid.

So mystery solved.

Solutions

There are two possible solutions to this issue, the first of which I would not recommend.

  1. Increase the «MaxEnumContextExpiration» value for Active Directory Web Service. There are many reasons not to take this approach.
    1. First and foremost, Microsoft recommends again this. «Changing the default value of this parameter is strongly discouraged. Most of the search results are returned within 30 minutes.»
    2. This change would have to be made on all Domain Controllers that the script would possibly hit.
    3. The target value to increase to would be a moving target depending on number of objects returned and length of time required to process all pipeline code.
    4. Increasing this value increases the potential impact of long running processes on your Domain Controllers.
    5. If you choose this method, it can be done by modifying the «Microsoft.ActiveDirectory.WebServices.exe.config» file in the ADWS directory. By default: «C:WindowsADWS».5
  2. Retrieve your Active Directory objects to a variable first, then send it down the pipeline using the variable. This method is easy to implement in your code without lots of configuration changes in your Active Directory environment. It works because writing
    the objects to a variable is very fast so your Get-ADUser and Get-ADComputer cmdlets can quickly write all object to the variable and request the next page until all object are received.

It’s this simple:

Instead of doing this:

get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

Do this:

$adobjects = get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop

$adobjects | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

Or if you wanna keep it on a single commandline for the PowerShell console, just separate it with a semicolon:

$adobjects = get-aduser -Filter {-NOT(UserAccountControl -band
2)} -ErrorAction Stop; $adobjects | ForEach-Object {Start-Sleep -Milliseconds
200; $_}

My hope is that you will find this useful. Please feel free to leave a comment and let me know how it has helped. Thanks for reading!

Для получения различной информации об учетных записях компьютера (серверах и рабочих станциях) в домене Active Directory можно использовать PowerShell командлет Get-ADComputer. Это один из наиболее полезных командлетов для выборки и поиска компьютеров по разным критериям в домене AD

Содержание:

  • Вывести атрибуты компьютера с помощью Get-ADComputer
  • Использование фильтров в Get-ADComputer
  • Полезные примеры использования командлета Get-ADComputer

Допустим, ваша задача – найти в Active Directory все неактивные компьютеры, которые не регистрировались в домене более 120 дней и заблокировать учетные записи этих компьютеров.

Прежде чем приступить к работе с командлетом Get-ADComputer, необходимо установить и импортировать модуль Active Directory Module для Windows PowerShell.

Import-Module activedirectory

Совет. В версии PowerShell 3.0 (представлен в Windows Server 2012) и выше этот модуль подключается по умолчанию при установке компонента Remote Server Administration Tools -> Role Administration Tools -> AD DS and AD LDS Tools -> Active Directory модуль для Windows PowerShell. Чтобы использовать командлет Get-ADComputer в клиентских Windows 11 или 10 нужно скачать и установить компонент RSAT для вашей версии ОС и включить модуль AD-PowerShell из панели управления или командой:
Add-WindowsCapability –online –Name “Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0”

Модуль Active Directory для Windows PowerShell

Справка о параметрах командлета Get-ADComputer вызывается стандартно с помощью Get-Help:

Get-Help Get-ADComputer

синтаксис командлета Get-ADComputer

Для получения информации из AD с помощью командлетов модуля AD для Powershell не обязательно иметь права администратора домена, достаточно чтобы учетная запись под которой запускается командлет входила в группу пользователей домена (Authenticated Users / Domain Users).

Чтобы получить информацию о доменной учетной записи конкретного компьютера или сервера, укажите его имя в качестве аргумента параметра —Identity:

Get-ADComputer -Identity SRV-DB01

get-adcomputer identity вывести базовую информацию о компьютере в домене Active Directory

DistinguishedName : CN=DB01,OU=Servers,OU=MSK,DC=winitpro,DC=ru
DNSHostName       : DB01.winitpro.ru
Enabled           : True
Name              : DB01
ObjectClass       : computer
ObjectGUID        : 1234567c-13f8-4a2c-8b00-b30a32324103
SamAccountName    : DB01$
SID               : S-1-5-21-3243682314-1360322815-2238451561-4318
UserPrincipalName :

Командлет вернул только базовые свойства объекта Computer из AD . Нас интересует время последней регистрации компьютера в домене AD, но этой информация в выводе команды нет. Выведем все доступные свойства (атрибуты) данного компьютера из Active Directory:

Get-ADComputer -Identity SRV-DB01 -Properties *

get-adcomputer вывести все свойства компьютера в AD

Этот список атрибутов компьютера также доступен в графической консоли Active Directory Users and Computers (
dsa.msc
) на вкладке редактора атрибутов.

список атрибутов компьютера в консоли ADUC

С помощью Get-Member можно получить список всех свойств класса Computer в AD:

Get-ADComputer -Filter * -Properties * | Get-Member

Как вы видите, время последнего входа данного компьютера в сеть указано в атрибуте компьютера LastLogonDate – 6/2/2022 3:59:30 AM.

Командлет Get-ADComputer позволяет вывести в результатах команды любые из свойств компьютера. Уберем всю лишнюю информацию, оставив в выводе только значения атрибутов Name и LastLogonDate.

Get-ADComputer -identity SRV-DB01 -Properties * | FT Name, LastLogonDate -Autosize

получить время последнего входа компьютера lastlogondate

Итак, мы получили данные о последнем времени регистрации в домене для одного компьютера. Теперь нам нужно изменить команду так, чтобы она возвращала информацию о времени последней регистрации в сети для всех компьютеров домена. Для этого заменим параметр –Identity на —Filter:

Get-ADComputer -Filter * -Properties * | FT Name, LastLogonDate -Autosize

get-adcomputer вывести врямя посленей загрузки для всех компьютеров

Мы получили таблицу, которая содержит только 2 поля: имя компьютера и дата LastLogonData. Вы можете добавить в эту таблицу другие поля объекта Computer из AD. Чтобы вывести данные о компьютерах в определенном контейнере домена (OU), воспользуйтесь параметром SearchBase:
Get-ADComputer -SearchBase ‘OU=Moscow,DC=winitpro,DC=loc’ -Filter * -Properties * | FT Name, LastLogonDate -Autosize

Отсортируем результаты запроса по времени последнего логина в сеть (поле LastLogonDate) с помощью команды Sort:

Get-ADComputer -Filter * -Properties * | Sort LastLogonDate | FT Name, LastLogonDate -Autosize

Сортировка по полю lastlogondate

Итак, мы получили список компьютеров домена и время их последнего входа в сеть Active Directory. Теперь мы хотим заблокировать учетные записи компьютеров, которые не использовались более 120 дней.

С помощью Get-Date получим в переменной значение текущей даты и вычтем из текущей даты 120 дней:

$date_with_offset= (Get-Date).AddDays(-120)

Полученную переменную с датой можно использовать в качестве фильтра запроса Get-ADComputer по полю LastLogonDate

Get-ADComputer  -Properties LastLogonDate -Filter {LastLogonDate -lt $date_with_offset } | Sort LastLogonDate | FT Name, LastLogonDate -Autosize

Таким образом, мы получили список неактивных компьютеров, которые не регистрировались в домене более 120 дней. С помощью командлета Set-ADComputer или Disable-ADAccount вы можете отключить эти учетные записи.

Совет. В первый раз лучше протестировать результаты команды с помощью переключателя WhatIf, благодаря которому команда не вносит никаких изменений, показывая, что произойдет при ее выполнении.

Get-ADComputer -Properties LastLogonDate -Filter {LastLogonData -lt $date_with_offset } | Set-ADComputer -Enabled $false -whatif

Теперь можно заблокировать все найденные учетные записи компьютеров:

Get-ADComputer -Properties LastLogonDate -Filter {LastLogonData -lt $date_with_offset} | Set-ADComputer -Enabled $false

Совет. Список заблокированных, отключенных и неактивных компьютеров и пользователей домена можно получить также с помощью отдельного командлета Search-ADAccount.

Использование фильтров в Get-ADComputer

С помощью аргумента -Filter командлета Get-ADComputer вы можете выбрать несколько компьютеров Active Directory по определенным критериями. Здесь можно использовать подстановочные знаки (wildcards) и логические операторы сравнения. В качестве фильтров можно использовать только базовые атрибуты компьютера.

Если вам нужно использовать фильтры по расширенными атрибутам компьютеров, их можно задавать через where-object. Несколько примеров есть в следующем разделе статьи.

Получить общее количество активных (незаблокированных) компьютеров в Active Directory:

(Get-ADComputer -Filter {enabled -eq "true"}).count

Вы можете использовать множественные фильтры для поиска компьютеров по нескольким параметрам сразу. Для этого используются логические операторы сравнения PowerShell (-and, -eq , -ne , -gt , -ge , -lt , -le , -like , -notlike , -and , -or , и т.д.).

Посчитать количество серверов с Windows Server в домене:

(Get-ADComputer -Filter {enabled -eq "true" -and OperatingSystem -Like '*Windows Server*' }).count

посчитать количество компьютеров и серверов в AD

Получить список компьютеров в определенном OU, имена которых начинаются с BuhPC:

Get-ADComputer -Filter {Name -like "BuhPC*"} -SearchBase ‘OU=Moscow,DC=winitpro,DC=loc’  -Properties IPv4Address | Format-table Name,DNSHostName,IPv4Address | ft -Wrap –Auto

При поиске по OU вы можете использовать дополнительный параметр -SearchScope 1, который означает, что нужно искать только в корневом разделе. Параметр -SearchScope 2 означает рекурсивный поиск компьютеров во всех вложенных OU.

Выбрать все рабочие станции с ОС Windows 10:

Get-ADComputer -Filter {OperatingSystem -like '*Windows 10*'}

Получить список серверов в домене с версией ОС, IP адресом и установленным Service Pack:
Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"' -Properties  Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack,IPv4Address | Sort-Object -Property Operatingsystem | Select-Object -Property Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack, IPv4Address| ft -Wrap –Auto

На выходе получили такую красивую таблицу со списком Windows Server в AD.

вывести версии windowsи ip адреса в домене с помощью powershell

Полезные примеры использования командлета Get-ADComputer

Ниже представлены еще несколько полезных примеров команд с использованием командлета Get-ADComputer, которые можно использовать для выборки и поиска компьютеров домена по определенными критериям.
Атрибут -LDAPFilter позволяет использовать в качестве параметра командлета Get-ADComputer различные LDAP запросы, например:

Get-ADComputer -LDAPFilter "(name=*db*)"|ft

Выбрать заблокированные компьютеры в определенном OU:

Get-ADComputer -filter * -SearchBase ‘OU=Computers, dc=winitpro,dc=loc’ | Where-Object {$_.enabled -eq $False}

Чтобы удалить все аккаунты компьютеров в домене, не авторизовавшиеся в домене более 6 месяцев, можете воспользоваться командой:

get-adcomputer -properties lastLogonDate -filter * | where { $_.lastLogonDate -lt (get-date).addmonths(-6) } | Remove-ADComputer

Вывести время последней смены пароля компьютера в Active Directory. По умолчанию пароль должен меняться компьютером автоматически раз в 30 дней, если пароль компьютера не совпадает с паролем в AD, доверительные отношения компьютера с доменом будут нарушены:

Get-ADComputer –Identity pc123456 -Properties PasswordLastSet

Результат выполнения команды Get-ADComputer можно выгрузить в текстовый файл:

Get-ADComputer -Filter { OperatingSystem -Like '*Windows Server 2019*' } -Properties OperatingSystem | Select DNSHostName, OperatingSystem | Format-Table -AutoSize C:Scriptserver_system.txt

Также вы можете получить выборку компьютеров и экспортировать его в CSV файл:

Get-ADComputer -Filter * -Property * | Select-Object Name,OperatingSystem,OperatingSystemServicePack | Export-CSV All-Windows.csv -NoTypeInformation -Encoding UTF8

Или получить HTML файл отчета со списком компьютеров и нужных атрибутов компьютера:

Get-ADComputer -Filter {OperatingSystem -Like '*Windows Server 2012*' } -Properties * | Select-Object Name,OperatingSystem | ConvertTo-Html | Out-File C:psad_computer.html

html отчеи по компьютерам в домене active directory

Можно удалено получить различную информацию с компьютеров AD через WMI (или CIM). Например, вывести серийные номера всех серверов в домене:

Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"' | Select-Object Name | Foreach-Object {Get-CimInstance Win32_Bios -ComputerName $_.Name -ErrorAction SilentlyContinue | Select-Object PSComputerName,SerialNumber}

Чтобы выполнить определенной действие со всеми компьютерами из полученного списка нужно использовать цикл Foreach. В этом примере мы хотим получить список серверов в домене с моделью и производителем:

$Computers = Get-ADComputer -Filter {OperatingSystem -Like '*Windows Server*'}
Foreach ($Computer in $Computers)
{
$Hostname = $Computer.Name
$ComputerInfo = (Get-WmiObject -Computername $Hostname Win32_ComputerSystem)
$Manufacturer = $Computer.Manufacturer
$Model = $Computer.Model
Write-Host "Name: $Hostname"
Write-Host "Manufacturer: $Manufacturer"
Write-Host "Model: $Model"
Write-Host " "
$Content = "$Hostname;$Manufacturer;$Model"
Add-Content -Value $Content -Path "C:PSServersInfo.txt"
}

Либо можно использовать более короткий синтаксис цикла. Допустим нам нужно выполнить определенную команду на всех компьютерах в определенном OU. В этом примере мы с помощью Invoke-Command выполним на всех серверах команду обновления настроек групповых политик:

get-adcomputer -SearchBase "OU=Servers,DC=winitpro,DC=loc" -Filter * | %{ Invoke-Command -Computer $_.Name -ScriptBlock {gpupdate /force} }

С помощью Get-ADComputer и логон скриптов PowerShell вы можете контролировать различные параметры компьютера или хранить различную полезную информацию в атрибутах компьютера в AD (можно например добавить имя пользователя в описание компьютера).

Я, например, контролирую состояние агента SCCM на компьютерах пользователей. При загрузке каждого компьютера на нем отрабатывает логон скрипт, который с помощью Set-ADComputer сохраняет состояние службы ccmexec в свободный атрибут компьютера — extensionAttribute10.

Затем с помощью следующей команды я могу найти компьютеры, на которых отсутствует или не запушена служба CCMExec:

get-adcomputer -filter {extensionAttribute10 -ne "SCCM Agent:Running"} -SearchBase “OU=Computers,OU=MSK,DC=winitpro,DC=ru” -properties dNSHostName,extensionAttribute10,LastLogonDate  |select-object dNSHostName,extensionAttribute10,LastLogonDate

get-adcomputer скрипт получения состояния службы на компьютерах домена через групповые политики

Для получения информации об учетных записях пользователей AD используется другой командлет — Get-ADUser .

Я пытаюсь получить всех подрядчиков и сотрудников из нашей AD с включенным фильтром true for. Сам AD очень огромен и содержит много данных, которые могут привести к ограничению времени ожидания при запуске сценария PowerShell. Я получаю эту ошибку, когда помещаю ее в переменную, и если я выполняю прямой экспорт, я получаю ограничение по времени ожидания.

Есть ли способ собрать все данные о подрядчиках и сотрудниках с включенными сотрудниками с большим AD без этой проблемы? Я получаю результат, но не уверен, насколько он реалистичен, так как получаю сообщение об ошибке.

Спасибо

Param(
    [Parameter(Mandatory = $true,ValueFromPipeline = $false,HelpMessage = "Specify a output file name and path.")]
    [string]
    $OutputFileNamePath = $null
)
$Storage = @()
Write-Host "**********************************************************" -ForegroundColor Green
Write-Host "*                  On Process                            *" -ForegroundColor Green
Write-Host "**********************************************************" -ForegroundColor Green
$filter = @("CONTRACTOR", "EMPLOYEE")
$Storage = Get-ADUser -Filter * -Properties EmployeeNumber,Name,SamAccountName,employeeType,PasswordLastSet,LastLogonDate | Where {($_.Enabled -eq $True) -and $filter -contains $_.employeeType} | Select EmployeeNumber,Name,SamAccountName,employeeType,PasswordLastSet,LastLogonDate 
$Storage | Export-Csv -Path $OutputFileNamePath".csv" -Force
Write-Host "**********************************************************" -ForegroundColor Green
Write-Host "*                     Done                               *" -ForegroundColor Green
Write-Host "**********************************************************" -ForegroundColor Green

2 ответа

Лучший ответ

Эта статья в TechNet дает нам подсказку, почему эта ошибка может возникать, в основном:

  1. Get-ADUser использует разбиение на страницы (по умолчанию 256 объектов на страницу)
  2. Клиент должен запрашивать новые страницы.
  3. При передаче объектов AD по конвейеру, чем дольше код в конвейере обрабатывается, тем медленнее мы получаем данные из веб-служб Active Directory.
  4. Если эта более медленная обработка приводит к тому, что время извлечения превышает 30 минут, истекает срок действия контекста перечисления.

Скорее всего, ваш запрос выполнялся в течение 30 минут, что привело к этому исключению. Решением этой проблемы является создание более эффективного запроса, чтобы он завершился до этого времени или не рекомендуется MS:

Увеличьте значение «MaxEnumContextExpiration» для веб-службы Active Directory. Есть много причин не использовать этот подход.


Вы можете использовать LDAPFilter для повышения производительности вашего запроса, в этом случае использование Where-Object не требуется:

$properties = 'EmployeeNumber', 'employeeType', 'PasswordLastSet', 'LastLogonDate'
$filter = '(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(|(employeeType=CONTRACTOR)(employeeType=EMPLOYEE)))'

Get-ADUser -LDAPFilter $filter -Properties $properties

Чтобы преобразовать фильтр LDAP во что-то читаемое:

  • & — логический оператор AND
  • | — логический оператор ИЛИ
  • (!userAccountControl:1.2.840.113556.1.4.803:=2) — активированный пользовательский объект
  • (|(employeeType=CONTRACTOR)(employeeType=EMPLOYEE)) employeeType — «Подрядчик» ИЛИ «Сотрудник»
  • (&(...)(|(...)(...))) — все пункты должны быть выполнены. Мы можем читать это так:
    ( Пользователь включен ) И ( Пользователь employeeType — это «Подрядчик» ИЛИ «Сотрудник» )

Для дальнейшего использования Active Directory: Фильтры синтаксиса LDAP


3

Santiago Squarzon
8 Дек 2021 в 07:24

Я изменил ваш сценарий, но я просто захватил всех пользователей в массив $Users, а затем запустил цикл ForEach для каждого отдельного пользователя. Внутри есть If, который проверяет, включен ли пользователь и что EmployeeType имеет значение -like CONTRACTOR или EMPLOYEE. Если да, он добавляет их в массив $Storage, а затем экспортирует его по завершении цикла.

У меня это сработало, но дайте мне знать, если у вас возникнут вопросы:

Param(
    [Parameter(Mandatory = $true,ValueFromPipeline = $false,HelpMessage = "Specify a output file name and path.")]
    [string]
    $OutputFileNamePath = $null
)
$Storage = @()
Write-Host "----------------------------------------------------------" -ForegroundColor Green
Write-Host "-                  On Process                            -" -ForegroundColor Green
Write-Host "----------------------------------------------------------" -ForegroundColor Green
$Users = Get-ADUser -Filter * -Properties EmployeeNumber,Name,SamAccountName,employeeType,PasswordLastSet,LastLogonDate 
Foreach($user in $users){
If(($User.Enabled -eq $True) -and (($User.EmployeeType -like "*CONTRACTOR*") -or (($User.EmployeeType -like  "*EMPLOYEE*")))){
    $Storage += $User  
    }

}
$Storage | Export-Csv -Path $OutputFileNamePath".csv" -NoTypeInformation -Force
Write-Host "----------------------------------------------------------" -ForegroundColor Green
Write-Host "-                     Done                               -" -ForegroundColor Green
Write-Host "----------------------------------------------------------" -ForegroundColor Green


0

Josh Gattis
7 Дек 2021 в 21:01

  • Remove From My Forums
  • Вопрос

  • Добрый день. Есть скрипт, который выгружает фото всех пользователей из AD:

    Import-Module ActiveDirectory
    get-aduser -filter {ObjectClass -eq "user"} -ResultSetSize $null -ResultPageSize 512 -Properties samaccountname,thumbnailphoto|`
    foreach { $login=$_.samaccountname;
    $path="c:1"+$login+".jpg";
    $user=Get-ADUser $login -Properties thumbnailphoto;
    if ($user.thumbnailphoto) {$user.thumbnailphoto | Set-Content $path -Encoding byte}
    $user=$null
    }

    Проблема в следующем — на рабочем компьютере (Windows 7 Enterprise) работает превосходно, а ни на одном контроллере домена (Windows Server 2008 R2) или серверах Exchange (2010 SP2) до конца не отрабатывает, выгружает все
    время разное количество фотографий и вылетает с ошибкой:

    Get-ADUser : Сервер вернул следующую ошибку: недопустимый контекст перечисления.
    C:Scriptexportphoto.ps1:6 знак:11
    + get-aduser <<<<  -filter {ObjectClass -eq "user"} -ResultSetSize $null -ResultPageSize 512 -Properties samaccountname,thumbnailphoto|`
        + CategoryInfo          : NotSpecified: (:) [Get-ADUser], ADException
        + FullyQualifiedErrorId : Сервер вернул следующую ошибку: недопустимый контекст перечисления.,Microsoft.ActiveDirectory.Management.Commands.GetADUser

    В чем может быть проблема?

Ответы

  • Советую поставить все обновления для .Net Framework,если не стоят.

    Как вариант можно воспользоваться ADSI:

    $Searcher = [adsisearcher]"(&(objectCategory=person)(objectClass=user)(thumbnailphoto=*))"
    $Searcher.PageSize  = 200
    $Searcher.SearchScope  = "subtree"
    
    $Attributes = "samaccountname","thumbnailphoto"
    ForEach($Attribute  In $Attributes)
    {
         $Searcher.PropertiesToLoad.Add($Attribute) | Out-Null
    }
    
    $Results =  $Searcher.FindAll()
    ForEach ($Result In $Results)
    {
         $login = $Result.Properties.Item("sAMAccountName")
         $user  = $Result.Properties.Item("thumbnailphoto")
         $path="c:1$login.jpg"
         $user| Set-Content $path -Encoding byte
    }

    • Помечено в качестве ответа

      5 июня 2013 г. 5:24

Solution 1

I never resolved this. I ended up making OUs under the larger container and running this against 1k accounts at a time.

Unfortunately, this will remain a mystery, since this environment no longer exists.

Solution 2

I would try playing with the ResultPageSize parameter based on this post. Perhaps setting it to a few hundred results at a time.

Solution 3

I like the get-adcomputer and quest-active directory commands for cases where i need a lot of information on a server, but otherwise stick with the active-directory commands dsquery and dsget, because I find the get-adcomputer and especially the quest commands unnecessarily slow, though something may be requiring you not to use these ds commands. If you do have access to these commands, this might be worth a shot, even if it just gives you a different error message, as it by-passes the use of get-adcomputer and existing method of determining ping-ability ( kind of Mickey-Mouse, but sometimes this way provides additional information ) —

dsquery computer ou=Somewhere,dc=My,dc=AD,dc=TLD | ?{$_ -imatch "cn=([^,]+,),"} | % {
    $your_computer = $Matches[1]
    $cannot_ping_computer = $false

    # similarly for the ping command, but should be it's own little function
    ping $your_computer | ?{$_ -imatch "s*Packets: Sent = (d+), Received = (d+)" }|% {
      # or whatever conditions you find satisfactory
      if ($Matches[1] -ne $Matches[2]) $cannot_ping_computer = $true
    }
    if ( $cannot_ping_computer ) {
      #command to jump to next element in pipe, I cannot recall >.<      
    }
    # rest of your code...
}

Been out of work for past couple months, and have no access to a Windows machine, so the code is off the top of my head, but I hope it works for you. Seems right.

I hope you solved the problem, but if not, I hope this can help in some way.

Good luck! :)

Solution 4

NB: I’m not a PS guru

My google fu turned up the following link.

In short, I think it has something to do with your -ResultSetSize $null portion of the script. In the link, the OP used -notlike "*"instead of the -eq "$Null"

Maybe play with that portion of the script and see what happens.

Related videos on Youtube

Bulk users creation powershell script for active directory

11 : 44

Bulk users creation powershell script for active directory

Run powershell command or script with AWX ansible tower on windows host.

07 : 24

Run powershell command or script with AWX ansible tower on windows host.

Using PowerShell Command Install-WindowsFeature to Install Server Roles onto Multiple Servers

04 : 24

Using PowerShell Command Install-WindowsFeature to Install Server Roles onto Multiple Servers

run a Powershell script | run a Powershell script | run a Powershell script

02 : 47

run a Powershell script | run a Powershell script | run a Powershell script

ProSo IT-Academy __Problem Solution__IT-Academy

Microsoft PowerShell for Beginners - Video 1 Learn PowerShell

27 : 57

Microsoft PowerShell for Beginners — Video 1 Learn PowerShell

Comments

  • We’re switching to WDS for deployment, so I’m writing a powershell script that will ping a computer and if it responds, get its MAC address and set the netbootGUID field in Active directory. It runs and works…for a while and then returns:

    Get-ADComputer : The server has returned the following error: invalid enumeration
    context.
    At PathToScriptssetNetbootGUIDremoteComputers.ps1:3 char:15

    • get-adcomputer <<<< -Filter * -searchbase «OU=Somehwere,DC=My,DC=AD,DC=TLD»
      -ResultSetSize $null | foreach-object {

      • CategoryInfo : NotSpecified: (:) [Get-ADComputer], ADException
      • FullyQualifiedErrorId : The server has returned the following error: invalid enumeration context.,Microsoft.ActiveDirectory.Management.Commands.GetADComputer

    This is the script:

    import-module ActiveDirectory
    
    get-adcomputer -Filter * -searchbase "OU=Somewhere,DC=MY,DC=AD,DC=TLD" -ResultSetSize $null | foreach-object {
    
        $strComputer = $_.Name
        $ping = new-object System.Net.NetworkInformation.Ping
        $Reply = $ping.send($strComputer)
        
        if ($Reply.status –eq “Success”){
                $colItems = GWMI -cl "Win32_NetworkAdapterConfiguration" -name "rootCimV2" -comp $strComputer -filter "IpEnabled = TRUE"
        
                ForEach ($objItem in $colItems) {
                    $MAC = $objItem.MacAddress.Replace(":", "") 
                    Write-Host "Machine Name: " $strComputer
                    Write-Host "MAC Address:" $MAC                          
                    [guid]$nbGUID = "00000000-0000-0000-0000-$MAC"        
                    $comp = get-adcomputer $strComputer -Properties netbootGUID
                    $comp.netbootGUID = $nbGUID.ToByteArray()
                    set-adcomputer -Instance $comp
                    write-output "$strComputer" | out-file -filePath c:somewhereguidSet.txt -append
                }
        }
        else {
            write-output "$strComputer" | out-file -filePath c:somewhereoffline.txt -append
        }
        $Reply = ""
    
    
         
    }
    

    I have no idea why I’m getting that error or what it means. My GoogleFu is failing me today.

    • Try wrapping it in a Try/Catch statement ala Ben’s example in my question here. It might give you more info to troubleshoot since it’s running for a bit before choking. It might be just a single result that’s causing it to crap out on you.

    • Curious. How many objects is this likely to return?

    • Do you know which computer is throwing this error? I suspect a permissions issue. I’ve run trough over 2000 of our machines and have only seen the "An exception occurred during a Ping request." error.

    • @uSlackr thousands

    • @jscott Unfortunately, no. But it seemed to happen at different positions each time. I ended up just creating sub-OUs and running it each of them with 1-2k objects a piece. This environment doesn’t exist anymore, so I can’t really follow up :(

    • Sorry to respond to an old thread — for some reason it bubbled up and I didni’t look at the thread date.

    • It’s OK. It bubbled up because it didn’t have an accepted answer. Unfortunately, it doesn’t look like I’ll be able to re-create the problem since the environment in question has changed substantially.

    • @MDMarra Doh, I didn’t even look at the date on this. :) Sorry. Glad to hear it’s working now.

  • Unfortunately, the -ResultSetSize isn’t a filter, so the -notlike switch doesn’t apply to it :( The get-help page for get-adcomputer says that -eq $NULL is the correct way to say that there is no max result size. Thanks for the try though.

  • I really don’t think that running dsquery computer -limit 0 is going to be faster than running a PowerShell query with a filter. The AD has well over 10k computer objects in it. Your implementation would return a result for every single computer and then evaluate whether or not it belongs in the set. I appreciate the attempt, but I’m looking for a pure native PowerShell implementation.

  • Mark, you are correct, and I updated the suggestion to filter as your task requires. This should eleviate concerns of efficiency on the 10k objects. I think the only advantage is handling small strings instead of objects with unused data, and that you can potentially get alternative information on the issue and, or, by-pass the current issue. Either way, good luck! :)

  • The site can’t be reached.? Anyway, does the -ResultPageSize will only limit the result up to that amount we specify not the complete result?

Recents

Для получения различной информации об учетных записях компьютера (серверах и рабочих станциях) в домене Active Directory можно использовать PowerShell командлет Get-ADComputer. Это один из наиболее полезных командлетов для выборки и поиска компьютеров по разным критериям в домене AD

Содержание:

  • Вывести атрибуты компьютера с помощью Get-ADComputer
  • Использование фильтров в Get-ADComputer
  • Полезные примеры использования командлета Get-ADComputer

Допустим, ваша задача – найти в Active Directory все неактивные компьютеры, которые не регистрировались в домене более 120 дней и заблокировать учетные записи этих компьютеров.

Прежде чем приступить к работе с командлетом Get-ADComputer, необходимо установить и импортировать модуль Active Directory Module для Windows PowerShell.

Import-Module activedirectory

Совет. В версии PowerShell 3.0 (представлен в Windows Server 2012) и выше этот модуль подключается по умолчанию при установке компонента Remote Server Administration Tools -> Role Administration Tools -> AD DS and AD LDS Tools -> Active Directory модуль для Windows PowerShell. Чтобы использовать командлет Get-ADComputer в клиентских Windows 11 или 10 нужно скачать и установить компонент RSAT для вашей версии ОС и включить модуль AD-PowerShell из панели управления или командой:
Add-WindowsCapability –online –Name “Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0”

Модуль Active Directory для Windows PowerShell

Вывести атрибуты компьютера с помощью Get-ADComputer

Справка о параметрах командлета Get-ADComputer вызывается стандартно с помощью Get-Help:

Get-Help Get-ADComputer

синтаксис командлета Get-ADComputer

Для получения информации из AD с помощью командлетов модуля AD для Powershell не обязательно иметь права администратора домена, достаточно чтобы учетная запись под которой запускается командлет входила в группу пользователей домена (Authenticated Users / Domain Users).

Чтобы получить информацию о доменной учетной записи конкретного компьютера или сервера, укажите его имя в качестве аргумента параметра —Identity:

Get-ADComputer -Identity SRV-DB01

get-adcomputer identity вывести базовую информацию о компьютере в домене Active Directory

DistinguishedName : CN=DB01,OU=Servers,OU=MSK,DC=winitpro,DC=ru
DNSHostName       : DB01.winitpro.ru
Enabled           : True
Name              : DB01
ObjectClass       : computer
ObjectGUID        : 1234567c-13f8-4a2c-8b00-b30a32324103
SamAccountName    : DB01$
SID               : S-1-5-21-3243682314-1360322815-2238451561-4318
UserPrincipalName :

Командлет вернул только базовые свойства объекта Computer из AD . Нас интересует время последней регистрации компьютера в домене AD, но этой информация в выводе команды нет. Выведем все доступные свойства (атрибуты) данного компьютера из Active Directory:

Get-ADComputer -Identity SRV-DB01 -Properties *

get-adcomputer вывести все свойства компьютера в AD

Этот список атрибутов компьютера также доступен в графической консоли Active Directory Users and Computers (
dsa.msc
) на вкладке редактора атрибутов.

список атрибутов компьютера в консоли ADUC

С помощью Get-Member можно получить список всех свойств класса Computer в AD:

Get-ADComputer -Filter * -Properties * | Get-Member

Как вы видите, время последнего входа данного компьютера в сеть указано в атрибуте компьютера LastLogonDate – 6/2/2022 3:59:30 AM.

Командлет Get-ADComputer позволяет вывести в результатах команды любые из свойств компьютера. Уберем всю лишнюю информацию, оставив в выводе только значения атрибутов Name и LastLogonDate.

Get-ADComputer -identity SRV-DB01 -Properties * | FT Name, LastLogonDate -Autosize

получить время последнего входа компьютера lastlogondate

Итак, мы получили данные о последнем времени регистрации в домене для одного компьютера. Теперь нам нужно изменить команду так, чтобы она возвращала информацию о времени последней регистрации в сети для всех компьютеров домена. Для этого заменим параметр –Identity на —Filter:

Get-ADComputer -Filter * -Properties * | FT Name, LastLogonDate -Autosize

get-adcomputer вывести врямя посленей загрузки для всех компьютеров

Мы получили таблицу, которая содержит только 2 поля: имя компьютера и дата LastLogonData. Вы можете добавить в эту таблицу другие поля объекта Computer из AD. Чтобы вывести данные о компьютерах в определенном контейнере домена (OU), воспользуйтесь параметром SearchBase:
Get-ADComputer -SearchBase ‘OU=Moscow,DC=winitpro,DC=loc’ -Filter * -Properties * | FT Name, LastLogonDate -Autosize

Отсортируем результаты запроса по времени последнего логина в сеть (поле LastLogonDate) с помощью команды Sort:

Get-ADComputer -Filter * -Properties * | Sort LastLogonDate | FT Name, LastLogonDate -Autosize

Сортировка по полю lastlogondate

Итак, мы получили список компьютеров домена и время их последнего входа в сеть Active Directory. Теперь мы хотим заблокировать учетные записи компьютеров, которые не использовались более 120 дней.

С помощью Get-Date получим в переменной значение текущей даты и вычтем из текущей даты 120 дней:

$date_with_offset= (Get-Date).AddDays(-120)

Полученную переменную с датой можно использовать в качестве фильтра запроса Get-ADComputer по полю LastLogonDate

Get-ADComputer  -Properties LastLogonDate -Filter {LastLogonDate -lt $date_with_offset } | Sort LastLogonDate | FT Name, LastLogonDate -Autosize

Таким образом, мы получили список неактивных компьютеров, которые не регистрировались в домене более 120 дней. С помощью командлета Set-ADComputer или Disable-ADAccount вы можете отключить эти учетные записи.

Совет. В первый раз лучше протестировать результаты команды с помощью переключателя WhatIf, благодаря которому команда не вносит никаких изменений, показывая, что произойдет при ее выполнении.

Get-ADComputer -Properties LastLogonDate -Filter {LastLogonData -lt $date_with_offset } | Set-ADComputer -Enabled $false -whatif

Теперь можно заблокировать все найденные учетные записи компьютеров:

Get-ADComputer -Properties LastLogonDate -Filter {LastLogonData -lt $date_with_offset} | Set-ADComputer -Enabled $false

Совет. Список заблокированных, отключенных и неактивных компьютеров и пользователей домена можно получить также с помощью отдельного командлета Search-ADAccount.

Использование фильтров в Get-ADComputer

С помощью аргумента -Filter командлета Get-ADComputer вы можете выбрать несколько компьютеров Active Directory по определенным критериями. Здесь можно использовать подстановочные знаки (wildcards) и логические операторы сравнения. В качестве фильтров можно использовать только базовые атрибуты компьютера.

Если вам нужно использовать фильтры по расширенными атрибутам компьютеров, их можно задавать через where-object. Несколько примеров есть в следующем разделе статьи.

Получить общее количество активных (незаблокированных) компьютеров в Active Directory:

(Get-ADComputer -Filter {enabled -eq "true"}).count

Вы можете использовать множественные фильтры для поиска компьютеров по нескольким параметрам сразу. Для этого используются логические операторы сравнения PowerShell (-and, -eq , -ne , -gt , -ge , -lt , -le , -like , -notlike , -and , -or , и т.д.).

Посчитать количество серверов с Windows Server в домене:

(Get-ADComputer -Filter {enabled -eq "true" -and OperatingSystem -Like '*Windows Server*' }).count

посчитать количество компьютеров и серверов в AD

Получить список компьютеров в определенном OU, имена которых начинаются с BuhPC:

Get-ADComputer -Filter {Name -like "BuhPC*"} -SearchBase ‘OU=Moscow,DC=winitpro,DC=loc’  -Properties IPv4Address | Format-table Name,DNSHostName,IPv4Address | ft -Wrap –Auto

При поиске по OU вы можете использовать дополнительный параметр -SearchScope 1, который означает, что нужно искать только в корневом разделе. Параметр -SearchScope 2 означает рекурсивный поиск компьютеров во всех вложенных OU.

Выбрать все рабочие станции с ОС Windows 10:

Get-ADComputer -Filter {OperatingSystem -like '*Windows 10*'}

Получить список серверов в домене с версией ОС, IP адресом и установленным Service Pack:
Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"' -Properties  Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack,IPv4Address | Sort-Object -Property Operatingsystem | Select-Object -Property Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack, IPv4Address| ft -Wrap –Auto

На выходе получили такую красивую таблицу со списком Windows Server в AD.

вывести версии windowsи ip адреса в домене с помощью powershell

Полезные примеры использования командлета Get-ADComputer

Ниже представлены еще несколько полезных примеров команд с использованием командлета Get-ADComputer, которые можно использовать для выборки и поиска компьютеров домена по определенными критериям.
Атрибут -LDAPFilter позволяет использовать в качестве параметра командлета Get-ADComputer различные LDAP запросы, например:

Get-ADComputer -LDAPFilter "(name=*db*)"|ft

Выбрать заблокированные компьютеры в определенном OU:

Get-ADComputer -filter * -SearchBase ‘OU=Computers, dc=winitpro,dc=loc’ | Where-Object {$_.enabled -eq $False}

Чтобы удалить все аккаунты компьютеров в домене, не авторизовавшиеся в домене более 6 месяцев, можете воспользоваться командой:

get-adcomputer -properties lastLogonDate -filter * | where { $_.lastLogonDate -lt (get-date).addmonths(-6) } | Remove-ADComputer

Вывести время последней смены пароля компьютера в Active Directory. По умолчанию пароль должен меняться компьютером автоматически раз в 30 дней, если пароль компьютера не совпадает с паролем в AD, доверительные отношения компьютера с доменом будут нарушены:

Get-ADComputer –Identity pc123456 -Properties PasswordLastSet

Результат выполнения команды Get-ADComputer можно выгрузить в текстовый файл:

Get-ADComputer -Filter { OperatingSystem -Like '*Windows Server 2019*' } -Properties OperatingSystem | Select DNSHostName, OperatingSystem | Format-Table -AutoSize C:Scriptserver_system.txt

Также вы можете получить выборку компьютеров и экспортировать его в CSV файл:

Get-ADComputer -Filter * -Property * | Select-Object Name,OperatingSystem,OperatingSystemServicePack | Export-CSV All-Windows.csv -NoTypeInformation -Encoding UTF8

Или получить HTML файл отчета со списком компьютеров и нужных атрибутов компьютера:

Get-ADComputer -Filter {OperatingSystem -Like '*Windows Server 2012*' } -Properties * | Select-Object Name,OperatingSystem | ConvertTo-Html | Out-File C:psad_computer.html

html отчеи по компьютерам в домене active directory

Можно удалено получить различную информацию с компьютеров AD через WMI (или CIM). Например, вывести серийные номера всех серверов в домене:

Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"' | Select-Object Name | Foreach-Object {Get-CimInstance Win32_Bios -ComputerName $_.Name -ErrorAction SilentlyContinue | Select-Object PSComputerName,SerialNumber}

Чтобы выполнить определенной действие со всеми компьютерами из полученного списка нужно использовать цикл Foreach. В этом примере мы хотим получить список серверов в домене с моделью и производителем:

$Computers = Get-ADComputer -Filter {OperatingSystem -Like '*Windows Server*'}
Foreach ($Computer in $Computers)
{
$Hostname = $Computer.Name
$ComputerInfo = (Get-WmiObject -Computername $Hostname Win32_ComputerSystem)
$Manufacturer = $Computer.Manufacturer
$Model = $Computer.Model
Write-Host "Name: $Hostname"
Write-Host "Manufacturer: $Manufacturer"
Write-Host "Model: $Model"
Write-Host " "
$Content = "$Hostname;$Manufacturer;$Model"
Add-Content -Value $Content -Path "C:PSServersInfo.txt"
}

Либо можно использовать более короткий синтаксис цикла. Допустим нам нужно выполнить определенную команду на всех компьютерах в определенном OU. В этом примере мы с помощью Invoke-Command выполним на всех серверах команду обновления настроек групповых политик:

get-adcomputer -SearchBase "OU=Servers,DC=winitpro,DC=loc" -Filter * | %{ Invoke-Command -Computer $_.Name -ScriptBlock {gpupdate /force} }

С помощью Get-ADComputer и логон скриптов PowerShell вы можете контролировать различные параметры компьютера или хранить различную полезную информацию в атрибутах компьютера в AD (можно например добавить имя пользователя в описание компьютера).

Я, например, контролирую состояние агента SCCM на компьютерах пользователей. При загрузке каждого компьютера на нем отрабатывает логон скрипт, который с помощью Set-ADComputer сохраняет состояние службы ccmexec в свободный атрибут компьютера — extensionAttribute10.

Затем с помощью следующей команды я могу найти компьютеры, на которых отсутствует или не запушена служба CCMExec:

get-adcomputer -filter {extensionAttribute10 -ne "SCCM Agent:Running"} -SearchBase “OU=Computers,OU=MSK,DC=winitpro,DC=ru” -properties dNSHostName,extensionAttribute10,LastLogonDate  |select-object dNSHostName,extensionAttribute10,LastLogonDate

get-adcomputer скрипт получения состояния службы на компьютерах домена через групповые политики

Для получения информации об учетных записях пользователей AD используется другой командлет — Get-ADUser .

Понравилась статья? Поделить с друзьями:
  • Сервер вернул ошибку при отправке транзакции electrum
  • Сервер вернул ошибку 403
  • Сервер вернул код ошибки 500
  • Сервер вернул код ошибки 404
  • Сервер активации вашей компании ошибка при активации виндовс