Passing array to another script with Invoke-Command - arrays

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)

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

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

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 }

Writing value from variable to file

$info = Invoke-SQLCmd -ServerInstance $Server -Database $Database -inputfile "$queriespath/compare_quantity.sql"
$changes = $info.length
for($i=0; $i-le$changes; $i++) {
$text = "Revise,$info[$i].quantity,$info[$i].itemid" | Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'
}
I'm trying to write the value of $info[$i].quantity,$info[$i].itemid to the file.
This is what my code outputs now
Revise,System.Data.DataRow System.Data.DataRow[2].quantity,System.Data.DataRow System.Data.DataRow[2].itemid
I want it to say the actual value of the varables how can I do this?
EDIT:
for($i=0; $i-le$changes; $i++) {
$text = $($info[$i].quantity) | Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'
}
The above is printing nothing.
for($i=0; $i-le$changes; $i++) {
$text = "$(Revise,$info[$i].quantity,$info[$i].itemid)" | Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'
}
The $() just tells powershell to interpret the expression contained in the parentheses, you still need the quotes to assign a string value to the variable.
You're running into an issue of String Expansion here.
Take this property, for instance:
$e = [psobject]#{Name='StackOverFlow';URL='http://stackoverflow.com'}
$e.URL
> http://stackoverflow.com
This works as expect. However, when I try to wrap the whole thing in quotes, look what happens:
"the URL of StackOverflow is $e.Url"
>the URL of StackOverflow is System.Collections.Hashtable.Url
Notice the lame 'lil .Url appended to the end there? PowerShell is doing String expansion for me, but it does this by scanning left to right and dropping in variable values for us, but it doesn't check to see if I'm really after a property or method name. It just thinks I see $e, let me replace it with what I've got for $e. Annoying!
So the easy way around this is to use the Order of Operation symbols (remember Please-Excuse-My-Dear-Aunt-Sally? Parenthesis, Exponent, Multiplication, Division, Addition, Subtraction) and just wrap the property references in parenthesis.
In my example then, I'd do this.
"the URL of StackOverflow is $($e.Url)"
> the URL of StackOverflow is http://stackoverflow.com
Going back to your question, try this instead:
"Revise,$($info[$i].quantity),$($info[$i].itemid)" |
Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'

How do I call New-Object for a constructor which takes a single array parameter?

In PowerShell, I want to use New-Object to call a single-argument .Net constructor new X509Certificate2(byte[] byteArray). The problem is when I do this with a byte array from powershell, I get
New-Object : Cannot find an overload for "X509Certificate2" and the argument count: "516".
This approach to using new-object should work:
$cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate `
-ArgumentList #(,$bytes)
The trick is that PowerShell is expecting an array of constructor arguments. When there is only one argument and it is an array, it can confuse PowerShell's overload resolution algorithm. The code above helps it out by putting the byte array in an array with just that one element.
Update: in PowerShell >= v5 you can call the constructor directly like so:
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate]::new($bytes)
Surprisingly to me, I tried this and it seems it works:
[byte[]] $certPublicBytes = something
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate] $certPublicBytes
return $cert
I don't yet know by what magic it works, so your explanatory comments are appreciated. :)
(Note: I since found that using square-brackets-type-name as I did above, can also lead to other errors, such as 'Cannot convert value "System.Byte[]" to type "System.Security.Cryptography.X509Certificates.X509Certificate". Error:
"Cannot find the requested object.' The explicit New-Object approach recommended by Keith seems better!)

Resources