Cannot move file after long running task - powershell

I have a windows powershell script executed by Windows Task Scheduler. The powershell code starts an executable cli program (java), which creates it's own logfile, then when finished my powershell script tries to rename the file then upload it to a remote server. However, I'm finding that it only successfully uploads the file ever 3rd or 4th execution and am not sure why. The history in task scheduler provides no details as to what might have happened (file lock?). Any ideas on how I can solve this? Here is a basic example of what I'm doing:
$old_log_name = "old_logfilename"
$new_log_name = "new_logfilename"
C:\path\to\my-java-program.exe -pass -some -options
Move-Item -Path $old_log_name -Destination $new_log_name
gsutil cp $new_log_name gs://cool-bucket-with-cf
I'm certain that the problem is with renaming the file, and not with uploading it, because can see so in windows file explorer.
Should I be checking to see if the file is available to be renamed or not?
Edit (revised code based on comments below):
$app_version = "4.5.7"
$now = get-date
$now = $now.ToString("yyyyMMddhhmm")
$old_log_name = "D:\myapp_$app_version.log"
$new_log_name = "D:\myapp_servername_$app_version.$now.log"
$arguments = "-a -c C:\myapp\config\Stage-$app_version.xml"
Start-Process C:\myapp\bin\4.5.7\myapp.exe -ArgumentList $arguments -NoNewWindow -Wait
Move-Item -Path $old_log_name -Destination $new_log_name
gsutil cp $new_log_name gs://path-to-mybucket

Why don't you start a process with your java program, so it wait for it to exit ?
$old_log_name = "old_logfilename"
$new_log_name = "new_logfilename"
Start-Process C:\path\to\my-java-program.exe -ArgumentList "your options" -NoNewWindow -Wait
Move-Item -Path $old_log_name -Destination $new_log_name
gsutil cp $new_log_name gs://cool-bucket-with-cf
Update
Since this is a scheduled task, you need to add a new event log in order to log events from your script (you need administrative rights for this) :
New-EventLog -LogName Application -Source TheScriptThatCallsMyJavaProgram
Then, replace Start-Process call with the following (EventId is random here) :
Write-EventLog -LogName Application -Source TheScriptThatCallsMyJavaProgram -EventId 3001 -Message "my-java-program.exe is about to start" # This logs the start of the program
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "C:\path\to\my-java-program.exe"
$pinfo.Arguments = "your options"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
Write-EventLog -LogName Application -Source TheScriptThatCallsMyJavaProgram -EventId 3002 -Message "my-java-program.exe is exited" # This logs the end of the program

Related

Installing MSI application via PowerShell

I am trying to build a powershell script to install, an application on 15 workstations. The application requires the following:
Application process to be stopped
Previous version to be uninstalled
Install new client.
I am running into a few snags though. The uninstaller is telling me that the application isn't installed, and or the application is not a valid 64-bit application. I can run the uninstaller fine if I use the uninstall string from command line. My uninstall log file, does not tell me much, it just tells me that Powershell.
If I run through the installer manually, I reach a point after accepting the EULA, and begin the actual installation by selecting "install" that button comes up with the "Admin" shield. Automating it I get an MSI error 1603 which I cannot get past...
This is a medical application to be installed in a very restricted, environment. Thank you for your help!
Here is my code:
$SourcePath = "LOCATION OF Source"
$Destination = "C:\Temp"
$App = Get-WmiObject win32_product | where{$_.name -like "mammoscreen*"}
$Install = 'C:\Temp\tpx-client-v2.2.3.msi'
$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
Start-Transcript -path "C:\Temp\TheraInstall.log" -append
#Do some stuff and begin logging
Write-Output "Start Logging"
Get-Date
Write-Host "Copying files..." -ForegroundColor Green
Copy-Item $SourcePath -Destination $Destination
Write-Host "Stopping process..." -ForegroundColor Cyan
Stop-Process -ProcessName tpx-client-agent
#Uninstall old versions of software
Write-Host "Uninstalling previous versions..." -ForegroundColor yellow
Start-Process 'C:\Windows\System32\msiexec.exe' -ArgumentList "/X $($App.IdentifyingNumber)/qn /norestart /L*v C:\Temp\UninstallTest.log" -Wait
#Installs the new Client
Write-Host "Installing new agent v2.2.3..." -ForegroundColor Green
Start-Process 'C:\Windows\System32\msiexec.exe' -Wait -ArgumentList '/I $Install /qn /norestart /L*v C:\Temp\InstallTest.log'
#Stop logging
Stop-Transcript
#Start-Process -Filepath "tpx-client-agent"

PowerShell Start-Process Not Starting when script called remotely

I am trying to remotely install Octopus Deploy Tentacle to a VM. I have a powershell script that I've written that handles this business for me. It works exactly as expected when I am physically logged into the machine, but when I am remotely executing it on the machine it doesn't run the installer. Every other part of the script works fine though.
The portion of the script that downloads and installs:
try{
$url = "https://octopus.com/downloads/latest/OctopusTentacle64"
$downloadPath = "$env:TEMP\octopus\"
if (Test-Path $downloadPath) {
Remove-Item -Path $downloadPath -Recurse -Force
}
New-Item -Type Directory -Path $downloadPath | Out-Null
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
(New-Object System.Net.WebClient).DownloadFile($url, "$downloadPath\Octopus.Tentacle.msi")
Start-Process -FilePath "$downloadPath\Octopus.Tentacle.msi" -ArgumentList "/passive" -Wait -verbose
Write-Output "Octopus Tentacle Installed."
} catch { throw "error downloading tentacle. try again later."}
I am using this to attempt to run the file remotely(edit, copied wrong line):
Invoke-Command -ComputerName $vmName -FilePath "\\$vmName\Installers\Install-Calamari.ps1" -Credential $creds -ArgumentList $deployTag, $envID
What am I missing?

Elevating PowerShell script permissions

I am trying to run script to manage some VHD Disks, but the disk mount is failing due to elevated permissions required. The user the script is run under is a local admin, but UAC is blocking it I think. The error which comes back is: “DiskState=Failed to mount disk - "Access to a CIM resource was not available to the client”
Ideally I need to the script to run under elevated command prompt automatically. Any idea's how I can achieve that programmatically?
The script I am running is this:
$location = "C:\temp"
$name = "downloadfile"
$Author = "FSLogix"
$FilePath = "Filepath here"
$LogFilePath = "Logfilepath here"
# Force to create a zip file
$ZipFile = "$location\$Name.zip"
New-Item $ZipFile -ItemType File -Force
$RepositoryZipUrl = "https://github.com/FSLogix/Invoke-FslShrinkDisk/archive/master.zip"
# download the zip
Write-Host 'Starting downloading the GitHub Repository'
Invoke-RestMethod -Uri $RepositoryZipUrl -OutFile $ZipFile
Write-Host 'Download finished'
#Extract Zip File
Write-Host 'Starting unzipping the GitHub Repository locally'
Expand-Archive -Path $ZipFile -DestinationPath $location -Force
Write-Host 'Unzip finished'
# remove the zip file
Remove-Item -Path $ZipFile -Force
# Run the FSLogix Optimisation
C:\temp\Invoke-FslShrinkDisk-master\Invoke-FslShrinkDisk.ps1 -Path $FilePath -Recurse -PassThru -LogFilePath $LogFilePath\logfile.csv
You can elevate the PS script using the Powershell as a separate process and make it "run as admin" like below:
start-process PowerShell -verb runas
OR
Powershell -Command "Start-Process PowerShell -Verb RunAs"
Apart from that , you can condition it as well. There is a beautiful conditional code shared by PGK which can help as well:
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" +$myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}

Invoke-Command doesn't return to local machine but software is installed

The tl;dr: I'm puzzled as to why my script isn't returning back to the deployment machine. Does anyone have an idea why?
I was provided an EXE and a couple arguments to install some software. I'm using PowerShell's Start-Process to run it. The deployment machine is Win Server 2012 domain controller, logged in and being run as domain admin. The test machines are two Windows 10 Pro domained machines. All three have PS 5.1.
Being a silent install, there is no user interaction required. If I run the exact command locally, via RDP, or in a pssession, with just c:\Software.exe /silent /arg2removed, it installs and returns as expected.
The script runs fine up to a point. Nothing happens after Start-Process inside Invoke-Command 's -ScriptBlock. In a separate PowerShell window, I can use Enter-PSSession for each of the two client machines, and Get-Service and Get-Process both show the software's service and background processes, respectively. I can Ctrl+c on the deployment machine and get back to a prompt. No errors are reported at any time.
Here's the Start-Process chunk. I've read the help and it doesn't sound like I'm missing anything that would allow the ScriptBlock to finish. If I prepend Start-Process with Write-Host (like we all do), it echoes the command that would run and I get back to a command prompt on the deployment machine.
# Start the installer.
Start-Process `
-FilePath "C:\${using:SrcExe}" `
-ArgumentList "/SILENT", "/arg2removed" `
-WorkingDirectory C:\ `
-Wait `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InstallErrors
Here's most of the script. The only items before Invoke-Command are where I set up $ComputersToInstallOn, enter the credentials (yes I'm sure they're correct), and supply the path to the EXE.
Invoke-Command `
-ComputerName $ComputersToInstallOn `
-Credential $Creds `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InvokeCommErrors `
-ScriptBlock {
# Get and print the destination machine's hostname
$ThisMachine = Get-Content Env:\COMPUTERNAME ; $ThisMachine
# Print the current date and time
Get-Date
# Check if Sentinel processes are running. If not, assume it's not installed.
$S1Procs = get-process sentinel*
if([string]::IsNullOrEmpty($S1Procs)) {
# Sentinel isn't installed. Continue.
# Map a drive letter to $SrcFolder. Not theoretically necessary but Start-Process complains when copying with the UNC path directly.
New-PSDrive `
-Name S `
-PSProvider FileSystem `
-Credential ${using:Creds} `
-Root ${using:SrcFolder} `
-verbose
# List remote folder
Get-ChildItem S:\
# Copy the $SrcExe to C:\
Copy-Item `
-Path "S:\${using:SrcExe}" `
-Destination C:\ `
-Verbose `
-ErrorAction Stop `
-ErrorVariable CopyErrors
# Unmount drive
Remove-PSDrive S -verbose
# Verify EXE exists locally
Get-ChildItem -Path C:\${using:SrcExe}
# If there were copy errors, abort.
if ($CopyErrors) {
Write-Host "There was an error copying '${using:SrcExe}' to $ThisMachine. Aborting."
exit 1 } else {
# All good so far. Continue to install.
Write-Host "$(Get-Date -UFormat '%Y%m%d %H:%M:%S') : Starting install on ${ThisMachine}. You may need to Ctrl+C to return to the local machine. Check processes on each machine though."
# Start the installer.
Start-Process `
-FilePath "C:\${using:SrcExe}" `
-ArgumentList "/SILENT", "/arg2removed" `
-WorkingDirectory C:\ `
-Wait `
-Verbose `
-ErrorAction SilentlyContinue `
-ErrorVariable InstallErrors
# ScriptBlock doesn't seem to make it to anything after Start-Process.
# Remove the EXE.
Remove-Item "C:\${using:SrcExe}" -Verbose -ErrorAction SilentlyContinue
exit 0
# Get-Process -Name Sentinel*
# echo "Sleeping. Now would be the time to abort."
# Start-Sleep 15
}
} else {
Write-Host "Sentinel appears to be installed and running."
$S1Procs
Get-Service -Name Sentinel* | Where-Object { $_.Status -match "Running" }
exit 0
}
}
if($InvokeCommErrors){
Write-Host "There were some errors."
}
EDIT: Added some requested info.

Scheduled Task for robocopy PowerShell script does not finish - what's wrong?

I'm working on a PowerShell script to automatically move some files to a remote server every 5 minutes.
The logs are always nested two directories deep. I don't care about those directories. Both the folders and the files can be, but are not always, randomly named. I have used the local temp directory to accomplish the de-nesting prior to shuttling the files over to robocopy.
I've also created a new event log source for troubleshooting. I'll cut those down after I can get this working. I believe I'm running this from Task Scheduler correctly (as start a program C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with arguments -WindowStyle Hidden -File "C:\PathToScript\ThisIsTheScript.ps1").
Here's how it should go when automated.
Triggered by Task Scheduler
Create temp log directory if needed
De-nest and move logs to temp log directory
Remove all empty directories from source
robocopy to destination share by UNC name
Task Scheduler completes task
The files remain in the temp log directory, do not copy to the destination server, and Task Scheduler never completes - it remains running. However, all of the troubleshooting log entries are created, even if using Start-Job and Wait-Job around the robocopy line.
Steps 5 and 6 are not happening when scheduled, only when run manually. Why aren't the files copying, and why won't the task complete when run in Task Scheduler?
# Source Variables
$LogSource = 'C:\Somewhat\Deeper\Directory\Log\Files\Dropped\Here'
$TempLogDir = 'C:\SomeFilesBrieflyHere'
# Destination variables
$DestHost = 'thisisthedestinationcomputername'
$DestShare = 'thisisthedestinationsharename$'
# Operating Variables
$Switches = #("/s", "/MOVE", "/zb", "/R:0", "/W:0", "/MT:8", "/NFL", "/NDL", "/NP", "/IS", "/IT")
$RoboDo = #("$TempLogDir","\\$DestHost\$DestShare","$Switches")
# Verify event log source exists and create it if it does not
if (-not ([System.Diagnostics.EventLog]::SourceExists("PowerShell Script"))) {
New-EventLog -LogName 'Application' -Source 'PowerShell Script'
Write-EventLog -LogName 'Application' -Source 'Powershell Script' -EventID 1 -EntryType Information -Message 'Created new Event Log: Application Source type: PowerShell Script'
}
# Write log for init
Write-EventLog -LogName 'Application' -Source 'Powershell Script' -EventID 1 -EntryType Information -Message 'The scheduled task to move logs to the log server has been triggered and will now execute.'
# Verify Local LogDir exists
if (!(Test-Path -Path $TempLogDir)) {
New-Item -ItemType directory -Path $TempLogDir
}
# De-nest logs to local log folder in preparation to move
Get-ChildItem -Path "$LogSource" | Get-ChildItem | Get-ChildItem | Move-Item -Destination "$TempLogDir" -Force
Write-EventLog -LogName 'Application' -Source 'Powershell Script' -EventID 1 -EntryType Information -Message "Files locally de-nested into $TempLogDir."
# Remove all empty directories with unicorn glitter
Get-ChildItem $LogSource -Recurse | Where {$_.PSIsContainer -and #(Get-ChildItem -LiteralPath:$_.FullName).Count -eq 0} | Remove-Item -Confirm:$false -Force
Get-ChildItem $TempLogDir -Recurse | Where {$_.PSIsContainer -and #(Get-ChildItem -LiteralPath:$_.FullName).Count -eq 0} | Remove-Item -Confirm:$false -Force
Write-EventLog -LogName 'Application' -Source 'Powershell Script' -EventID 1 -EntryType Information -Mes
# Begin Robocopy operation
robocopy $TempLogDir \\$DestHost\$DestShare $Switches
Write-EventLog -LogName 'Application' -Source 'Powershell Script' -EventID 1 -EntryType Information -Message "The scheduled task to move logs to the log server has been completed and the files were successfully copied to $DestHost."
Try changing the robocopy line to this instead:
& robocopy $TempLogDir \\$DestHost\$DestShare $Switches
The problem might be that your script stops at that line, complaining that robocopy isn't a cmdlet or function, even though it works when running it manually.