3

I'm trying to create a script to run as a scheduled task, which will run against multiple servers and retrieve some information.

To start with, I populate the list of servers by querying AD for all servers that match a certain set of criteria, using Get-ADComputer.

The problem is, the list is returned as an object, which I can't then pass to the New-PSSession list. I have tried converting it to a comma-seperated string by doing the following:

foreach ($server in $serverlist) {$newlist += $server.Name + ","}

but this still doesn't work.

the alternative is to iterate through the list and run the various commands against each server one at a time, but my preference would be to avoid this and run them using one-to-many remoting.

UPDATE: To clarify what I want to end up being able to do is using -ComputerName $serverlist, so I want $serverlist to be a string rather than an object.

UPDATE 2: Thanks for all the suggestions. Between them and my original method I'm starting to wonder whether -ComputerName can accept a string variable? I've got varying degrees of success getting the list of computers converted to a comma separated string, but no matter how I do it I always get invalid network address.

Nic
  • 13,025
  • 16
  • 59
  • 102
Matt
  • 1,883
  • 5
  • 26
  • 39

3 Answers3

4

To explain it a little more, if you have executed Get-ADComputer then you will get possibly an array of objects or a single object back.

I assume you are calling something like the following:

$serverList = Get-ADComputer -LDAPFilter '(cn=*DC*)';

This would get all the computers with DC in their name.

Because you potentially have an array (more then one) or objects that you have process using the ForEach-Object cmdlet (also aliased as % or foreach) is a good approach. Which is basically execute a script block for each record in the pipeline. However as you have experienced you are getting an object back from the query not a string. So you just have to choose a property to use.

For example if you use:

$serverList[0] | Get-Member

You will get all the properties of a computer object. gm is also an alias for Get-Member. E.g.

PS> $serverList[0] | gm

TypeName: Microsoft.ActiveDirectory.Management.ADComputer

Name              MemberType            Definition
----              ----------            ----------
Contains          Method                bool Contains(string propertyName)
Equals            Method                bool Equals(System.Object obj)
GetEnumerator     Method                System.Collections.IDictionaryEnumerator GetEnumerator()
GetHashCode       Method                int GetHashCode()
GetType           Method                type GetType()
ToString          Method                string ToString()
Item              ParameterizedProperty Microsoft.ActiveDirectory.Management.ADPropertyValueCollection Item(string p...
DistinguishedName Property              System.String DistinguishedName {get;set;}
DNSHostName       Property              System.String DNSHostName {get;set;}
Enabled           Property              System.Boolean Enabled {get;set;}
Name              Property              System.String Name {get;}
ObjectClass       Property              System.String ObjectClass {get;set;}
ObjectGUID        Property              System.Nullable`1[[System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, ...
SamAccountName    Property              System.String SamAccountName {get;set;}
SID               Property              System.Security.Principal.SecurityIdentifier SID {get;set;}
UserPrincipalName Property              System.String UserPrincipalName {get;set;}

Quickly looking you can see that there is a DNSHostName property.

So to execute your command you could use something like:

$serverList = Get-ADComputer -LDAPFilter '(cn=*DC*)';
$serverList | ForEach-Object { New-PSSession -ComputerName $_.DNSHostName; }

However I will note that the New-PSSession cmdlet doesn't do much except create a remoting session to a computer (PowerShell Remoting has to be enabled to support this). You would need to use the Invoke-Command cmdlet to execute something from a script.

Example 1:

$serverList = Get-ADComputer -LDAPFilter '(cn=*DC*)';
$serverList | ForEach-Object { New-PSSession -ComputerName $_.DNSHostName; } | ForEach-Object { Invoke-Command -Session $_ -ScriptBlock { #Execute commands here }; };

Example 2:

Get-ADComputer -LDAPFilter '(cn=*DC*)' | % { Invoke-Command -ComputerName $_.DNSHostName -ScriptBlock { #Execute commands here }; };

The second example optimises out the New-PSSession because it is not really needed if you are only going to call the Invoke-Command without advanced options.

Also note there is no real need to store the result of Get-ADComputer in a variable, you would only do that if Get-ADComputer was took a long time to execute and you had to use it more then once.

The $_ is a pipeline variable that become whatever object in currently in the pipeline.

Updated

Specifically to use Invoke-Command to execute an identical command against multiple computers you can use the -ComputerName parameter to supply an array of strings. (This can be seen by looking at the help, i.e.-ComputerName <String[]>. This would not be the case for all commands).

As mentioned the output from Get-ADComputer is an object. So a string array compatible with the -ComputerName parameter would be constructed by.

$serverList = @();
Get-ADComputer -LDAPFilter '(cn=*DC*)' | % { $serverList += $_.DNSHostName; }

Basically this creates a new array and adds each computers DNS host name to it, but you end up with string[]. You would then be able to use:

Invoke-Command -ComputerName $serverList -ScriptBlock { #Execute commands here };

However also note that while is may appear you are only executing a single command under the hood it is almost always a number of PowerShell cmdlet. For example command pipeline have a silent | Out-Host appended to them.

You could also wrap any of the commands in a function to execute a single command.

Hope that answers you question.

Bernie White
  • 1,024
  • 7
  • 17
  • This provides me a way to do what I need, but doesn't answer my initial question of using one-to-many ps remoting. I'm guessing that it's not possible. – Matt Oct 02 '12 at 23:35
  • @Matt updated answer to directly answer the one-to-many specifics. – Bernie White Oct 03 '12 at 08:10
0

Every result on powershell is a .Net object.

When you have a result in powershell like yours (an object) and you need a string for a search or to use on another command you can pipe the result (the object) to the cmdlet Out-String. Alternatively you can use Out-File.

If you want to change something in the Output you can combine it with another cmdlets like the Format-* ones, Format-Table, Format-List, etc.

So, when you get an object and you need a string use something like this:

Command-Let | Out-String

or

Command-Let | Format-Custom (parameters) | Out-String
Maxwell
  • 5,026
  • 1
  • 25
  • 31
Ricardo Polo Jaramillo
  • 2,039
  • 1
  • 18
  • 35
0

I don't have access to AD cmdlets so can only offer something I have done before when needing the object as a string.

I wrote a tip on MSSQLTips.com for backing up databases using SQLPS. So I basically queried SQLPS for the names of the database and then passed that to the BACKUP command, which had to see the name as a string and not the object type it was. You can see it here if you want.

If you take your $serverlist and pass it to Get-Member, see if there is a ToString() method. I think this method shows up for most System object types. If you do see it then you can try your command this way:


$serverlist | foreach {$_.ToString()} | foreach {New-PSSession -computer $_}