Download file with random name and execute - powershell

MSI Generator - Downloads custom-named MSI, and I need to download and execute.
I can download and specify a saved file name, but I can't get passed that.
So, this special URL creats a custom MSI file to download, e.g. Installer_t493ht.msi (the random part of that changes each time). I'm trying to use PowerShell to download this file to a specific directory, assign the file name to a variable, and then execute.
What I have so far is:
$url = "https://UrlThatGeneratesRandomlyNamedMSI"
$output = "c:\SaveToThisDirectory\"
Import-Module BitsTransfer
$job += Start-BitsTransfer -Source $url -Destination $output
while ($job | Where-Object {$job.JobState -eq "Transferring"}) {
Sleep -Seconds 1
}
Start-Process msiexec.exe -Wait -ArgumentList '/I /qn $output/$RandomName.msi REBOOT=ReallySuppress'

Just add something like:
$msiFile = Get-Item -Path "$output\Installer_*.msi"
Than execute that (make sure you use double quotes, so the variabele gets expanded):
Start-Process msiexec.exe -Wait -ArgumentList "/I /qn $($msiFile.FullName) REBOOT=ReallySuppress"

Related

Find and execute a file with powershell

I have to find and then execute a .exe file from a script deployed by our asset management software. Currently it looks like this:
Set-Location $PSScriptRoot
$proc = (Start-Process -FilePath "C:\Program Files (x86)\software\software name\Uninstall.exe" -ArgumentList "/S /qn" -Wait -PassThru)
$proc.WaitForExit()
$ExitCode = $proc.ExitCode
Exit($ExitCode)
As far as I understand the location for the location for the file is set and some users do not have it there hence why it fails.
So I understand that you can search for a program with
Get-ChildItem C:\Program Files (x86)\software\
And execute with Start-process -Filepath
But do I simply combine that with a | or is there an easier way/will it even work.
As commenter suggested, you can use Test-Path to test if a path exists:
$uninstallPath = Join-Path ${env:ProgramFiles(x86)} 'software\software name\Uninstall.exe'
if( Test-Path $uninstallPath ) {
$proc = Start-Process -FilePath $uninstallPath -ArgumentList '/S /qn' -Wait -PassThru
$proc.WaitForExit()
$ExitCode = $proc.ExitCode
Exit $ExitCode
}
I've also made the code more robust by avoiding the hardcoded "Program Files (x86)" directory, using an environment variable. Because of the parentheses in the name of the env var, it must be enclosed in curly braces.
For added robustness, you may read the path of the uninstall program from the registry, as detailed by this Q&A. If you are lucky, the program even stores a QuietUninstallString in the registry, which gives you the full command line for silent uninstall.

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
}

PowerShell script to find a file and then launch a command that references it? [duplicate]

This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 3 years ago.
I'm trying to write a script in PowerShell that will search a folder for a specific msi file (subfolder location will vary) and then run an install command referencing that file and other files that would be in the same directory. It would be for a tool that our L1 helpdesk staff could use to run an install for software assigned through SCCM, possibly using different command line variables than what SCCM would run.
Tried different combinations of code and can't install the app.
This works fine on finding the software
Get-ChildItem -Path C:\Windows\ccmcache -Recurse -Filter softwarename.msi
ForEach portion does not work
$Path = Get-ChildItem -Path C:\Windows\CCMCache -Recurse -Filter Something.MSI
ForEach ( $Installer in ( Get-ChildItem -Path $Path.DirectoryName -Filter *.MSI ) ) {
Start-Process -Wait -FilePath C:\windows\system32\msiexec.exe -ArgumentList "/i $Installer.FullName"
}
I basically want to do something like this, using the directory the msi file was found in to do the install:
msiexec /i softwarename.msi /q /norestart
The only thing I see wrong is you can't normally refer to properties of an object inside of a string without adding $( ):
Start-Process -Wait -FilePath C:\windows\system32\msiexec.exe -ArgumentList "/i $($Installer.FullName)"
Why not run msiexec directly?
msiexec /i $installer.fullname

Cannot move file after long running task

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

Power shell script - can we set dll Product Version when we build it?

I am new to Powershell.
I am using power shell script to build VB6 dlls.
$compiler = "C:\Program Files (x86)\Microsoft Visual Studio\VB98\VB6.EXE".
$vbpPath= "C:\...\Desktop\ProjectFile\ProjectName.vbp"
$Outputpath = "C:\...\Desktop\Destination\ProjectName.dll"
Start-Process -FilePath "`"$compiler `"" -ArgumentList "/out
error.txt /make `"$vbpPath`" `"$Outputpath `"" -PassThru -Wait
Can we set its product version by our self when we build it?
Let's say set product version to "Mysoftware 5.1 Beta 6".
Thanks in advance.
Hi to anyone who faced similar issues,
After referencing this link:
How do I set the version information for an existing .exe, .dll?
I was able to change dlls and exes' product version under a specific folder. This step is done after all the dlls and exes were built.
Use Resource Hacker to Extract the *.RC file from exes/ dlls :
$ResourceHackerPath = "C:\Tools\resource_hacker\ResourceHacker.EXE"
$Dllpath = "...\MySoftware\Mydll.dll"
$RCpath = "...\MySoftware\Mydll.RC"
Start-Process -FilePath "`"$ResourceHackerPath`"" -ArgumentList "-extract `"$ProductPath`",`"$RCpath`",versioninfo,," -wait
Edit product version in *.RC file :
$OriProVer = (Get-Item -literalpath $Dllpath ).VersionInfo.ProductVersion
$NewProVer = "Mysoftware 5.1 Beta 6"
(Get-Content $Dllpath).Replace($OriProVer, $NewProVer ) | Set-Content $Dllpath
Use GoRC to change *.RC file format to *.RES file:
Start-Process -FilePath "`"$GoRCPath`"" -ArgumentList "/r `"$RCpath`"" -wait
Use Resource Hacker again to add the *.RES file to the dlls or exes
$RESpath = "...\MySoftware\Mydll.RES"
Start-Process -FilePath "`"$ResourceHackerPath`"" -ArgumentList "-addoverwrite `"$Dllpath `",`"$Dllpath `",`"$RESpath`", , ," -wait
Your dlls' product version should be updated to any string that you want.
Extra tips, if you wish to update a lot of dlls and exes product version:
You can try this:
Search all the dlls and exes under a specific folder:
$directory = "...\Desktop\New Folder"
$FileNameList = Get-ChildItem -literalpath $directory -Include *.dll, *.exe -recurse | foreach-object { "{0}" -f [System.Diagnostics.FileVersionInfo]::GetVersionInfo($_).FileName}
Doing the all 4 steps above in a for loop:
for ($i = 0;$i -lt $FileNameList.count;$i++){...}
Thanks & have a nice day :)