Invoke-Command inside PROCESS not returning anything - powershell

I'm trying to use Invoke-Command inside an advanced function to return an object with the workspaces.
This works:
$g = Invoke-Command -ComputerName server1,server2,server3 -ScriptBlock {
$AgentCfg = New-Object -ComObject AgentConfigManager.MgmtSvcCfg
$AgentCfg.GetCloudWorkspaces()
}
$g| Measure-Object
This does not, Measure-Object says there are 0 objects.
function Get-WorkspaceInfo {
[CmdletBinding()]
param(
[string[]]$ComputerName,
[string]$ErrorLog
)
BEGIN {}
PROCESS {
foreach ($Computer in $ComputerName) {
write-verbose "$Computer Iteration"
$RemoteWorkspaces = Invoke-Command -ComputerName $Computer -ScriptBlock {
$AgentCfg = New-Object -ComObject AgentConfigManager.MgmtSvcCfg
$OMSWorkspaces = $AgentCfg.GetCloudWorkspaces() }
$RemoteWorkspaces | Measure-Object
}
}
END {}
}
Get-Workspaceinfo -ErrorLog x.txt -ComputerName server1,server2,server3 -Verbose

Related

PowerShell Script Issues with Variable Values

I am trying to write this script to restart computers only if they are Offline. The script for getting user infomration works but I cannot get the variable values for the restart portion at the bottom of the script. Does anyone have a suggestion? I am somewhat new to Powershell, but writing code. Example of my script follows:
Function Get-LoggedOnUser
{
Param
(
$ComputerName = $env:COMPUTERNAME,
$Credential
)
Function Test-RemoteRegistry
{
Param
(
[Parameter(Mandatory = $False)]
[switch]$Enable
,
[Parameter(Mandatory = $False)]
[switch]$Disable
,
[Parameter(ValueFromPipeline=$True)]
[String[]]$ComputerName = $env:COMPUTERNAME
)
Begin
{
$PipelineInput = (-not $PSBOUNDPARAMETERS.ContainsKey("ComputerName")) -and (-not $ComputerName)
Function Test ($Computer)
{
Try
{
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) | Out-Null
#20ms faster than Get-Service per computer! Not sure how to handle/check things like the firewall though...
#If we hit here without error Remote Reg is enabled.
If ($Disable)
{
Try
{
Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Stopped -ErrorAction Stop
Return $False
#If we hit here without error Remote Reg is now disabled.
}
Catch
{
Return $True
#If we hit here, we couldn't stop remote registry.
}
}
Else
{
Return $True
}
}
Catch
{
If ($Enable)
{
Try
{
Get-Service -Name RemoteRegistry -ComputerName $Computer | Set-Service -Status Running -ErrorAction Stop
Return $True
#If we hit here without error Remote Reg is now enabled.
}
Catch
{
Return $False
#If we hit here, we couldn't start remote registry.
}
}
Else
{
Return $False
#If we hit here remote registry is disabled.
}
}
}
}
Process
{
If ($PipelineInput)
{
Test $_
}
Else
{
$ComputerName | ForEach-Object {
Test $_
}
}
}
}
Foreach ($Computer in $Computername)
{
$Online = $False
$User = $False
$Locked = $False
If (Test-Connection $Computer -Count 2 -Quiet)
{
$Online = $True
If ($Credential)
{
$User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer -Credential $Credential | Select-Object -ExpandProperty UserName -ErrorAction Stop
}
Else
{
$User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer | Select-Object -ExpandProperty UserName -ErrorAction Stop
}
If (Test-RemoteRegistry -Enable -ComputerName $Computer)
{
If ((Get-Process logonui -ComputerName $Computer -ErrorAction SilentlyContinue) -and ($user))
{
$Locked = $True
}
}
}
$Output = New-Object PSObject
$Output | Add-Member noteproperty ComputerName $Computer
$Output | Add-Member noteproperty Online $Online
$Output | Add-Member noteproperty Username $User
$Output | Add-Member noteproperty Locked $Locked
$Output
}
}
Get-LoggedOnUser
If (($Online) -eq $False)
{Shutdown /r t 0 /m \\$Computername}
ELSE
{Write-host 'HELLO $Online $Computername'}
I just want this for a single user as I am using PDQ Inventory to roll out the script. The variables at the end of the script are $null?
Variables defined in a child scope - in which functions run by default - are never seen by the calling scope. See the conceptual about_Scopes help topic
It's best for functions to communicate values to the caller via their output ("return value"), which you're function is already doing: it outputs objects whose properties contain the values of interest.
Therefore:
Get-LoggedOnUser |
ForEach-Object { # Loop over all output objects
# Refer to the object at hand via the automatic $_ variable.
# Note the use of "..." (expandable strings) so as to support
# expansion (string interpolation).
if (-not $_.Online) { Shutdown /r t 0 /m "\\$($_.ComputerName)" }
else { "HELLO $($_.Online) $($_.ComputerName)" }
}

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 -parallel not working

I am writing a PowerShell script to display the status of free space and CPU, below is snippet:
function Get_FreeSpace ($authType, $comp) {
$freespace = Invoke-Command -ScriptBlock {
Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='E:'" |
Select-Object Size,Freespace
} -ComputerName $comp -Credential $authType
$freesize = [String]::Format("{0:P2}" -f ($freespace.Freespace / $freespace.Size))
return $freesize
}
function Get_CPUAverage ($authType, $comp) {
$Cpu_Average = Invoke-Command -ScriptBlock {
Get-WmiObject Win32_Processor |
Measure-Object -Property LoadPercentage -Average |
Select-Object Average
} -ComputerName $comp -Credential $authType
return $Cpu_Average.Average
}
workflow Test-Workflow {
foreach -parallel ($serv1 in $Servers) {
$Size = Get_FreeSpace -authType $cred -comp $serv1
$Cpu_Avg = Get_CPUAverage -authType $cred -comp $serv1
Write-Host $Size
}
}
Test-Workflow
Write-Host $Size
$Size is not returning anything if I use -parallel loop.
should pass $Servers as param as shown below:
workflow Test-Workflow {
param($Servers)
foreach -parallel ($serv1 in $Servers) {
$Size = Get_FreeSpace -authType $cred -comp $serv1
$Cpu_Avg = Get_CPUAverage -authType $cred -comp $serv1
Write-Host $Size
}
}
Test-Workflow $Servers
Alternatively, invoke-command runs in parallel with multiple computers:
invoke-command comp1,comp2,comp3 { sleep 5; "done $env:computername" }

Number of table count is wrong in dataset

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

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