В продолжение к посту «PSSession через обратный SSH туннель», написал два варианта скрипта для управления файлами и папками на удаленном хосте с графическим интерфейсом. Скрипт так же можно использовать и с обычной удаленной сессией Powershell.

Для работы скрипта предварительно необходимо создать сессию с удаленным хостом, затем запустить скрипт. Который в свою очередь проверит сессии, выберет открытую и отобразить форму. Что-то похожее есть в TeamViewer - «Передача файлов».

Вариант 1. Используется System.Windows.Forms

Этот вариант не стал доделывать, так как неудобно проектировать форму (фактический на ощупь) и сложную форму будет сделать очень тяжело.

PowerCommander.ps1:
```powershell
<#
 .SYNOPSIS
  Remote session file copy with GUI

 .DESCRIPTION


 .EXAMPLE


 .LINK
  https://webnote.satin-pl.com/


 .NOTES
  Version:        0.1
  Author:         Pavel Satin
  Email:          plsatin@yandex.ru
  Creation Date:  31.10.2018
  Purpose/Change: Initial script development
#>



$RemoteSession = (Get-PSSession | Where-Object State -eq "Opened").Name

$global:RemotePath = "C:\Users\"
$global:RemotePathPrev = "C:\"
$global:LocalPath = "C:\Temp\Downloads\"
$global:RemoteCopyPath = ""
$global:LocalCopyPath = ""

Add-Type -assembly System.Windows.Forms


function Copy-FromRemoteSession {
    param (
        [string]$RemoteSession,
        [string]$RemotePath,
        [string]$LocalPath
    )

    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession
    Copy-Item -FromSession $RemoteSessionObj -Path $RemotePath -Destination $LocalPath -Recurse

}

function Copy-ToRemoteSession {
    param (
        [string]$RemoteSession,
        [string]$LocalPath,
        [string]$RemotePath
    )

    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession
    Copy-Item -ToSession $RemoteSessionObj -Path $LocalPath -Destination $RemotePath -Recurse

}

function Update-RemoteListView {
    param (
        [string]$RemoteSession,
        [string]$RemotePath
    )


    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession

    $arrayFolders = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { Get-ChildItem -Path $using:RemotePath | ?{ $_.PSIsContainer }})
    $arrayFiles = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { Get-ChildItem -Path $using:RemotePath | ?{ ! $_.PSIsContainer }})


    $RemoteFilesListView.Items.Clear()
    $RemoteFilesListView.Items.Add("..", -1)

    foreach ( $obj in $arrayFolders ) {
        $ListViewItem = New-Object System.Windows.Forms.ListViewItem($obj.ToString())
        $ListViewItem.ImageIndex = 0
        $ListViewItem.Subitems.Add("0")
        $RemoteFilesListView.Items.Add($ListViewItem)

    }

    foreach ( $obj in $arrayFiles ) {
        $ListViewItem = New-Object System.Windows.Forms.ListViewItem($obj.ToString())
        $ListViewItem.ImageIndex = 1
        $ListViewItem.Subitems.Add($obj.Length)
        $RemoteFilesListView.Items.Add($ListViewItem)
    }


}

function Update-LocalListView {
    param (
        [string]$LocalPath
    )


    $arrayFolders = Get-ChildItem -Path $global:LocalPath | ?{ $_.PSIsContainer }
    $arrayFiles = Get-ChildItem -Path $global:LocalPath | ?{ ! $_.PSIsContainer }

    $LocalFilesListView.Items.Clear()
    $LocalFilesListView.Items.Add("..", -1)

    foreach ( $obj in $arrayFolders ) {
        $ListViewItem = New-Object System.Windows.Forms.ListViewItem($obj.ToString())
        $ListViewItem.ImageIndex = 0
        $ListViewItem.Subitems.Add("0")
        $LocalFilesListView.Items.Add($ListViewItem)

    }

    foreach ( $obj in $arrayFiles ) {
        $ListViewItem = New-Object System.Windows.Forms.ListViewItem($obj.ToString())
        $ListViewItem.ImageIndex = 1
        $ListViewItem.Subitems.Add($obj.Length)
        $LocalFilesListView.Items.Add($ListViewItem)
    }

}




$MainWindow  = New-Object System.Windows.Forms.Form
$DownloadButton = New-Object System.Windows.Forms.Button
$UploadButton = New-Object System.Windows.Forms.Button
$RefreshRemoteFiles = New-Object System.Windows.Forms.Button
$RemoteSessionTextBox = New-Object System.Windows.Forms.TextBox
$RemotePathTextBox = New-Object System.Windows.Forms.TextBox
$LocalPathTextBox = New-Object System.Windows.Forms.TextBox
$RemoteFilesListView = New-Object System.Windows.Forms.ListView
$LocalFilesListView = New-Object System.Windows.Forms.ListView


#region ImageList for nodes 
$global:imageList = new-Object System.Windows.Forms.ImageList
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 16
$System_Drawing_Size.Height = 16
$global:imageList.ImageSize = $System_Drawing_Size
$imageFolder = [System.Drawing.Image]::FromFile("C:\ProgramData\icinga2\scripts\icinga2\img\folder.ico")
$imageFile = [System.Drawing.Image]::FromFile("C:\ProgramData\icinga2\scripts\icinga2\img\file.ico")

$global:imageList.Images.Add("folder", $imageFolder)
$global:imageList.Images.Add("file", $imageFile)


$RemoteFilesListView_MouseDoubleClick = {
    $SelItem = $RemoteFilesListView.SelectedItems[0].SubItems[0].Text

    if ($SelItem -eq "..") {
        $global:RemotePath = Split-Path -Path $global:RemotePath
        Write-Host $global:RemotePath
        $RemotePathTextBox.Text = $global:RemotePath
        Update-RemoteListView $RemoteSession $global:RemotePath

    } else {
        $global:RemotePath = Join-Path -Path $RemotePath -ChildPath $SelItem
        Write-Host $global:RemotePath
        $RemotePathTextBox.Text = $global:RemotePath
        Update-RemoteListView $RemoteSession $global:RemotePath

    }

}

$RemoteFilesListView_MouseClick = {
    $SelItem = $RemoteFilesListView.SelectedItems[0].SubItems[0].Text
    if ($SelItem -eq "..") {

    } else {
        $global:RemoteCopyPath = Join-Path -Path $RemotePath -ChildPath $SelItem
    }

}

$LocalFilesListView_MouseDoubleClick = {
    $SelItem = $LocalFilesListView.SelectedItems[0].SubItems[0].Text

    if ($SelItem -eq "..") {
        $global:LocalPath = Split-Path -Path $global:LocalPath
        Write-Host $global:LocalPath
        $LocalPathTextBox.Text = $global:LocalPath
        Update-LocalListView $global:LocalPath

    } else {
        $global:LocalPath = Join-Path -Path  $global:LocalPath -ChildPath $SelItem
        Write-Host  $global:LocalPath
        $LocalPathTextBox.Text = $global:LocalPath
        Update-LocalListView $global:LocalPath

    }

}

$LocalFilesListView_MouseClick = {
    $SelItem = $LocalFilesListView.SelectedItems[0].SubItems[0].Text

    if ($SelItem -eq "..") {

    } else {
        $global:LocalCopyPath = Join-Path -Path $global:LocalPath -ChildPath $SelItem
    }

}



$MainWindow.StartPosition = "CenterScreen"
$MainWindow.Text = "PowerCommander"
$MainWindow.Width = 648
$MainWindow.Height = 560

$RemoteSessionTextBox.Location = New-Object System.Drawing.Point(10,10)
$RemoteSessionTextBox.Text = $RemoteSession
$RemoteSessionTextBox.Width = 180
$RemoteSessionTextBox.add_TextChanged({ $RemoteSession = $RemoteSessionTextBox.Text })
$RemoteSessionTextBox.TabIndex = 1

$RemotePathTextBox.Location = New-Object System.Drawing.Point(10,40)
$RemotePathTextBox.Text = $global:RemotePath
$RemotePathTextBox.Width = 300
$RemotePathTextBox.add_TextChanged({ $global:RemotePath = $RemotePathTextBox.Text })
$RemotePathTextBox.TabIndex = 2

$LocalPathTextBox.Location = New-Object System.Drawing.Point(320,40)
$LocalPathTextBox.Text = $global:LocalPath
$LocalPathTextBox.Width = 300
$LocalPathTextBox.add_TextChanged({ $global:LocalPath = $LocalPathTextBox.Text })
$LocalPathTextBox.TabIndex = 3


$RemoteFilesListView.Location = New-Object System.Drawing.Size(10,70) 
$RemoteFilesListView.Size = New-Object System.Drawing.Size(300,20) 
$RemoteFilesListView.Height = 380
$RemoteFilesListView.SmallImageList = $global:imageList
$RemoteFilesListView.View = "Details"
$RemoteFilesListView.Columns.Add("Name", -2) | Out-Null
$RemoteFilesListView.Columns.Add("Size") | Out-Null


$RemoteFilesListView.add_MouseDoubleClick($RemoteFilesListView_MouseDoubleClick)
$RemoteFilesListView.add_MouseClick($RemoteFilesListView_MouseClick)

$LocalFilesListView.Location = New-Object System.Drawing.Size(320,70) 
$LocalFilesListView.Size = New-Object System.Drawing.Size(300,20) 
$LocalFilesListView.Height = 380
$LocalFilesListView.SmallImageList = $global:imageList
$LocalFilesListView.View = "Details"
$LocalFilesListView.Columns.Add("Name", -2) | Out-Null
$LocalFilesListView.Columns.Add("Size") | Out-Null


$LocalFilesListView.add_MouseDoubleClick($LocalFilesListView_MouseDoubleClick)
$LocalFilesListView.add_MouseClick($LocalFilesListView_MouseClick)


$DownloadButton.add_click({ $RemoteSession = $RemoteSessionTextBox.Text; $global:LocalPath = $LocalPathTextBox.Text; Copy-FromRemoteSession $RemoteSession $global:RemoteCopyPath $global:LocalPath} )

$DownloadButton.Location = New-Object System.Drawing.Point(10,470)
$DownloadButton.Autosize = 1
$DownloadButton.TabIndex = 4
$DownloadButton.Text = "Download"



$UploadButton.add_click({ $RemoteSession = $RemoteSessionTextBox.Text; Copy-ToRemoteSession $RemoteSession $global:LocalCopyPath $global:RemotePath} )

$UploadButton.Location = New-Object System.Drawing.Point(320,470)
$UploadButton.Autosize = 1
$UploadButton.TabIndex = 4
$UploadButton.Text = "Upload"


$RefreshRemoteFiles.add_click({ $RemoteSession  = $RemoteSessionTextBox.Text; $global:RemotePath = $RemotePathTextBox.Text; Update-RemoteListView $RemoteSession $global:RemotePath })

$RefreshRemoteFiles.Location = New-Object System.Drawing.Point(196,10)
$RefreshRemoteFiles.Autosize = 1
$RefreshRemoteFiles.TabIndex = 5
$RefreshRemoteFiles.Text = "Update remote files"


$MainWindow.Controls.Add($RemoteSessionTextBox)
$MainWindow.Controls.Add($RemotePathTextBox)
$MainWindow.Controls.Add($LocalPathTextBox)
$MainWindow.Controls.Add($DownloadButton)
$MainWindow.Controls.Add($UploadButton)
$MainWindow.Controls.Add($RefreshRemoteFiles)
$MainWindow.Controls.Add($RemoteFilesListView)
$MainWindow.Controls.Add($LocalFilesListView)


Update-LocalListView $global:LocalPath | Out-Null


$MainWindow.ShowDialog() | Out-Null


```
Вариант 2. Используется Windows Presentation Foundation

В этом варианте для проектрованиия формы используется Visual Studio, после чего готовый XAML копируется в скрипт.

PowerCommanderXAML.ps1:
```powershell

<#
 .SYNOPSIS
  Remote session file copy with GUI (WPF)

 .DESCRIPTION


 .EXAMPLE


 .LINK
  https://webnote.satin-pl.com/2018/11/01/posh_over_ssh_tunnel_part2/


 .NOTES
  Version:        0.3
  Author:         Pavel Satin
  Email:          plsatin@yandex.ru
  Creation Date:  06.11.2018
  Purpose/Change: Initial script development
#>
param (
    [Parameter(Mandatory = $false)]
    [string]$RemoteSession0 = ""
)


## Значения по умолчанию

$RemoteSession = (Get-PSSession | Where-Object State -eq "Opened").Name

$global:RemotePath = "C:\Users\"
$global:RemotePathPrev = "C:\"
$global:LocalPath = "C:\Temp\Downloads\"
$global:RemoteCopyPath = ""
$global:LocalCopyPath = ""
$global:computerOS = $null


$imageFolder = "C:\ProgramData\icinga2\scripts\icinga2\img\folder-16_office.png"
$imageFile =  "C:\ProgramData\icinga2\scripts\icinga2\img\document-16_office.png"



$inputXML = @"
<Window x:Class="PowerCommander.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PowerCommander"
        mc:Ignorable="d"
        Title="PowerCommander" Height="482.863" Width="796.285" ResizeMode="NoResize">
    <Grid HorizontalAlignment="Left" Width="789">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="396*"/>
            <ColumnDefinition Width="396*"/>
        </Grid.ColumnDefinitions>
        <ListView x:Name="RemoteFilesListView" HorizontalAlignment="Left" Height="270" Margin="10,123,0,0" VerticalAlignment="Top" Width="376" SelectionMode="Single">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header=" ">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <Image Width="16" Height="16" Source="{Binding Image}" />
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Name" Width="260" DisplayMemberBinding ="{Binding 'Name'}"/>
                    <GridViewColumn Header="Size" DisplayMemberBinding ="{Binding 'Size'}"/>
                </GridView>
            </ListView.View>
        </ListView>
        <Button x:Name="DownloadButton" Content="Download &gt;&gt;" HorizontalAlignment="Left" Margin="280,398,0,0" VerticalAlignment="Top" Width="106" Height="23"/>
        <ListView x:Name="LocalFilesListView" HorizontalAlignment="Left" Height="270" Margin="0,123,0,0" VerticalAlignment="Top" Width="376" Grid.Column="1">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header=" ">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <Image Width="16" Height="16" Source="{Binding Image}" />
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Name" Width="260" DisplayMemberBinding ="{Binding 'Name'}"/>
                    <GridViewColumn Header="Size" DisplayMemberBinding ="{Binding 'Size'}"/>
                </GridView>
            </ListView.View>
        </ListView>
        <Button x:Name="UploadButton" Content="&lt;&lt; Upload" HorizontalAlignment="Left" Margin="0,398,0,0" VerticalAlignment="Top" Width="106" RenderTransformOrigin="-3.079,0.131" Grid.Column="1" Height="23"/>
        <TextBox x:Name="RemoteSessionTextBox" HorizontalAlignment="Left" Height="23" Margin="69,9,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="146"/>
        <Button x:Name="RefreshRemoteFilesButton" Content="" HorizontalAlignment="Left" Margin="362,91,0,0" VerticalAlignment="Top" Width="23" Height="23">
            <Button.Background>
                <ImageBrush ImageSource="C:\ProgramData\icinga2\scripts\icinga2\img\restart.png"/>
            </Button.Background>
        </Button>
        <TextBox x:Name="RemotePathTextBox" HorizontalAlignment="Left" Height="23" Margin="10,91,0,0" TextWrapping="Wrap" Text=" " VerticalAlignment="Top" Width="347"/>
        <TextBox x:Name="LocalPathTextBox" HorizontalAlignment="Left" Height="23" Margin="0,91,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="347" Grid.Column="1"/>
        <Label x:Name="RemoteHostLabel" Content="Remote host" HorizontalAlignment="Left" Margin="51,60,0,0" VerticalAlignment="Top" Height="26" Width="334"/>
        <Label x:Name="LocalHostLabel" Content="Local host" HorizontalAlignment="Left" Margin="39,60,0,0" VerticalAlignment="Top" Grid.Column="1" Height="26" Width="337"/>
        <Image x:Name="LocalHostImage" HorizontalAlignment="Left" Height="26" Margin="0,60,0,0" VerticalAlignment="Top" Width="34" Source="C:\ProgramData\icinga2\scripts\icinga2\img\workstation_32_gray.png" Grid.Column="1"/>
        <Image x:Name="RemoteHostImage" HorizontalAlignment="Left" Height="26" Margin="12,60,0,0" VerticalAlignment="Top" Width="34" Source="C:\ProgramData\icinga2\scripts\icinga2\img\workstation_32.png"/>
        <Label x:Name="SessionLabel" Content="Session:" HorizontalAlignment="Left" Margin="12,8,0,0" VerticalAlignment="Top" Height="26" Width="52"/>
        <Button x:Name="RemoteHostAddFolderButton" Content="" HorizontalAlignment="Left" Margin="12,398,0,0" VerticalAlignment="Top" Width="23" Height="23">
            <Button.Background>
                <ImageBrush ImageSource="C:\ProgramData\icinga2\scripts\icinga2\img\add-folder-32.png"/>
            </Button.Background>
        </Button>
        <Button x:Name="RemoteHostDeleteButton" Content="" HorizontalAlignment="Left" Margin="41,398,0,0" VerticalAlignment="Top" Width="23" Height="23">
            <Button.Background>
                <ImageBrush ImageSource="C:\ProgramData\icinga2\scripts\icinga2\img\delete-32.png"/>
            </Button.Background>
        </Button>
        <Button x:Name="RefreshLocalFilesButton" Content="" HorizontalAlignment="Left" Margin="353,91,0,0" VerticalAlignment="Top" Width="23" Height="23" Grid.Column="1">
            <Button.Background>
                <ImageBrush ImageSource="C:\ProgramData\icinga2\scripts\icinga2\img\restart.png"/>
            </Button.Background>
        </Button>
        <Button x:Name="LocalHostAddFolderButton" Content="" HorizontalAlignment="Left" Margin="324,398,0,0" VerticalAlignment="Top" Width="23" Height="23" Grid.Column="1">
            <Button.Background>
                <ImageBrush ImageSource="C:\ProgramData\icinga2\scripts\icinga2\img\add-folder-32.png"/>
            </Button.Background>
        </Button>
        <Button x:Name="LocalHostDeleteButton" Content="" HorizontalAlignment="Left" Margin="353,398,0,0" VerticalAlignment="Top" Width="23" Height="23" Grid.Column="1">
            <Button.Background>
                <ImageBrush ImageSource="C:\ProgramData\icinga2\scripts\icinga2\img\delete-32.png"/>
            </Button.Background>
        </Button>

    </Grid>
</Window>
"@ 



$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML

# Читаем XAML
$reader = (New-Object System.Xml.XmlNodeReader $xaml)

try {
    $Form = [Windows.Markup.XamlReader]::Load( $reader )
} catch {
    Write-Warning "Unable to parse XML, with error: $($Error[0])`n Ensure that there are NO SelectionChanged or TextChanged properties (PowerShell cannot process them)"
    throw
}

# Загружаем XAML объекты в PowerShell
$xaml.SelectNodes("//*[@Name]") | ForEach-Object {
    Write-Verbose "trying item $($_.Name)"
    try {
        Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -ErrorAction Stop
    } catch {
        throw
    }
}

Function Get-FormVariables {
    if ($global:ReadmeDisplay -ne $true) {
        Write-Verbose "If you need to reference this display again, run Get-FormVariables"
        $global:ReadmeDisplay=$true
    }
    Write-Verbose "Found the following interactable elements from our form"
    get-variable WPF*
}

$FormVariables = Get-FormVariables | Out-String
Write-Verbose $FormVariables





## ===========================================================================
## Пишем логику

function Get-RemoteHostEnv {
    param (
        [string]$RemoteSession
    )

    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession
    $envComputerName = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { get-childitem -path env:computername })
    $global:computerOS = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { Get-WmiObject Win32_OperatingSystem -ErrorAction SilentlyContinue })


    Write-Verbose "Connected to host: $($envComputerName.Value)"
    $WPFRemoteHostLabel.Content = "Remote host: " + $envComputerName.Value
    $WPFRemoteHostImage.ToolTip = "$($global:computerOS.Caption) ($($global:computerOS.OSArchitecture)) $($global:computerOS.Version)"

}


function Copy-FromRemoteSession {
    param (
        [string]$RemoteSession,
        [string]$RemotePath,
        [string]$LocalPath
    )

    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession
    Copy-Item -FromSession $RemoteSessionObj -Path $RemotePath -Destination $LocalPath -Recurse

    ## Обновляем список локальных фалов после даунлоада
    Update-LocalListView $global:LocalPath
}

function Copy-ToRemoteSession {
    param (
        [string]$RemoteSession,
        [string]$LocalPath,
        [string]$RemotePath
    )

    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession
    Copy-Item -ToSession $RemoteSessionObj -Path $LocalPath -Destination $RemotePath -Recurse

    ## Обновляем список файлов на удаленном хосте после аплоада
    Update-RemoteListView $RemoteSession $global:RemotePath
}

function Update-RemoteListView {
    param (
        [string]$RemoteSession,
        [string]$RemotePath
    )

    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession

    ## Передать локальный параметр в ScriptBlock можно с помощью дерективы ($using:)

    $arrayFolders = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { Get-ChildItem -Path $using:RemotePath | Where-Object{ $_.PSIsContainer } | Select-Object Name })
    $arrayFiles = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { Get-ChildItem -Path $using:RemotePath | Where-Object{ ! $_.PSIsContainer } | Select-Object Name, Length })

    $WPFRemoteFilesListView.Items.Clear()
    $WPFRemoteFilesListView.Items.Add([pscustomobject]@{Name = ".."; Size = ""})

    foreach ( $obj in $arrayFolders ) {
        $WPFRemoteFilesListView.Items.Add([pscustomobject]@{Image = $imageFolder; Name = $obj.Name; Size = "0"})
    }

    foreach ( $obj in $arrayFiles ) {
        $WPFRemoteFilesListView.Items.Add([pscustomobject]@{Image = $imageFile; Name = $obj.Name; Size = $obj.Length})
    }

    # $WPFRemotePathTextBox.Text = $global:RemotePath
    $WPFRemotePathTextBox.ToolTip = $global:RemotePath

}

function Update-LocalListView {
    param (
        [string]$LocalPath
    )

    $arrayFolders = Get-ChildItem -Path $global:LocalPath | Where-Object{ $_.PSIsContainer } | Select-Object Name
    $arrayFiles = Get-ChildItem -Path $global:LocalPath | Where-Object{ ! $_.PSIsContainer } | Select-Object Name, Length

    $WPFLocalFilesListView.Items.Clear()
    $WPFLocalFilesListView.Items.Add([pscustomobject]@{Name = ".."; Size = ""})

    foreach ( $obj in $arrayFolders ) {
        $WPFLocalFilesListView.Items.Add([pscustomobject]@{Image = $imageFolder; Name = $obj.Name; Size = "0"})
    }

    foreach ( $obj in $arrayFiles ) {
        $WPFLocalFilesListView.Items.Add([pscustomobject]@{Image = $imageFile; Name = $obj.Name; Size = $obj.Length})
    }

    # $WPFLocalPathTextBox.Text = $global:LocalPath
    $WPFLocalPathTextBox.ToolTip = $global:LocalPath


}

function Remove-RemoteFile {
    param (
        [string]$RemoteSession,
        [string]$RemotePath
    )

    $SelItem = $WPFRemoteFilesListView.SelectedItems.Name
    $global:RemoteCopyPath = Join-Path -Path $RemotePath -ChildPath $SelItem
    Write-Verbose "Remove [$RemoteSession]$global:RemoteCopyPath"

    Add-Type -Assembly PresentationFramework
    $msgBoxInput =  [System.Windows.MessageBox]::Show('Would you like delete item?','Confirm','YesNoCancel','Warning')

    switch  ($msgBoxInput) {
        'Yes' {
            $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession

            $removeItem = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { Remove-Item -Path $using:RemoteCopyPath -Force -Recurse })

            Update-RemoteListView $RemoteSession $global:RemotePath
        }
        'No' {
            ## Do something
        }
        'Cancel' {
            ## Do something
        }
    }

}

function New-RemoteFolder {
    param (
        [string]$RemoteSession,
        [string]$RemotePath
    )

    Write-Verbose "New folder in [$RemoteSession]$global:RemotePath"

    Add-Type -AssemblyName Microsoft.VisualBasic
    $newRemoteFolder = [Microsoft.VisualBasic.Interaction]::InputBox('Enter a new folder name', 'New folder', "New folder")

    $global:RemoteCopyPath = Join-Path -Path $RemotePath -ChildPath $newRemoteFolder

    $RemoteSessionObj = Get-PSSession | Where-Object Name -eq $RemoteSession

    $newItem = @(Invoke-Command -Session $RemoteSessionObj -ScriptBlock { New-Item -ItemType directory -Path $using:RemoteCopyPath })

    Update-RemoteListView $RemoteSession $global:RemotePath

}

function Remove-LocalFile {
    param (
        [string]$LocalPath
    )

    $SelItem = $WPFLocalFilesListView.SelectedItems.Name
    $global:LocalCopyPath = Join-Path -Path $LocalPath -ChildPath $SelItem
    Write-Verbose "Remove $global:LocalCopyPath"

    Add-Type -Assembly PresentationFramework
    $msgBoxInput =  [System.Windows.MessageBox]::Show('Would you like delete item?','Confirm','YesNoCancel','Warning')

    switch  ($msgBoxInput) {
        'Yes' {

            Remove-Item -Path $global:LocalCopyPath -Force -Recurse
            Update-LocalListView $global:LocalPath
        }
        'No' {
            ## Do something
        }
        'Cancel' {
            ## Do something
        }
    }

}

function New-LocalFolder {
    param (
        [string]$LocalPath
    )

    Write-Verbose "New folder in $global:LocalPath"

    Add-Type -AssemblyName Microsoft.VisualBasic
    $newLocalFolder = [Microsoft.VisualBasic.Interaction]::InputBox('Enter a new folder name', 'New folder', "New folder")

    $global:LocalCopyPath = Join-Path -Path $LocalPath -ChildPath $newLocalFolder
    $newItem = New-Item -ItemType directory -Path $global:LocalCopyPath
    Update-LocalListView $global:LocalPath

}


## Добавляем события контролам

$RemoteFilesListView_MouseDoubleClick = {
    $SelItem = $WPFRemoteFilesListView.SelectedItems.Name

    if ($SelItem -eq "..") {
        $global:RemotePath = Split-Path -Path $global:RemotePath
        Write-Verbose $global:RemotePath
        $WPFRemotePathTextBox.Text = $global:RemotePath
        Update-RemoteListView $RemoteSession $global:RemotePath

    } else {
        $global:RemotePath = Join-Path -Path $RemotePath -ChildPath $SelItem
        Write-Verbose $global:RemotePath
        $WPFRemotePathTextBox.Text = $global:RemotePath
        Update-RemoteListView $RemoteSession $global:RemotePath
    }

}

$LocalFilesListView_MouseDoubleClick = {
    $SelItem = $WPFLocalFilesListView.SelectedItems.Name

    if ($SelItem -eq "..") {
        $global:LocalPath = Split-Path -Path $global:LocalPath
        Write-Verbose $global:LocalPath
        $WPFLocalPathTextBox.Text = $global:LocalPath
        Update-LocalListView $global:LocalPath

    } else {
        $global:LocalPath = Join-Path -Path  $global:LocalPath -ChildPath $SelItem
        Write-Verbose  $global:LocalPath
        $WPFLocalPathTextBox.Text = $global:LocalPath
        Update-LocalListView $global:LocalPath
    }

}

$DownloadButton_Click = {
    Write-Verbose "Download start ..."
    $SelItem = $WPFRemoteFilesListView.SelectedItems.Name
    if ($SelItem -eq "..") {

    } else {
        $global:RemoteCopyPath = Join-Path -Path $RemotePath -ChildPath $SelItem
        Write-Verbose "Download [$RemoteSession]$global:RemoteCopyPath to $global:LocalPath"
        Copy-FromRemoteSession $RemoteSession $global:RemoteCopyPath $global:LocalPath

        Update-LocalListView $global:LocalPath | Out-Null
    }

}

$UploadButton_Click = {
    Write-Verbose "Upload start ..."
    $SelItem = $WPFLocalFilesListView.SelectedItems.Name

    if ($SelItem -eq "..") {

    } else {
        $global:LocalCopyPath = Join-Path -Path $global:LocalPath -ChildPath $SelItem
        Write-Verbose "Upload $global:LocalCopyPath to [$RemoteSession]$global:RemotePath"
        Copy-ToRemoteSession $RemoteSession $global:LocalCopyPath $global:RemotePath

        Update-RemoteListView $RemoteSession $global:RemotePath
    }

}

$RemotePathTextBox_KeyDown = {
    param
    (
      [Parameter(Mandatory)][Object]$sender,
      [Parameter(Mandatory)][Windows.Input.KeyEventArgs]$e
    )

    if($e.Key -eq "Enter") {
        Write-Verbose $global:RemotePath
        $WPFRemotePathTextBox.Text = $global:RemotePath
        Update-RemoteListView $RemoteSession $global:RemotePath
    }

}

$LocalPathTextBox_KeyDown = {
    param
    (
      [Parameter(Mandatory)][Object]$sender,
      [Parameter(Mandatory)][Windows.Input.KeyEventArgs]$e
    )

    if($e.Key -eq "Enter") {
        Write-Verbose  $global:LocalPath
        $WPFLocalPathTextBox.Text = $global:LocalPath
        Update-LocalListView $global:LocalPath
    }

}



$WPFRemoteFilesListView.Add_MouseDoubleClick($RemoteFilesListView_MouseDoubleClick)
$WPFLocalFilesListView.Add_MouseDoubleClick($LocalFilesListView_MouseDoubleClick)
$WPFRefreshRemoteFilesButton.Add_Click({ Update-RemoteListView $RemoteSession $global:RemotePath })
$WPFDownloadButton.Add_Click($DownloadButton_Click)
$WPFUploadButton.Add_Click($UploadButton_Click)
$WPFRemotePathTextBox.Add_TextChanged({ $global:RemotePath = $WPFRemotePathTextBox.Text })
$WPFLocalPathTextBox.Add_TextChanged({ $global:LocalPath = $WPFLocalPathTextBox.Text })
$WPFRemoteHostDeleteButton.Add_Click({ Remove-RemoteFile $RemoteSession $global:RemotePath })
$WPFRemoteHostAddFolderButton.Add_Click({ New-RemoteFolder $RemoteSession $global:RemotePath })
$WPFRemotePathTextBox.Add_KeyDown($RemotePathTextBox_KeyDown)
$WPFLocalPathTextBox.Add_KeyDown($LocalPathTextBox_KeyDown)
$WPFRefreshLocalFilesButton.Add_Click({ Update-LocalListView $global:LocalPath })
$WPFLocalHostDeleteButton.Add_Click({ Remove-LocalFile $global:LocalPath })
$WPFLocalHostAddFolderButton.Add_Click({ New-LocalFolder $global:LocalPath })


## Заполняем поля ввода данными по умолчанию
$WPFRemoteSessionTextBox.Text = $RemoteSession
$WPFLocalPathTextBox.Text = $global:LocalPath
$WPFRemotePathTextBox.Text = $global:RemotePath

## Обновляем список локальных файлов
Update-LocalListView $global:LocalPath | Out-Null

Get-RemoteHostEnv $RemoteSession

Write-Verbose "To show the form, run the following"
Write-Verbose '$Form.ShowDialog() | out-null'

## Показываем форму
$Form.ShowDialog() | out-null


```

Ссылки