PowerShell Server 2016
PowerShell Server 2016
Questions / Feedback?

SFTP Scripting

By default, the SFTP Server will act as a standard SFTP server and provide file management functionality for the specified root directory. In some cases, it may be desirable to implement advanced functionality. PowerShell Server provides an advanced SFTP scripting technique where a PowerShell script can be used to customize the SFTP functions.

This functionality can be enabled by pointing the SFTP Root Directory option on the Other tab to a PowerShell script.

Required Functions

The following functions must be implemented in the specified PowerShell script to control the corresponding SFTP functionality. Note that the below examples exhibit default functionality, and can be modified to suit your implementation's specific needs.

The following examples make use of a $sftpRoot variable and these helper functions:

  $sftpRoot = "C:\temp"
 
  function Get-UnixTime($time) {
    return [long]($time - [DateTime]'1970/01/01 12:00:00 AM').TotalSeconds
  }
  function Resolve-SFTPPath($vpath) {
    return [IO.Path]::Combine($sftpRoot, $vpath.Substring(1))
  }

Confirm-DirList - Called when listing the contents of a directory.

  function Confirm-DirList($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: directory virtual path
  # out:
  # $sftpArgs.statusCode: operation result
  # $sftpArgs.fileList: string[] with just filenames
    $path = Resolve-SFTPPath $sftpArgs.path
    if ( -not (test-path $path) ) {
      $sftpArgs.statusCode = $SSH_FXS_NO_SUCH_PATH
      return
    }
    $sftpArgs.fileList = Get-ChildItem $path | %{
      $_.Name
    }
    $sftpArgs.statusCode = $SSH_FXS_OK
  }

Confirm-DirCreate - Called when creating a directory.

  function Confirm-DirCreate($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: directory virtual path
  # $sftpArgs.attrs: directory attributes
  # out:
  # $sftpArgs.statusCode: operation result
    $sftpArgs.statusCode = $SSH_FXS_OK
    $path = Resolve-SFTPPath $sftpArgs.path
    New-Item -Path $path -ItemType Directory
  }

Confirm-DirRemove - Called when removing a directory.

  function Confirm-DirRemove($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: directory virtual path
  # out:
  # $sftpArgs.statusCode: operation result
    $sftpArgs.statusCode = $SSH_FXS_OK
    $path = Resolve-SFTPPath $sftpArgs.path
    Remove-Item -Path $path -force
  }

Confirm-FileOpen - Called when opening a file.

  function Confirm-FileOpen($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: file virtual path
  # $sftpArgs.desiredAccess: desired file access
  # $sftpArgs.flags: file open flags
  # $sftpArgs.attrs: file attributes
  # out:
  # $sftpArgs.statusCode: operation result
  # $sftpArgs.physicalPath: physical path to file the server will handle
    $physicalPath = Resolve-SFTPPath $sftpArgs.path
    $flags = $sftpArgs.flags
    if ( -not ($flags -band $SSH_V3_FXF_CREAT) ) {
      # opening existing file
      if ( -not (test-path $physicalPath) ) {
        $sftpArgs.statusCode = $SSH_FXS_NO_SUCH_FILE;
        return
      }
    } else {
      # creating a new file
      if ( -not (test-path $physicalPath) ) {
        New-Item -Path $physicalPath -ItemType File
      }
    }
    $sftpArgs.physicalPath = $physicalPath
    $sftpArgs.statusCode = $SSH_FXS_OK
  }

Confirm-FileClose - Called when closing a file.

  function Confirm-FileClose($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: file or directory virtual path
  # $sftpArgs.statusCode: operation result
  # $sftpArgs.physicalPath: physical path of the opened file
  # you could for example grab the contents here and delete it
    $sftpArgs.statusCode = $SSH_FXS_OK
  }

Confirm-FileRemove - Called when removing a file.

  function Confirm-FileRemove($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: file virtual path
  # out:
  # $sftpArgs.statusCode: operation result
    $sftpArgs.statusCode = $SSH_FXS_OK
    $path = Resolve-SFTPPath $sftpArgs.path
    if ( -not (test-path $path) ) {
      $sftpArgs.statusCode = $SSH_FXS_NO_SUCH_PATH
      return
    }
    Remove-Item $path
  }

Confirm-FileRename - Called when renaming a file.

  function Confirm-FileRename($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: original file virtual path
  # $sftpArgs.newPath: new file virtual path
  # out:
  # $sftpArgs.statusCode: operation result
    $sftpArgs.statusCode = $SSH_FXS_OK
    $path = Resolve-SFTPPath $sftpArgs.path
    if ( -not (test-path $path) ) {
      $sftpArgs.statusCode = $SSH_FXS_NO_SUCH_PATH
      return
    }
    $newPath = Resolve-SFTPPath $sftpArgs.newPath
    Write-Debug -Message "Moving $path to $newPath"
    Move-Item $path $newPath
  }

Confirm-GetAttributes - Called when retrieving a file's attributes.

  function Confirm-GetAttributes($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: directory virtual path
  # $sftpArgs.flags: flags for this operation
  # $sftpArgs.attrs: file attributes to return, as a hashtable
  # out:
  # $sftpArgs.statusCode: operation result
    $sftpArgs.statusCode = $SSH_FXS_OK
    $path = Resolve-SFTPPath $sftpArgs.path
    if ( -not (test-path $path) ) {
      $sftpArgs.statusCode = $SSH_FXS_NO_SUCH_PATH
      return
    }
    $file = Get-ItemProperty $path
    $acl = Get-ACL $path
    $attrs = $sftpArgs.attrs
    $attrs.creationTime = Get-UnixTime($file.CreationTimeUtc)
    $attrs.isDir = $file.PSIsContainer
    $attrs.modifiedTime = Get-UnixTime($file.LastWriteTimeUtc)
    $attrs.accessTime = Get-UnixTime($file.LastAccessTimeUtc)
    $attrs.size = $file.Length
    $attrs.ownerId = $acl.Owner
    $attrs.groupId = $acl.Group
  }

Confirm-SetAttributes - Called when setting a file's attributes.

  function Confirm-SetAttributes($sftpArgs) {
  # $sftpArgs.connectionId: connection id
  # $sftpArgs.user: username
  # $sftpArgs.path: file virtual path
  # $sftpArgs.attrs: file attributes
  # out:
  # $sftpArgs.statusCode: operation result
    $sftpArgs.statusCode = $SSH_FXS_OK
  }

Error Codes

The following SFTP Error Codes may be useful if you need to return an error from one of the above functions:

  $SSH_FXS_OK = 0
  $SSH_FXS_EOF = 1
  $SSH_FXS_NO_SUCH_FILE = 2
  $SSH_FXS_PERMISSION_DENIED = 3
  $SSH_FXS_FAILURE = 4
  $SSH_FXS_BAD_MESSAGE = 5
  $SSH_FXS_NO_CONNECTION = 6
  $SSH_FXS_CONNECTION_LOST = 7
  $SSH_FXS_OP_UNSUPPORTED = 8
  $SSH_FXS_INVALID_HANDLE = 9
  $SSH_FXS_NO_SUCH_PATH = 10
  $SSH_FXS_FILE_ALREADY_EXISTS = 11
  $SSH_FXS_WRITE_PROTECT = 12
  $SSH_FXS_NO_MEDIA = 13
  $SSH_FXS_NO_SPACE_ON_FILESYSTEM = 14
  $SSH_FXS_QUOTA_EXCEEDED = 15
  $SSH_FXS_UNKNOWN_PRINCIPAL = 16
  $SSH_FXS_LOCK_CONFLICT = 17
  $SSH_FXS_DIR_NOT_EMPTY = 18
  $SSH_FXS_NOT_A_DIRECTORY = 19
  $SSH_FXS_INVALID_FILENAME = 20
  $SSH_FXS_LINK_LOOP = 21
  $SSH_FXS_CANNOT_DELETE = 22
  $SSH_FXS_INVALID_PARAMETER = 23
  $SSH_FXS_FILE_IS_A_DIRECTORY = 24
  $SSH_FXS_BYTE_RANGE_LOCK_CONFLICT = 25
  $SSH_FXS_BYTE_RANGE_LOCK_REFUSED = 26
  $SSH_FXS_DELETE_PENDING = 27
  $SSH_FXS_FILE_CORRUPT = 28
  $SSH_FXS_OWNER_INVALID = 29
  $SSH_FXS_GROUP_INVALID = 30
  $SSH_FXS_NO_MATCHING_BYTE_RANGE_LOCK = 31
 
  # File open flags
  $SSH_V3_FXF_READ = 0x00000001
  $SSH_V3_FXF_WRITE = 0x00000002
  $SSH_V3_FXF_APPEND = 0x00000004
  $SSH_V3_FXF_CREAT = 0x00000008
  $SSH_V3_FXF_TRUNC = 0x00000010
  $SSH_V3_FXF_EXCL = 0x00000020
  $SSH_V4_FXF_TEXT = 0x00000040

 
 
Copyright (c) 2017 /n software inc. - All rights reserved.
PowerShell Server 2016 - Version 16.0 [Build 6240]