1

Minimal Question:

How do I properly dispose of the remote Session Connection left behind after doing:

$session = New-PSSession -ComputerName $VM -Credential $CurrentUser
Invoke-Command -Session $session -ScriptBlock {
    $drive = New-PSDrive -Credential $Using:CurrentUser "dummyDriveName" -Root (Split-Path $Using:TargetPath) -PSProvider "FileSystem" 
    Set-Location $Using:TargetPath
}

Invoke-Command -Session $session -ScriptBlock {
    Remove-PSDrive "dummyDriveName"
}
Remove-PSSession -Session $session

I'm running code that looks roughly like this:

$VMs = @(
    "vm1.foo.lan",
    "vm2.foo.lan",
    "vm3.foo.lan"
)

$TargetPath = "\\$env:ComputerName\bar\bin\Debug"

$CurrentUser = (Get-Credential -Credential $env:UserName)
[System.Management.Automation.Runspaces.PSSession[]]$Sessions = @()

foreach ($VM in $VMs) {
    $session = New-PSSession -ComputerName $VM -Credential $CurrentUser
    $Sessions = $Sessions + $session

    Invoke-Command -Session $session -ScriptBlock {
        $drive = New-PSDrive -Credential $Using:CurrentUser "dummyDriveName" -Root (Split-Path $Using:TargetPath) -PSProvider "FileSystem" 
        Set-Location $Using:TargetPath
        #Actually do something here, but it's not relevant ... I can reproduce with this line commented out.
    }
}

# Wait until Target.exe are known to be complete.

foreach ($session in $Sessions) {
    Invoke-Command -Session $session -ScriptBlock {
        Remove-PSDrive "dummyDriveName"
    }
    Remove-PSSession -Session $session
}

My intent is to get a set of remote machines to all invoke an exe sitting on my machine, exposed via a remote share.

Broadly speaking, I:

  • Connect to the remote machine as myself.
  • Capture that connection.
  • Set up a remote drive connecting it to me
  • Do some stuff
  • Repeat for all machines.
  • Wait until remote processes are completed.
  • Reconnect to all the machines to remove the drive and the connection session.

At the end of this, I still have: enter image description here

These sessions do eventually seem to decay, but not reliably, and it's been able to saturate the max # sessions allowed and thus cause errors saying:

No more connections can be made to this remote computer at this time because there are already as many connections as the computer can accept
+ CategoryInfo          : InvalidOperation: (dummy:PSDriveInfo) [New-PSDrive], Win32Exception
+ FullyQualifiedErrorId : CouldNotMapNetworkDrive,Microsoft.PowerShell.Commands.NewPSDriveCommand
+ PSComputerName        : build7.foo.lan

UPDATE:

Thanks to @jrider who has suggested Get-SmbSession. Running that after the rest of my script returns:

PS C:\WorkingDirectoty> Get-SmbSession

SessionId    ClientComputerName ClientUserName NumOpens
---------    ------------------ -------------- --------
773228331225 10.xxx.yyy.89       FOO\MDM        785
773228331233 10.xxx.yyy.60       FOO\MDM        637
773228331245 10.xxx.yyy.89       FOO\MDM        239
773228331253 10.xxx.yyy.54       FOO\MDM        136
773228331261 10.xxx.yyy.54       FOO\MDM        882
773228331269 10.xxx.yyy.60       FOO\MDM        389

I obviously don't want this script to blindly close EVERY session irrespective of whether it relates to this script, so I guess I want to map my sessions to IP addresses and close anything with that IP address? Does anyone JustKnow the necessary PowerShell incantation to achieve that?

Brondahl
  • 7,402
  • 5
  • 45
  • 74

2 Answers2

2

To Close the SMB sessions by computer name(Updated to include Brondahl's suggestion):

$vmName = $env:ComputerName
$IP =  [System.Net.Dns]::GetHostAddresses($vmName).IPAddressToString
Get-SmbSession | Where-Object {$_.ClientComputerName -eq $IP} | Close-SmbSession -Force
jrider
  • 1,571
  • 1
  • 10
  • 26
  • 1
    Could you add `[System.Net.Dns]::GetHostAddresses($vmName).IPAddressToString` to get the whole thing (source: https://blogs.msdn.microsoft.com/powershell/2006/06/26/windows-powershell-one-liner-name-to-ip-address/) – Brondahl Apr 26 '18 at 20:20
1

For final reference, my complete cleanup code looks like this:

foreach ($session in $Sessions) {
    Write-Host ""
    $vmName = $session.ComputerName
    $vmIPAddress = [System.Net.Dns]::GetHostAddresses($vmName).IPAddressToString

    Write-Host "*** Killing any active Target.exe processes on $vmName ***"
    Invoke-Command -Session $session -ScriptBlock {
        Stop-Process -Name "Target" -Force
    }

    Write-Host "*** Disconnecting the remote Drive created to give $vmName easy access to $RepositoryShare***"
    Invoke-Command -Session $session -ScriptBlock {
        Remove-PSDrive "dummyDriveName" 
    }

    Write-Host "*** Closing any open PS connections to $vmName ***"
    Remove-PSSession -Session $session

    Write-Host "*** Closing the still-open Windows Share connection from $vmName to $env.ComputerName ***"
    Get-SmbSession | Where-Object {$_.ClientComputerName -eq $vmIPAddress} | Close-SmbSession -Force
}
Brondahl
  • 7,402
  • 5
  • 45
  • 74