2

TL:DR actual question is at the bottom

I'm trying to troubleshoot a Powershell v1.0 script issue. The script basically downloads a file from an FTP site and puts it on a remote server via UNC and emails the success or failure of the task.

The script runs as a task with a generic ID that is a Domain Admin but is not used to log into systems so the server it runs off of does not contain a profile for it.

If I do a runas for that user and execute the script via command line it works flawlessly. However, if I try to run it as a task it runs then exits instantly. If I open a runas command prompt and run the scheduled task vi at he command line all I get back is:

SUCCESS: Attempted to run the scheduled task "Task Name".

I've tried writing variable values to a text file to see what is going on but it never writes even when I write them as the very first step of execution.

What I want to do is capture any script error messages you would normally see when trying to run the script and/or write the variable information to a text file.

Is there any way to do this? BTW I doing via calling powershell with the following arguments:

-file -ExecutionPolicy Bypass "d:\datscript\myscript.ps1"

-I've tried -command instead of -file.
-I've tried "d:\datscript\myscript.ps1 5>&1 test.txt"
-I've tried "d:\datscript\myscript.ps1 9>&1 test.txt"
-I've tried "d:\datscript\myscript.ps1 | out-file d:\datscript\test.txt"

Nothing worked. I'm sure I can fix whatever bug I have but I'm banging my head against the wall trying to get some kind of failure info.

--Update: Here is a copy of the script minus details--

#-------------------------------------------------------------------------------------------------------------------------------------------------------------

#
#Variable Declaration
#
#$path = Path on local server to downlaod DAT to
#$olddat = Old/last DAT downloaded
#$currentdat = Next DAT number
#$ftpsite = McAfee FTP site. Update if path changes
#$ftpuser = FTP user (anon login)
#$ftppass = FTP password (anon login)
#$tempstring = Manipulation variable
#$gotdat = Boolean if updated DAT exists
#$success = Status if a new DAT exists and has been downloaded (used for email notification).
#$thetime = Variable use dto hold time of day manipulation.

$path = "\\myservername\ftproot\pub\mcafee\datfiles\"
$olddat = ""
$currentdat =""
$ftpsite = "ftp://ftp.nai.com/virusdefs/4.x/"
$ftpuser = "something"
$ftppass = "anything"
$tempstring =""
$gotdat = "False"
$success = ""
$thetime = ""



#
#Normalized functions handles UNC paths
#
function Get-NormalizedFileSystemPath
{
    <#
    .Synopsis
       Normalizes file system paths.
    .DESCRIPTION
       Normalizes file system paths.  This is similar to what the Resolve-Path cmdlet does, except Get-NormalizedFileSystemPath also properly handles UNC paths and converts 8.3 short names to long paths.
    .PARAMETER Path
       The path or paths to be normalized.
    .PARAMETER IncludeProviderPrefix
       If this switch is passed, normalized paths will be prefixed with 'FileSystem::'.  This allows them to be reliably passed to cmdlets such as Get-Content, Get-Item, etc, regardless of Powershell's current location.
    .EXAMPLE
       Get-NormalizedFileSystemPath -Path '\\server\share\.\SomeFolder\..\SomeOtherFolder\File.txt'

       Returns '\\server\share\SomeOtherFolder\File.txt'
    .EXAMPLE
       '\\server\c$\.\SomeFolder\..\PROGRA~1' | Get-NormalizedFileSystemPath -IncludeProviderPrefix

       Assuming you can access the c$ share on \\server, and PROGRA~1 is the short name for "Program Files" (which is common), returns:

       'FileSystem::\\server\c$\Program Files'
    .INPUTS
       String
    .OUTPUTS
       String
    .NOTES
       Paths passed to this command cannot contain wildcards; these will be treated as invalid characters by the .NET Framework classes which do the work of validating and normalizing the path.
    .LINK
       Resolve-Path
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('PSPath', 'FullName')]
        [string[]]
        $Path,

        [switch]
        $IncludeProviderPrefix
    )

    process
    {
        foreach ($_path in $Path)
        {
            $_resolved = $_path

            if ($_resolved -match '^([^:]+)::')
            {
                $providerName = $matches[1]

                if ($providerName -ne 'FileSystem')
                {
                    Write-Error "Only FileSystem paths may be passed to Get-NormalizedFileSystemPath.  Value '$_path' is for provider '$providerName'."
                    continue
                }

                $_resolved = $_resolved.Substring($matches[0].Length)
            }

            if (-not [System.IO.Path]::IsPathRooted($_resolved))
            {
                $_resolved = Join-Path -Path $PSCmdlet.SessionState.Path.CurrentFileSystemLocation -ChildPath $_resolved
            }

            try
            {
                $dirInfo = New-Object System.IO.DirectoryInfo($_resolved)
            }
            catch
            {
                $exception = $_.Exception
                while ($null -ne $exception.InnerException)
                {
                    $exception = $exception.InnerException
                }

                Write-Error "Value '$_path' could not be parsed as a FileSystem path: $($exception.Message)"

                continue
            }

            $_resolved = $dirInfo.FullName

            if ($IncludeProviderPrefix)
            {
                $_resolved = "FileSystem::$_resolved"
            }

            Write-Output $_resolved
        }
    } # process

} # function Get-NormalizedFileSystemPath

#
#Get the number of the exisiting DAT file and increment for next DAT if the DAT's age is older than today.
# Otherwise, exit the program if DATs age is today.
#
$tempstring = "xdat.exe"


$env:Path = $env:Path + ";d:\datscript"
$path2 ="d:\datscript\debug.txt"
add-content $path2 $path
add-content $path2 $olddat
add-content $path2 $currentdat
add-content $path2 $success
add-content $path2 "      "


$path = Get-NormalizedFileSystemPath -Path $path

Set-Location -Path $path
$olddat = dir $path | %{$_.Name.substring(0, 4) }
$olddatfull = "$olddat" + "$tempstring"
if ( ((get-date) - (ls $olddatfull).LastWriteTime).day -lt 1)
    {
#***** Commented out for testing!
#        exit
    }
$currentdat =  [INT] $olddat
$currentdat++
$currentdat = "$currentdat" + "$tempstring"

add-content $path2 $olddat
add-content $path2 $currentdat
add-content $path2 $success
add-content $path2 "      "


#
#Connect to FTP site and get a current directory listing. 
#
[System.Net.FtpWebRequest]$ftp = [System.Net.WebRequest]::Create($ftpsite) 
$ftp.Method = [System.Net.WebRequestMethods+FTP]::ListDirectoryDetails

$response = $ftp.getresponse() 
$stream = $response.getresponsestream() 

$buffer = new-object System.Byte[] 1024 
$encoding = new-object System.Text.AsciiEncoding 

$outputBuffer = "" 
$foundMore = $false 

#
# Read all the data available from the ftp directory stream, writing it to the 
# output buffer when done. After that the buffer is searched to see if it cotains the expected
# lastest DAT.
#
do 
{ 
    ## Allow data to buffer for a bit 
    start-sleep -m 1000 

    ## Read what data is available 
    $foundmore = $false 
    $stream.ReadTimeout = 1000

    do 
    { 
        try 
        { 
            $read = $stream.Read($buffer, 0, 1024) 

            if($read -gt 0) 
            { 
                $foundmore = $true 
                $outputBuffer += ($encoding.GetString($buffer, 0, $read)) 
            } 
        } catch { $foundMore = $false; $read = 0 } 
    } while($read -gt 0) 
} while($foundmore)

$gotdat = $outputbuffer.Contains($currentdat)
$target = $path + $currentdat


#
# Downloads DATs and cleans up old DAT file. Returns status of the operation. 
# Return 1 = success
# Return 2 = Latest DAT not found and 4pm or later
# Return 3 = DAT available but did not download or is 0 bytes
# Return 4 = LatesT DAT not found and before 4pm
#
$success = 0
if ($gotdat -eq "True")
     {
        $ftpfile = $ftpsite + $ftppath + $currentdat
        write-host $ftpfile
        write-host $target
        $ftpclient = New-Object system.Net.WebClient
        $uri = New-Object System.Uri($ftpfile)
        $ftpclient.DownloadFile($uri, $target)
        Start-Sleep -s 30
        if ( ((get-date) - (ls $target).LastWriteTime).days -ge 1)
        {
            $success = 3
        }
        else
        {
            $testlength = (get-item $target).length
            if( (get-item $target).length -gt 0)
            {
                Remove-Item "$olddatfull"
                $success = 1
            }
            else
            {
                $success = 3
            }
        }
    }
    else
    {
        $thetime = Get-Date
        $thetime = $thetime.Hour
        if ($thetime -ge 16)
        {
            $success = 2   
        }
        else
        {
            $success = 4
            exit
        }
    }


#
# If successful download (success = 1) run push bat
#
if ($success -eq 1)
{
    Start-Process "cmd.exe"  "/c c:\scripts\mcafeepush.bat"
}


#Email structure
#
#Sends result email based on previous determination
#
#SMTP server name
$smtpServer = "emailserver.domain.com"

#Creating a Mail object
$msg = new-object Net.Mail.MailMessage

#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$msg.From = "[email protected]"
$msg.ReplyTo = "[email protected]"
$msg.To.Add("[email protected]")
switch ($success)
    {
     1 {
        $msg.subject = "McAfee Dats $currentdat successful"
        $msg.body = ("DAT download completed successfully. Automaton v1.0")
        }
     2 {
        $msg.subject = "McAfee DATs Error"
        $msg.body = ("Looking for DAT $currentdat on the FTP site but I coud not find it. Human intervention may be required. Automaton v1.0")
        }
     3 {
        $msg.subject = "McAfee DATs Error"
        $msg.body = ("$currentdat is available for download but download has failed. Human intervention will be required. Automaton v1.0")
        }
     default {
        $msg.subject = "DAT Automaton Error"
        $msg.body = ("Something broke with the McAfee automation script. Human intervention will be required. Automaton v1.0")
        }
     }

#Sending email
$smtp.Send($msg)

#Needed to keep the program from exiting too fast.
Start-Sleep -s 30


#debugging stuff
add-content $path2 $olddat
add-content $path2 $currentdat
add-content $path2 $success
add-content $path2 "      "
7
  • 3
    Is this really powershell 1.0, and if so why? PowerShell 2.0 runs even on Windows 2003/XP. If you're not sure, check the value of $PSVersionTable. Commented Jul 9, 2015 at 20:27
  • 1
    Do you have the script that you can show us? Commented Jul 9, 2015 at 21:01
  • 1
    The ScheduledJob commands in V3 and later solves this issue by allowing you to create a job that runs on a schedule. And like a regular PowerShell job, you can come back later and ask for any script output - including errors. Commented Jul 10, 2015 at 2:54
  • 1
    Sorry it looks like is is version 2.0.Name Value ---- ----- CLRVersion 2.0.50727.5466 BuildVersion 6.1.7601.17514 PSVersion 2.0 WSManStackVersion 2.0 PSCompatibleVersions {1.0, 2.0} SerializationVersion 1.1.0.1 PSRemotingProtocolVersion 2.1 Commented Jul 10, 2015 at 14:31
  • Is D: a mapped network drive? Commented Jul 10, 2015 at 14:59

1 Answer 1

0

Apparently you have an error in starting Powershell, either because execution policy is different on the Powershell version you start, or on the account, or there is an access error on the scheduled task. To gather actual error, you can launch a task like so:

cmd /c "powershell.exe -file d:\datscript\myscript.ps1 test.txt 2>&1" >c:\windows\temp\test.log 2&>1

This way if there would be an error on starting Powershell, it will be logged in the c:\windows\temp\test.log file. If the issue is in execution policy, you can create and run (once) a task with the following:

powershell -command "Get-ExecutionPolicy -List | out-file c:/windows/temp/policy.txt; Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force"

Running a task under the account you plan to run your main task will first get the policies in effect (so that if setting machine-level policy won't help, you'll know what scope to alter) and set machine-level policy to "RemoteSigned", the least restrictive level beyond allowing every script (highly not recommended, there are encoder scripts written on Powershell that can ruin your data).

Hope this helps.

UPDATE: If that's not policy, there might be some errors in properly writing the parameters for the task. You can do this: Create a .bat file with the string that launches your script and redirects output to say test1.txt, then change the scheduled task to cmd.exe -c launcher.bat >test2.txt, properly specifying the home folder. Run the task and review both files, at least one of them should contain an error that prevents your script from launching.

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for the info. The first step did not work. The second returned. MachinePolicy Undefined UserPolicy Undefined Process Undefined CurrentUser Undefined LocalMachine Unrestricted
If I try to change it I get get Set-ExecutionPolicy : Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\M icrosoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied. At line:1 char:20 + set-executionpolicy <<<< unrestricted + CategoryInfo : NotSpecified: (:) [Set-ExecutionPolicy], Unautho rizedAccessException + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.Pow erShell.Commands.SetExecutionPolicyCommand
"Unrestricted"? This shouldn't be the policy case, then. And what is the user under which you run second task? Also, did you check if the log file from the first step contains any data?
I'm using an AD account our infrastructure dept set up specifically for running tasks with on various servers. The account is a member of Domain Admins, Domain Users, and Enterprise Admins.
Nice. Did you see that the parser yells at $path? Replace the double quotes for single quotes in that line.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.