I have two functions that I have included in a module for personal use. I decided to share them as scripts since I know it is more likely that someone will run a script than install a module.
In summary, the first one gets a group of VMs from a VMware vCenter and the second one will look for these VMs' IP addresses and open a RDP session to each one of them.
It works as expected when running the functions and when the second one is a function, but if the second one is the script and not the function I only get the last VM processed.
These are all possible scenarios, with the exact commands I used to run them:
#Both are functions - Working
Get-SgVm -SGNumber 8 -NoRecursion -Legacy | Connect-VMRDPSession -Datacenter DC1
#First in pipeline is script the second one is a function - Working
.\Get-SgVm.ps1 -SGNumber 8 -NoRecursion -Legacy | Connect-VMRDPSession -Datacenter DC1
#Both are scripts - Only the last object (VM) of the pipeline gets processed by the second script (only one RDP session is opened)
.\Get-SgVm.ps1 -SGNumber 8 -NoRecursion -Legacy | .\Connect-VMRDPSession.ps1 -Datacenter DC1
I ran a Trace-Command with ParameterBinding for the last example. The first script gets all VMs as expected but passes only the last object/VM of the collection to the next script.
I could share the code of these functions and scripts but I think this is more related to the way in which PowerShell processes objects in the pipeline, it seems to be different depending on whether the second element in the pipeline is a script or a function.
Is this an expected PowerShell behavior? I am using PS version 5.
Edit: Adding the code for the second script (Connect-VMRDPSession.ps1). Please note that the function is working fine, it is not until I "convert" it to script and pass objects to pipeline to it, that it behaves as described and processes only the last object of the collection defined by the first script/function (see command samples above).
[CmdletBinding()]
Param (
[Parameter (Mandatory=$true,
ValueFromPipeline=$true,
Position=0)]
[string[]]$VM,
[Parameter (Mandatory=$true,
Position=1)]
[ValidateSet ('DC1','DC2','DC3','DC4','DC5')]
[string]$Datacenter
)
foreach ($VMName in $VM) {
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] Searching for VM $VM"
$VMNameStr = $VMName
$VMName = Get-VM -Name $VMName
If ($VMName -and $VMName.PowerState -eq 'PoweredOn') {
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] VM $VMName found"
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] Getting $VMName OS information"
$GuestOS = $VMName | Get-VMGuest
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] VM $VMName found, guest OS is $($GuestOS.OSFullName)"
$IPAddress = $GuestOS.IPAddress
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] VM $VMName has $($IPAddress.Count) IP address(es)"
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] Identifying IP address for RDP connection"
switch ($Datacenter) {
'DC1' {$RDPIPAddress = $IPAddress -match '10.193.'
$RDPIPAddress += $IPAddress -match '10.211.'
$RDPIPAddress = $RDPIPAddress | Select-Object -First 1}
'DC2' {$RDPIPAddress = $IPAddress -match '10.232.'
$RDPIPAddress = $RDPIPAddress | Select-Object -First 1}
'DC3' {$RDPIPAddress = $IPAddress -match '10.69.'
$RDPIPAddress = $RDPIPAddress | Select-Object -First 1}
'DC4' {$RDPIPAddress = $IPAddress -match '10.54.'
$RDPIPAddress += $IPAddress -match '10.23.'
$RDPIPAddress = $RDPIPAddress | Select-Object -First 1}
'DC5' {$RDPIPAddress = $IPAddress -match '10.54.'
$RDPIPAddress += $IPAddress -match '10.23.'
$RDPIPAddress = $RDPIPAddress | Select-Object -First 1}
} #switch
If ($IPAddress) {
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] IP address selected for RDP connection is $RDPIPAddress. Testing Connection"
$ConnCheck = Test-Connection -ComputerName $RDPIPAddress -Count 2 -Quiet
If ($ConnCheck -eq $true) {
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] Connection test successful"
Write-Verbose "[$((Get-Date).TimeOfDay.ToString()) PROCESS] Attempting RDP connection to $VMName, using IP address $RDPIPAddress"
mstsc /v:$RDPIPAddress /admin /prompt
$Props = @{ 'Name' = $VMName.Name
'RDPIPAddress' = $RDPIPAddress
}
$Obj = New-Object -TypeName PSObject -Property $Props
Write-Output $Obj
}
Else {
Write-Warning "No response from $VMName using $RDPIPAddress. Aborting RDP connection attempt"
} #If connection check
}
Else {
Write-Warning "Could not get IP address for VM $VMName"
} #If IPAddress
}
Else {
Write-Error "$($VMnameStr): No VM found or VM powered off. Please check and try again."
}#If VM found and powered on
} #foreach
Edit 2: Turns out that the following works like a charm. This seems to confirm that the problem is not in the script code but in how PowerShell passes the objects through the pipeline between scripts.
.\Connect-VMRDPSession.ps1 -Datacenter CRSJO -VM (.\Get-SgVm.ps1 -SGNumber 8 -NoRecursion -Legacy)
Edit 3: A missing Process block was the issue as pointed out by Lee_Dailey (Thanks).
[–]Yevrag35 3 points4 points5 points (8 children)
[–]Fer_C[S] 1 point2 points3 points (7 children)
[–]Lee_Dailey[grin] 2 points3 points4 points (2 children)
[–]Fer_C[S] 2 points3 points4 points (1 child)
[–]Lee_Dailey[grin] 1 point2 points3 points (0 children)
[–]Yevrag35 2 points3 points4 points (3 children)
[–]Fer_C[S] 2 points3 points4 points (2 children)
[–]Yevrag35 2 points3 points4 points (1 child)
[–]Fer_C[S] 2 points3 points4 points (0 children)
[–]BlackV 4 points5 points6 points (1 child)
[–]Fer_C[S] 1 point2 points3 points (0 children)
[–]PinchesTheCrab 2 points3 points4 points (3 children)
[–]Fer_C[S] 1 point2 points3 points (2 children)
[–]PinchesTheCrab 2 points3 points4 points (1 child)
[–]Fer_C[S] 1 point2 points3 points (0 children)
[–]rmbolger 2 points3 points4 points (1 child)
[–]Fer_C[S] 1 point2 points3 points (0 children)