Invoke-Command with Computers array, access single array element inside scriptblock - arrays

I am wondering how to accomplish the following and hope you can help:
I want to execute several tasks on different computers by providing a array of computernames using
Invoke-Command -ComputerName $ComputerNameArray -ScriptBlock { ...}
Inside the scriptblock, I want to access the current ComputerName (not the whole array).
How is this possible?
I tried enclosing the Invoke-Command within a foreach loop, something like this:
foreach ($Computer in $ComputerNameArray)
{
Invoke-Command-ComputerName $Computer -ScriptBlock { ...}
}
which works, this way I can access the current ComputerName with $env:Computer but as the foreach isn't necessary here, I want to get rid of it.
Any ideas?
Thanks all!

This works for me:
Invoke-Command $ComputerNameArray { $env:computername }

Related

How do I access properties of nested arrays in powershell within foreach loops?

I'm trying to write a script that checks whether a group of registry keys are set individually and then either sets them or modifies them conditional on thier current state.
The keys that I want to check/set contain a mix of Strings and DWORDs
I want to loop through an array that contains the key I want to set paired with the value.
I've tried as a hashtable/splatting but the input to Get-ItemProperty fails because of the value parameter so I tried basic arrays instead with no luck.
They are all at the same registry path but I was attempting to do something similar to this:
$Path = "HKLM:\Software\path\to\keys"
$Properties = (
('key', value),
('key2', value2),
('key3', 'value3')
)
foreach ($item in $Properties){
$exist = Get-ItemProperty -Path $Path -name $item[0]
if ($exist) {
Set-ItemProperty -Path $Path -Name $item[0] -Value $item[1]
} else {
New-ItemProperty -Path $Path -Name $item[0] -Value $item[1]
}
}
But no matter what I've tried I cannot retrieve the individual elements of the inner arrays.
I realize I could probably do this long-form and just do it line by line rather than attempting to iterate through, but this is definitely a more elegant way, and would be a great template if I need to do something similar in the future.
Holy moly. Nothing breaks you out of a rut like posting the question to an online forum.
The .GetValue() method is what I needed, not the raw index number.
...So $Item.GetValue(0) for the key name and $Item.GetValue(1) for the value.
Feel silly answering my own question but hopefully it helps someone else!

Possible to have different array names when write-output PSCustomObject]#{ in my function?

Is there any way to name the variable for an array something in my Catch { and then have another array name for the array inside Try/script part of my function?
Cause when i try doing like this $computerObject = [PSCustomObject]#{
and then doing Write-Output $computerArray i can only get either my variables inside Try/script array being displayed inside Powershell window. Or only get the $error message from my Catch.. Is there any way to name each array something so i can do like below.
write-host "Results"
Write-Output $computerArray - display my first array here
write-host "Failed: computerlist" -foregroundcolor red
Write-Output $computerArray2 - display $error computers here. $error should just include computers who did not answer to ping and other stuff from my invoke-command computerlist.txt
The only true answer to why i need this separately is that sometimes i want my array in a CSV file. And sometimes i just want to copy info directly from Powershell window. And then its more practical to have failed computers separated and not in the same array output.
This function (as mentioned in comments) doesn't leverage the CIM cmdlets parallel capabilities, would recommend some tweaks to it but to answer the actual question, how can you "split" the output between success and fail:
The function as-is, doesn't require any modification to achieve this, it's try and catch blocks are outputting objects with the same properties and luckily one of those properties is Error and it's value is a boolean so you can simply first query all the computers and then split the result using .Where with Split mode.
The code would be like this:
$computers = 'computer1', 'computer2', ....
$computerArray = foreach($computer in $computers) {
Get-ComputerInformation -ComputerName $computer
}
# now we can split between FAIL and SUCCESS
$fail, $success = $computerArray.Where({ $_.Error }, 'Split')
$success | Export-Csv path\to\success.csv -NoTypeInformation
$fail | Export-Csv path\to\fail.csv -NoTypeInformation

Attempting to use two arrays as variables to search file names (powershell)

I need to search multiple (named) folders on multiple servers for files matching a specific date and copy those files to a local folder using Powershell. The number of folders are not the same size arrays as the number of servers. I.e. I need \server1\interfacefolders\folder1, \server1\interfacefolders\folder2, \server2\interfacefolders\folder1, \server2\interfacefolders\folder2, etc.
I have the following set up as arrays/variables preparing for this, I thought "nested" foreach loops would work, but it bombs out...any ideas how to get started on this?:
[string[]]$ProdServerArray = "server1", "server2", "server3"
[string[]]$InterfaceArray = "folder1", "folder2" "folder3" do {
$date = Read-host "Enter date (MM/DD/YYYY) : " } while ($date -as [datetime] -isnot [datetime])
$date = $date -as [datetime]
$destination = new-item c:\GetFilesResults\$($date.toshortdatestring().replace("/","-")) -type directory
$path = foreach ($ProdServer in $ProdServerArray)
{
$folder = foreach ($Interface in $InterfaceArray)
{
$file = "\\$path\InterfaceFolder\$folder\*"
if ("$file".LastWriteTime -gt $date.date)
{
Copy-Item -Path $file.fullname -Destination $destination
}
}
}
to build the full folder names from those two arrays, you can use two nested foreach loops. once you have the values, you can build the paths via something like the -f string format operator.
i left out the rest of your code since it does not appear to pertain to the question you asked. [grin]
$ProdServerArray = 'serverAAA', 'serverBbBbBb', 'server_CCC'
$InterfaceArray = 'folder1', 'folder2', 'folder3', 'folder666'
foreach ($PSA_Item in $ProdServerArray)
{
foreach ($IA_Item in $InterfaceArray)
{
'\\{0}\InterfaceFolders\{1}' -f $PSA_Item, $IA_Item
}
'=' * 30
}
output ...
\\serverAAA\InterfaceFolders\folder1
\\serverAAA\InterfaceFolders\folder2
\\serverAAA\InterfaceFolders\folder3
\\serverAAA\InterfaceFolders\folder666
==============================
\\serverBbBbBb\InterfaceFolders\folder1
\\serverBbBbBb\InterfaceFolders\folder2
\\serverBbBbBb\InterfaceFolders\folder3
\\serverBbBbBb\InterfaceFolders\folder666
==============================
\\server_CCC\InterfaceFolders\folder1
\\server_CCC\InterfaceFolders\folder2
\\server_CCC\InterfaceFolders\folder3
\\server_CCC\InterfaceFolders\folder666
==============================
First of all, you are missing a comma in the line:
[string[]]$InterfaceArray = "folder1", "folder2" "folder3"
Additionally, as far as I can tell, your do while loop doesn't appear to be accomplishing anything, as the only time this will ever be true is if the time 12:00:00AM exactly on the date specified. No matter what date you input in the format (MM/DD/YYYY), they will not be equal unless the case I said above.
Since you are searching multiple servers, Invoke-Command is your friend, as a foreach loop will act in series, while this will work in parallel. It will send out the search command to each server simultaneously.
I am not quite sure exactly what you are trying to do, so I did not fill in the actual search code(seen below), but the part i have left blank would be where you enter what filename/filename schema you are looking for. If you provide more clarity I can assist further if needed.
(Note: $filepath, although self explanatory, is the file paths you wish to search. You can generate them in a way similar to the one provided by Lee_Dailey. I'd recommend removing the divider lines and saving the paths generated to a String System.Array Object)
Invoke-Command -ComputerName $ProdServerArray -ScriptBlock {Get-Childitem –Path $filepath -Recurse -ErrorAction SilentlyContinue |where {<your code here>}}

Determine if wmiobject class exists for a given namespace

I have a namespace that I'd like to use to validate the existence of a WMI object before I run a Get-WmiObject against it further down in the code. For example, I want to throw the namespace for SSRS 2012 at it and if it doesn't exist on the machine, then I'll try the next namespace for SSRS 2008 R2.
Is there a way to check for the class's existence, by just guessing a namespace, without throwing an error if it does not exist?
I don't want to have to use a try-catch as the solution. I'd like to know a way that I can get a simple boolean result that tells me whether the class exists in this namespace.
I don't want to have to use SilentlyContinue as the solution either.
This will be executed from a Powershell job step in a SQL Agent job. This sometimes handles errors differently than pure Powershell, and is the reason for my concern about #'s 1 and 2 above.
You can use the -Class, -List, and -Namespace parameters of the Get-WmiObject cmdlet to see if a single class exists in the specified namespace:
$class = Get-WmiObject -Class 'Win32_BIOS' -List -Namespace 'root\cimv2';
$classExists = $class -ne $null;
Here's an alternative (but slower) method from an earlier revision of my answer:
$class = Get-WmiObject -List -Namespace 'root\cimv2' `
| Where-Object { $_.Name -eq 'Win32_BIOS'; };
$classExists = $class -ne $null;
Going back to my original answer, here's a third option that, in my testing, does not throw any errors if either the namespace or the class is invalid:
$class = Get-WmiObject -List | Where-Object {
$_.__NAMESPACE -eq 'root\cimv2' -and $_.__CLASS -eq 'Win32_BIOS';
};
$classExists = $class -ne $null;
Note that $_.Name and $_.__CLASS are effectively synonyms. In each of these code snippets, $class will contain a ManagementClass instance for the class you searched for, if found, otherwise $null.

Passing array to another script with Invoke-Command

I'm stuck trying to figure this out. I've seen the other articles and have tried everything, but I'm not getting anywhere. I am trying to pass an array as an argument to another PS script. Here's what I'm trying:
$IPArray = Get-Content C:\ListofIps.txt
Invoke-Command -Computername $server -Credential $cred -FilePath "C:\script.ps1" -ArgumentList (,$IPArray)
The 5 values in $IPArray aren't being passed to the script that I call.
Thanks in advance, guys, I really appreciate any help you can give...
Use:
... -ArgumentList (,$IPArray)
Because the ArgumentList parameter expects an array you've correctly noted that you need to wrap the array in another array. However, the correct syntax in this scenario (specifying a parameter value) is to use a grouping expression () to create the nested array.
This is an old question, but I'm going to re-iterate #keith-hill on the answer--add a comma at the beginning of the array declaration (even when including an existing array).
It's stupid, but I'm answering again because I tried all alternatives and that's the only thing that works--even with PowerShell 3.0+. You can use #require for anything from at least v3.0+, but nothing will work unless you do what #keith-hill suggests--even though it's an array, and the parameter is an array, PS sucks in this case (and I love PS)...do what he said (posting didn't work, so sorry but working answers are better):
\
... -ArgumentList (,$IPArray)
It makes no sense, but that works. Hands down to the PS team for dropping the bomb on this one, but if I hadn't done that my script would be null and void. And I've become the "scripting guy"...so here you go.
If you are just trying to pass one array, you can treat the $args variable itself as the array in your remote command and you get the same result as passing it as (, $IPArray) and then accessing that array in the scriptblock as $args[0]. I didn't test whether this works with multiple arrays or not.
That is to say,
$MyScriptBlock = { $args | % { Do-Stuff -Thing $PSItem } }
Invoke-Command -Session $MySession -ScriptBlock $MyScriptBlock -ArgumentList $IPArray
will return the same values as
$MyScriptBlock = { $args[0] | % { Do-Stuff -Thing $PSItem } }
Invoke-Command -Session $MySession -ScriptBlock $MyScriptBlock -ArgumentList (,$IPArray)

Resources