Using POSH-SSH in a scheduled task

I have a script that connects in to an SFTP site using stored credentials. It copies a file locally, then disconnects from the site and connects to a second SFTP site and uploads the file to that site.

The code works perfectly when I execute it as myself logged into the server as a domain admin.

I have the script setup to run as a scheduled task. When the task runs it starts the script. The problem I am experiencing is that the script seems to be unable to establish the remote server session via SFTP. I added code to the script to send me an email when it starts, and had it grab module information to verify that the POSH-SSH module was properly running and it checks out OK. So I added code to send me the session.host name. This comes back as blank, so it seems the issue is when running as a scheduled task the session cannot establish. Can anyone let me know if they see a problem in the code?

$ErrorActionPreference = “SilenetlyContinue”
#==========================================================================

NAME: SFTPCopy.ps1

AUTHOR: Mark D. MacLachlan, Element Payment Services

DATE : 11/30/2015 10:29:51

This code is copyright (c) 2015 Element Payment Services.

All rights reserved.

THIS CODE AND INFORMATION IS PROVIDED “AS IS” WITHOUT WARRANTY OF

ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO

THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A

PARTICULAR PURPOSE.

IN NO EVENT SHALL ELEMENT PAYMENT SERVICES AND/OR ITS RESPECTIVE

SUPPLIERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES

OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,

WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS

ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE

OF THIS CODE OR INFORMATION.

COMMENT:

This script requires the PowerShell SFTP Add on available free on GitHub.

To install the SFTP Add on run the following command within an elevated PowerShell command.

wget https://gist.github.com/darkoperator/6152630/raw/c67de4f7cd780ba367cccbc2593f38d18ce6df89/instposhsshdev | iex

#==========================================================================

Get the ID and security principal of the current user account

$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

Get the security principal for the Administrator role

$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

Check to see if we are currently running “as Administrator”

if ($myWindowsPrincipal.IsInRole($adminRole))
{

We are running “as Administrator” - so change the title and background color to indicate this

$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + “(Elevated)”
$Host.UI.RawUI.BackgroundColor = “DarkBlue”
clear-host
}
else
{

We are not running “as Administrator” - so relaunch as administrator

Create a new process object that starts PowerShell

$newProcess = new-object System.Diagnostics.ProcessStartInfo “PowerShell”;

Specify the current script path and name as a parameter

$newProcess.Arguments = $myInvocation.MyCommand.Definition;

Indicate that the process should be elevated

$newProcess.Verb = “runas”;

Start the new process

Exit from the current, unelevated, process

exit
}

Run your code that needs to be elevated here

#Import the SFTP Module & CmdLets
Set-Location "C:\Program Files\WindowsPowerShell\Modules"
Import-Module posh-ssh

#Get our stored credentials
$encryptedSource = Get-Content C:\PSScripts\EncryptedELPSSFTP.txt | ConvertTo-SecureString
$CredSource = New-Object System.Management.Automation.PsCredential(“ELPSSFTP”, $encryptedSource)

#Get yesterdays date for our file
$FileDate = (Get-Date).AddDays(-1)
$FileDate = $FileDate.ToString(“yyyyMMdd”)
#Get date for our file 2 days ago
$FileDate2 = (Get-Date).AddDays(-2)
$FileDate2 = $FileDate2.ToString(“yyyyMMdd”)
#Get date for our file 3 days ago
$FileDate3 = (Get-Date).AddDays(-3)
$FileDate3 = $FileDate3.ToString(“yyyyMMdd”)

#Create a session to get the files
$Session = New-SFTPSession -ComputerName “mmm.ftpsvr.com” -Credential $CredSource -AcceptKey

#Send an email the process has started
#Email the list
$Body = “SFTP Script Started `r`n $Session.Host”
$Subject = “SFTP Upload Started”
$SMTP = “phx1util01.company.prod”
$To = “mmaclachlan@company.com
$From = “SFTP@company.com
Send-MailMessage -To $To -From $From -SmtpServer $SMTP -BodyAsHTML $Body -Subject $Subject

#Download the files
Get-SFTPFile -SFTPSession $Session -RemoteFile “/Inbox/FOTO_P0BCD71D_$FileDate” -LocalPath "C:\Temp\SFTP"
Get-SFTPFile -SFTPSession $Session -RemoteFile “/Inbox/FOTO_P0BCD71D_$FileDate2” -LocalPath "C:\Temp\SFTP"
Get-SFTPFile -SFTPSession $Session -RemoteFile “/Inbox/FOTO_P0BCD71D_$FileDate3” -LocalPath "C:\Temp\SFTP"

#Remove the session
Get-SFTPsession | Remove-SFTPSession

#Create a session to put our file up
$encryptedDest = Get-Content C:\PSScripts\EncryptedCoreCMS_SFTP.txt | ConvertTo-SecureString
$CredDest = New-Object System.Management.Automation.PsCredential(“corecms”, $encryptedDest)
$Session2 = New-SFTPSession -ComputerName phx1sftp01.company.prod -Credential $CredDest -AcceptKey -Port 2222

#Specify the local files
Set-Location "C:\Temp\SFTP"
$LocalFiles = Get-ChildItem "C:\Temp\SFTP"

#Now copy the files up
ForEach ($LocalFile in $LocalFiles)
{
Set-SFTPFile -SessionId 0 -LocalFile “$LocalFile” -RemotePath “/1020380/”
}

#Create a list of uploaded files
$Files = (Get-SFTPChildItem -SessionId 0 -Path /1020380) | Select FullName | Sort-Object FullName
#Email the list
$Body = $Files.FullName -Join “`r`n”
$Subject = “$FileDate SFTP Upload Report”
Send-MailMessage -To $To -From $From -SmtpServer $SMTP -BodyAsHTML $Body -Subject $Subject

#Remove the session
Get-SFTPsession | Remove-SFTPSession

#Remove the local files
ForEach ($LocalFile in $LocalFiles)
{
Remove-Item $LocalFile -Force
}

Well, it’s probably not the script if it works fine when run interactively. Basic troubleshooting: you’ve more or less eliminated the script as a problem, since it works fine in one scenario.

So what’s different between the scenarios? Credentials. Is the scheduled task running as a domain admin? Because you’re manually retrieving principles - something the System account, which Task Scheduler normally uses, isn’t necessarily allowed to do.

My first thought goes to the encrypted password. Did you check that the RunAs account executing the script, can successfully decrypt it?

You may be on to something there. Going to check the rights to that folder. Thanks

Thanks guys you have both put me on the right track. I tested code to decode the encrypted password and I am able to do it as me, but the service account doesn’t do it. I made sure the service account has rights to read the saved credential file and the folder they exist in, still no joy.

I think I read some were if you don’t supply a key the encryption is tied to the account that encrypted it

Try providing a key https://technet.microsoft.com/en-us/library/hh849818.aspx when you encrypt and decrypt it. I know I have done this before when I get to work tomorrow I will post what I have done.

Article

To recap my last blog, part 1 of Encrypting Credentials, when you use ConvertTo-SecureString and ConvertFrom-SecureString without a Key or SecureKey, Powershell will use Windows Data Protection API (DPAPI) to encrypt/decrypt your strings. This means that it will only work for the same user on the same computer.