Number of table count is wrong in dataset - powershell

I am supplying 3 servers to loop however the $mdtable.table.count is only 1. I must be missing a simple thing here. Can anyone please help me resolve this?
Get-Content 'C:\test\computers.txt' | ? { $_.Trim() -ne "" } | ForEach-Object {
$value = Invoke-Command -Computer $_ -ScriptBlock {
Param($computer)
# Connect to SQL and query data, extract data to SQL Adapter
$SqlQuery = "xp_fixeddrives"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Data Source=$computer;Initial Catalog='Secaudit';Integrated Security = True";
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($SqlQuery, $Sqlconnection)
$mdtable = New-Object System.Data.Dataset
$nRecs = $SqlAdapter.Fill($mdtable) | Out-Null
$nRecs | Out-Null
$res = $mdtable.Tables.Count
$res
} -ArgumentList $_ -Credential $cred
}
$value

The thing you're missing is that
... | ForEach-object {
$value = Invoke-Command -Computer $_ -ScriptBlock {
...
} -ArgumentList $_ -Credential $cred
}
replaces the value of $value with each iteration when you actually want to accumulate the values.
You can achieve this for instance like this:
... | ForEach-object {
$value += Invoke-Command -Computer $_ -ScriptBlock {
...
} -ArgumentList $_ -Credential $cred
}
or like this:
$value = ... | ForEach-object {
Invoke-Command -Computer $_ -ScriptBlock {
...
} -ArgumentList $_ -Credential $cred
} | Measure-Object -Sum | Select-Object -Expand Sum

Related

csv powershell cmd output

I use this script for check on multiple machine if I have installed some apps and now the script return in txt file result. (format hostname\nYes/No). How can I change return result in csv file in 2 columns ; 1.Hostname ; 2. Result ?
actual result.txt
hostname
YES / NO
desired csv export
Hostname | Result
hostname | YES / NO / OFFLINE
script
$Computers = Get-Content -Path C:\Users\m\Desktop\ip.txt | Where-Object { $_ -match '\S' }
foreach($Computer in $Computers){
Write-Host $Computer
$User = "ussr"
$Password = "pssd"
$Command = 'hostname && if exist "C:\LdApp.exe" (echo YES) else (echo NO)'
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential($User, $secpasswd)
Get-SSHTrustedHost | Remove-SSHTrustedHost
try{
$SessionID = New-SSHSession -ComputerName $Computer -Credential $Credentials -AcceptKey:$true
Invoke-SSHCommand -Index $sessionid.sessionid -Command $Command | Select -Expand Output | Add-Content -Path result.txt}
catch {Add-Content -Path result.txt -Value "$Computer conectare esuata!"}
}
Thank you,
I have modified your code and not tested it. I will do some thing like this
$result = Get-Content -Path C:\Users\m\Desktop\ip.txt | ForEach-Object {
$computer = $_
try {
Write-Host $Computer
$User = "ussr"
$Password = "pssd"
$Command = 'if exist "C:\LdApp.exe" (echo YES) else (echo NO)'
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential($User, $secpasswd)
Get-SSHTrustedHost | Remove-SSHTrustedHost
$SessionID = New-SSHSession -ComputerName $Computer -Credential $Credentials -AcceptKey:$true
$output = if ($SessionID) {
(Invoke-SSHCommand -Index $sessionid.sessionid -Command $Command).Output
}
else {
"Offline"
}
}
catch {
{
Write-Host "$Computer conectare esuata!"
$output = "conectare esuata!"
}
}
[PsCustomObject]#{
'Hostname' = $computer
'Status' = $output
}
}
$result | Export-csv -Path "C:\Users\m\Desktop\result.csv" -NoTypeInformation
Answer2: for multiple commands and capturing results, and yet again not tested :(
Get-Content -Path C:\Users\m\Desktop\ip.txt | ForEach-Object {
$computer = $_
try {
Write-Host $Computer
$User = "ussr"
$Password = "pssd"
$Command = 'hostname && if exist "C:\LdApp.exe" (echo YES) else (echo NO)'
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential($User, $secpasswd)
Get-SSHTrustedHost | Remove-SSHTrustedHost
$SessionID = New-SSHSession -ComputerName $Computer -Credential $Credentials -AcceptKey:$true
if ($SessionID) {
$Output = (Invoke-SSHCommand -Index $sessionid.sessionid -Command $Command).Output
$result = $Output.split("`n")
[PSCustomObject]#{
"HostName" = $result[0]
"IPAddress" = $result[1] # Print $result to verify the exact index of this value.
"Status" = $result[2]
}
}
else {
[PSCustomObject]#{
"HostName" = $computer
"IPAddress" = "NA"
"Status" = "offline"
}
}
}
catch {
{
Write-Host "$Computer conectare esuata!"
}
}
} | Export-csv -Path "C:\Users\m\Desktop\result.csv" -NoTypeInformation

How to loop through Multiple Machines in a PSSession such that I execute Invoke-Command against the correct Machine?

My Powershell Script below wants to retrieve multiple registry values, from a set of Windows Services (200+), from multiple machines (up to 8 machines). Since I'm retrieving quite a lot of info, I decided to use New-PSSession.
But whenever it gets into the scriptblock of the foreach loop, it executes it on the incorrect machine and thus eventually errors out saying
The Error:
Cannot find path 'HKLM:\system\currentcontrolset\services\SomeServiceName' because it does not exist.
+ CategoryInfo : ObjectNotFound: (HKLM:\system\cu...ces\SomeServiceName:String) [Get-ItemProperty], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemPropertyCommand
+ PSComputerName : WEB002
The Powershell Script:
$MyCustomServiceArray = #()
$c1 = 0
$s = New-PSSession -ComputerName WEB001, WEB002
$MyCustomServices = Invoke-Command -session $s -scriptblock {Get-WmiObject -Class win32_service |
Where-Object -FilterScript {$_.Displayname -like "*Special Service for Me*"} |
Select PSComputerName, Name, DisplayName, State, StartMode, StartName, PathName |
Sort DisplayName}
foreach($MyCustomService in $MyCustomServices)
{
$MyCustomServiceName = $MyCustomService.Name
$RegistryPathForService = "hklm:\system\currentcontrolset\services\$MyCustomServiceName"
$c1 += 1
Write-Host "$c1. $MyCustomServiceName`n"
$StartUpParameter = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).startupparameter}
if ($StartUpParameter -eq $null) {$StartUpParameter = ""}
$DatabaseServerName = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).servername}
if ($DatabaseServerName -eq $null) {$DatabaseServerName = ""}
$DatabaseName = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).database}
if ($DatabaseName -eq $null) {$DatabaseName = ""}
$NetType = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).nettype}
if ($NetType -eq $null) {$NetType = ""}
$ObjectCacheSize = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).objectcache}
if ($ObjectCacheSize -eq $null) {$ObjectCacheSize = 0}
$Row = "" | Select HostName,ServiceName,StartUpParameter,ServiceDisplayName,Status,StartUpType, `
DatabaseServerName,DatabaseName,NetType,ObjectCacheSize, `
LogOnName,PathName
$Row.HostName = $MyCustomService.PSComputerName
$Row.ServiceName = $MyCustomService.Name
$Row.StartUpParameter = $StartUpParameter
$Row.ServiceDisplayName = $MyCustomService.DisplayName
$Row.Status = $MyCustomService.State
$Row.StartUpType = $MyCustomService.StartMode
$Row.DatabaseServerName = $DatabaseServerName
$Row.DatabaseName = $DatabaseName
$Row.NetType = $NetType
$Row.ObjectCacheSize = $ObjectCacheSize
$Row.LogOnName = $MyCustomService.StartName
$Row.PathName = $MyCustomService.PathName
$MyCustomServiceArray += $Row
}
$MyCustomServiceArray | Export-Csv -NoTypeInformation -Path "C:\Users\GaryTheBrave\Documents\AllMyCustomServices.csv"
Is there a way to loop through these Multiple Machines in the correct order?
The Solution was, as Lee_Daily hinted, Looping through sessions. Thanks Lee_Daily!!!
Here is the modified Powershell Script that works perfectly
$MyCustomServiceArray = #()
$c1 = 0
$sessions = New-PSSession -ComputerName WEB001, WEB002
foreach($session in $sessions)
{
$MyCustomServices = Invoke-Command -session $session -scriptblock {Get-WmiObject -Class win32_service |
Where-Object -FilterScript {$_.Displayname -like "*Special Service for Me*"} |
Select PSComputerName, Name, DisplayName, State, StartMode, StartName, PathName |
Sort DisplayName}
foreach($MyCustomService in $MyCustomServices)
{
$MyCustomServiceName = $MyCustomService.Name
$RegistryPathForService = "hklm:\system\currentcontrolset\services\$MyCustomServiceName"
$c1 += 1
Write-Host "$c1. $MyCustomServiceName`n"
$StartUpParameter = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).startupparameter}
if ($StartUpParameter -eq $null) {$StartUpParameter = ""}
$DatabaseServerName = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).servername}
if ($DatabaseServerName -eq $null) {$DatabaseServerName = ""}
$DatabaseName = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).database}
if ($DatabaseName -eq $null) {$DatabaseName = ""}
$NetType = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).nettype}
if ($NetType -eq $null) {$NetType = ""}
$ObjectCacheSize = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).objectcache}
if ($ObjectCacheSize -eq $null) {$ObjectCacheSize = 0}
$Row = "" | Select HostName,ServiceName,StartUpParameter,ServiceDisplayName,Status,StartUpType, `
DatabaseServerName,DatabaseName,NetType,ObjectCacheSize, `
LogOnName,PathName
$Row.HostName = $MyCustomService.PSComputerName
$Row.ServiceName = $MyCustomService.Name
$Row.StartUpParameter = $StartUpParameter
$Row.ServiceDisplayName = $MyCustomService.DisplayName
$Row.Status = $MyCustomService.State
$Row.StartUpType = $MyCustomService.StartMode
$Row.DatabaseServerName = $DatabaseServerName
$Row.DatabaseName = $DatabaseName
$Row.NetType = $NetType
$Row.ObjectCacheSize = $ObjectCacheSize
$Row.LogOnName = $MyCustomService.StartName
$Row.PathName = $MyCustomService.PathName
$MyCustomServiceArray += $Row
}
}
$MyCustomServiceArray | Export-Csv -NoTypeInformation -Path "C:\Users\GaryTheBrave\Documents\AllMyCustomServices.csv"

Can I pass a function and call it in a PSJobs scriptblock, and receive a return value?

When I run get-job | receive-job -keep I get no results and have no way to verify that my function is passed and working. Can I pass a function like this into a psjob? How do I capture the return value afterwards?
$servers = #("xxxx", "xxxx")
$jobs = New-Object System.Collections.ArrayList;
foreach ($server in $servers)
{
$jobName = $server + "_job";
$scriptBlock =
{
param($server)
param($portNumber)
Function testPort ($server, $portNumber)
{
$testPort = New-Object System.Net.Sockets.TCPClient # -ArgumentList $server, 3389;
$testPort.SendTimeout = 3;
try
{
$testPort.Connect($server, 3389);
}
catch
{
#do nothing;
}
$result = $testPort.Connected;
$testPort.Close();
return $result;
}
testPort -server $server -portNumber 3389; sleep 10;
}
$portNumber = "3389";
#Start-Job -Name $jobName -ScriptBlock {$scriptBlock} -ArgumentList $server, $portNumber;
$jobs.Add((Start-Job -Name $jobName -ScriptBlock {$scriptBlock} -ArgumentList $server, $portNumber | Out-Null));
}
$jobsReturnValues = New-Object System.Collections.ArrayList;
foreach ($job in $jobs)
{
$jobsReturnValues.Add(($job | Wait-Job | Receive-Job | Out-Null));
}
The code is almost fine, you did not have many errors. The main issue was the use of Out-Null, the { } on -ScriptBlock {$scriptBlock} and the param( ) block was defined 2 times in your function. I did a little modification to your function too :)
$servers = #('google.com','twitter.com')
$jobs = New-Object System.Collections.ArrayList;
$testPort = 80
foreach ($server in $servers)
{
$jobName = $server + "_job";
$scriptBlock = {
param(
[string]$server,
[int]$portNumber
)
Function testPort ($server, $portNumber)
{
$testPort = New-Object System.Net.Sockets.TCPClient # -ArgumentList $server, 3389;
$testPort.SendTimeout = 3;
try
{
$testPort.Connect($server, $portNumber);
}
catch
{
#do nothing;
}
$result = $testPort.Connected;
$testPort.Close();
[pscustomobject]#{
ServerName = $server
Port = $portNumber
TestConnection = $result
}
}
testPort -server $server -portNumber $portNumber
}
$jobs.Add((Start-Job -Name $jobName -ScriptBlock $scriptBlock -ArgumentList $server, $testPort)) > $null;
}
$jobsReturnValues = New-Object System.Collections.ArrayList;
foreach ($job in $jobs)
{
$jobsReturnValues.Add(($job | Receive-Job -Wait -AutoRemoveJob)) > $null;
}
Looks like this:
PS C:\> $jobsReturnValues|select * -ExcludeProperty RunspaceID|ft
ServerName Port TestConnection
---------- ---- --------------
google.com 80 True
twitter.com 80 True
If you go to the docs you will see that there are a family of cmdlets that work with Jobs.
$job = Start-Job...
$data = $job | Wait-Job | Receive-Job
Wait-Job waits for the job until it is not in a Running State.
Receive-Job returns the output of the job.
Update
After you updated your code I can say the following:
Start-Job -Name $jobName -ScriptBlock {$scriptBlock} -ArgumentList $server, $portNumber | Out-Null
Why do you pipe it into Out-Null? This makes the output of the Start-Job disappear, and you are appending into $job only null values. Remove the pipe into the Out-Null. Start-Job returns a System.Management.Automation.PSRemotingJob object, which you need so you can reference it later.
Also, you do the same thing here: $job | Wait-Job | Receive-Job | Out-Null. This makes the job's output disappear.
Start-Job -Name $jobName -ScriptBlock {$scriptBlock}
needs to be
Start-Job -Name $jobName -ScriptBlock $scriptBlock
Basically, you put a script block inside a script block with the first approach.
I think you should first test with some basic examples until you get the hang of how jobs work.

PowerShell 2.0,Get-ADComputer Script Issues (no output)

I am trying to use the script below to test the trust relationship with the domain controller for every computer in AD. I am using powershell 2.0. When I test the script I get no output. It is based off a powershell 4.0 script that works.
$localCredential = Get-Credential
ForEach ($Name in Get-AdComputer -Filter *){
$output = { $Name = $_.Name }
if (-not (Test-Connection $Name $_.Name -Quiet -Count 1)) { $output.Status = 'Offline'
} else {
$trustStatus = Invoke-Command $Name $_.Name -ScriptBlock { Test-ComputerSecureChannel } -Credential $localCredential
$output.Status = $trustStatus
}
[pscustomobject]$output
}
Below is a powershell 4.0 script that I have tried to convert because the .ForEach syntax is not valid in Powershell 2.0.
Source: https://adamtheautomator.com/trust-relationship-between-this-workstation-and-the-primary-domain-failed/
here is the working script I tried to convert from:
$localCredential = Get-Credential
#(Get-AdComputer -Filter *).foreach({
$output = #{ ComputerName = $_.Name }
if (-not (Test-Connection -ComputerName $_.Name -Quiet -Count 1)) { $output.Status = 'Offline'
} else {
$trustStatus = Invoke-Command -ComputerName $_.Name -ScriptBlock { Test-ComputerSecureChannel } -Credential $localCredential
$output.Status = $trustStatus
}
[pscustomobject]$output
})
Does anyone know why I am not getting an output? Is there something clearly wrong with the first script I posted? Any help would be greatly appreciated.
Thank you very much,
Dave
In the foreach() statement you declare the iterator variable $Name, but inside the loop body you inconsistently use $_ as well.
You're also using [pscustomobject]#{}, a special object allocation syntax that was introduced in PowerShell 3.0 - you need to use New-Object psobject -Property in version 2.0.
Finally, your $output variable needs to be a dictionary and not a scriptblock (notice the # in front of { Name = ... }).
To fix it all:
ForEach ($Computer in Get-AdComputer -Filter *){
$output = #{ Name = $Computer.Name }
if (-not (Test-Connection $Computer.Name -Quiet -Count 1)) {
$output.Status = 'Offline'
} else {
$trustStatus = Invoke-Command -ComputerName $Computer.Name -ScriptBlock { Test-ComputerSecureChannel } -Credential $localCredential
$output.Status = $trustStatus
}
New-Object psobject -Property $output
}

invoke-parallel in Powershell - data sharing inside script with global variable

Using invoke-parallel in Powershell, I'm trying to get a list of hosts where a certain command works vs. does not. How can I write to a global variable inside of invoke-parallel?
$creds = Get-Credential -UserName $username -Message 'Password?'
$servers = get-content .\hosts.txt
$success = #()
$failure = #()
Invoke-Parallel -InputObject $servers -throttle 20 -runspaceTimeout 30 -ImportVariables -ScriptBlock {
try
{
$result = Invoke-Command $_ -Credential $creds -Authentication "Negotiate" -ErrorAction Stop {hostname}
$success += $result
}
catch
{
$failure += $_
}
}
write-host $success
write-host $failure
Try this:
$Results = Invoke-Parallel -InputObject $servers -throttle 20 -runspaceTimeout 30 -ImportVariables -ScriptBlock {
try
{
$Output = Invoke-Command $_ -Credential $creds -Authentication "Negotiate" -ErrorAction Stop {hostname}
}
catch
{
$Output = $_
}
#($Output)
}
Final code sample for what I ended up using.
$username = "myuser"
$creds = Get-Credential -UserName $username -Message 'Password?'
$servers = get-content .\hosts.txt
$Results = Invoke-Parallel -InputObject $servers -throttle 20 -runspaceTimeout 30 -ImportVariables -ScriptBlock {
$arr = #{}
try
{
$Output = Invoke-Command $_ -Credential $creds -Authentication "Negotiate" {hostname} -ErrorAction Stop
$arr[$_] = "successful"
}
catch
{
$arr[$_] = "failed"
}
$arr
}
$Results