powershellinvoke-commandhorizon

Taking input from one PSSession and sending it to another


Like many others, my background is in Linux with no powershell experience. So this object oriented programming is messing me up.

I need to search through VMware Horizon for VMs with users assigned to them, then check if they are disabled in AD. If they are disabled in AD I want to recycle the VM.

At the moment I am pulling the SIDs for the users from VMware Horizon, but when I try to use these in an invoke-command against AD I receive the following error

"Object reference not set to an instance of an object"

The Script so far

function getlist() {
$temp=Invoke-Command -ComputerName $vdiserver -ScriptBlock { add-pssnapin vmware.view.broker; get-desktopvm | select user_sid }
$list=$temp | Select-Object user_sid
#$list
}
$vdi1="server1"
$vdi2="server2"
$test=Test-Connection -ComputerName $vdi1 -Quiet
$test2=Test-Connection -ComputerName $vdi2 -Quiet
if ($test -eq "True"){
$vdiserver=$vdi1
getlist
}
elseif ($test2 -eq "True"){
$vdiserver=$vdi2
getlist
}
else {echo "No servers to connect to"}

ForEach ($user in $list) #{
#echo $user
#sleep 1
#}
{Invoke-Command -ComputerName domaincontroller -ScriptBlock {param($p1) get-aduser -identity $p1 } -argumentlist $user}

Solution

  • So this object oriented programming is messing me up.

    So you're trying to revert to shell script, and writing twice as much code to do achieve half as much work.

    The most important bit you're missing is to imagine an object as a collection of things - like, imagine you're working with /etc/passwd and each line has a user ID and a group ID and a home directory and a login shell.. and you're passing the entire line around at once, that's your analogous object.

    An object has many properties, just like that (but overall more capable).

    When you Select user_sid you're choosing that field to stay in the 'line', but the line is still something like :::user_sid:::: with the other fields now empty. (Approximately). But they're still there and in the way. To work with it directly, you have to get it out of the 'line' entirely - throw the container away and just have the user_sid outside of it.

    get-desktopvm | select user_sid
    
    ->
    
    get-desktopvm | select -expandproperty user_sid
    

    which makes "sid1", "sid2", "sid3", but no containers for each sid.


    This

    function getlist() {
        $temp=Invoke-Command -ComputerName $vdiserver -ScriptBlock { add-pssnapin vmware.view.broker; get-desktopvm | select user_sid }
        $list=$temp | Select-Object user_sid
    }
    

    is essentially saying

    function getlist() {
        #do any amount of work here, and throw it all away.
    }
    

    Because the function returns nothing, and it doesn't change any data on disk or anything, so when the function finishes, the variables are cleared out of memory, and you can't use them afterwards.


    This:

    if ($test -eq "True"){
    

    is a bit of a nonsense. It might work, but it's not working how you expect because it's happenstance that "a string with content" compared to a boolean True is True, regardless of the string containing the English word "True" or not. But it's also redundant - $test is itself true or false, you don't need to compare True with anything. if ($test). Or even if (Test-Connection -ComputerName $vdi -Quiet)


    But stillll, so much work. Just connect to them all, and let it fail for the ones it can't contact. Maybe add -ErrorAction SilentlyContinue if you don't want to see the error.

    $VMs = Invoke-Command -ComputerName Server1,Server2 -ScriptBlock { 
        Add-PsSnapin vmware.view.broker
        Get-DesktopVm 
    } 
    

    Now you have all the VMs, get the user enabled/disabled state

    foreach ($VM in $VMs) {
    
        $Sid = $VM.user_sid
    
        $AdEnabled = Invoke-Command -ComputerName domaincontroller -ScriptBlock {
            (Get-AdUser -Identity $using:Sid).Enabled
        }
    
        $VM| Add-Member -NotePropertyName 'AdEnabled' -NotePropertyValue $AdEnabled
    
    }
    

    Now you should ideally have $VM as an array of objects, each one having all the VM Desktop properties - and also the True/False state of the AD Enabled property for that user account.

    $VM | Out-Gridview
    

    or

    $VM | Export-Csv Report.csv
    

    or

    $VM | Where-Object { -not $_.AdEnabled }