Automate Software Updates from batch - command-line

Can someone please help me figure out the code for Windows batch file that will look a the properties of executables or installers in a directory, determine the version of the installer and the name of the product, store them in variables, and query agaist installed products in the registry? The idea is that I would like to copy updates to a folder on a machine that has no internet connection from disc. From there I would like to execute a batch file that looks at the version and name of software, stores them in variables, then queries the registry to see if there is a previous version installed. So, if I had downloaded install_flash_player_ax.exe, it should know to look in the registry for adobe flashplayer. If the version on the executable is newer than the version in the registry, it would do a quiet install.
Any help or suggestions would be greatly appreciated!

Though it is an old question, I'd try to answer it, as it may be useful for the others.
Windows command shell doesn't have a straight way to get file metadata like version but you can use wmic for it.
The main problem is that the display name of the software may be different in the properties of the installation/update exe file and than the one in the registry. So it is a bad idea to take the name from the file metadata and query for it in whole HKLM registry hive.
Also, if you do not have a predefined list of the software to be updated and do not know the exact path in the registry where version for each of them is stored, the idea to loop against the list of exe to get names from their metadata id also bad.
So, the best way to search for that is to make a script for each exe separately and to add them to Windows scheduler.
Here is an example of the required batch script to automate updates for Adobe Flash Player for 64-bit OS:
#echo off
for /f %%a in ('wmic datafile where name^="C:\\Users\\username\\Downloads\\install_flash_player_19_active_x.exe" get version ^| find /n /v "" ^| findstr "^\[2\]"') do set var=%%a
for /f "tokens=2 delims=]" %%a in ("%var%") do set prver=%%a
echo Available version: %prver%
for /f "tokens=3" %%a in ('reg query "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{EE56217C-B3F9-402B-B4EC-63F090F51D3D}" /v DisplayVersion') do set regversion=%%a
echo Installed version: %regversion%
if %prver% == %regversion% (echo The newest version %regversion% installed) else (echo Update required & "C:\Users\username\Downloads\install_flash_player_19_active_x.exe")
Update file is located in some local folder, in my case C:\Users\username\Downloads\install_flash_player_19_active_x.exe. When programs are installed, they register themselves in
HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\ for 64-bot OS and
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ for 32-bit.
Hence, you need to find the path for every installation you need. Please note that {EE56217C-B3F9-402B-B4EC-63F090F51D3D} in my script is the GUID for the given version of Flash Player 19.
Here is the same in PowerShell which is more elegant:
$filever = (Get-Item "C:\Users\username\Downloads\install_flash_player_19_active_x.exe").versioninfo.fileversion
$appname = (Get-Item "C:\Users\username\Downloads\install_flash_player_19_active_x.exe").versioninfo.internalname
$regpath = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{EE56217C-B3F9-402B-B4EC-63F090F51D3D}"
$regversion = Get-ItemProperty $regpath -Name "DisplayVersion" | select -ExpandProperty "DisplayVersion"
if ($winrarreg -eq $regversion) {
"The newest version of Flash Player $regpath is already installed"
} else {
"Current installed version is:" + $regversion
"Available version is:" + $filever
"Let's update Flash Player"
Start-Process -FilePath "C:\Users\username\Downloads\install_flash_player_19_active_x.exe"
}

Related

Updating a software from the MS Store using the msixBundle file with a powershell command

I need to prepare a powershell script, that I can use after installing Windows. The script will install basic programs with Winget.
The problem is, that after an installation of Windows, the App Installer isn't updated, so the Winget can't work.
In order to solve the problem, I wrote a PowerShell code that downloads an already updated file for the App installer from my Google Drive, and installs it.
The problem is, that because the App installer is already installed at my PC, and it only needs an update, I receive an error.
(If I install the file manually it works)
I decided to delete the folders in the path "C:\Program Files\WindowsApps" of the app installer, and then I saw that it's not possible to delete the files because the access is denied.
I'm looking for a way to take ownership of the files with a powershell command.
I tried several commands and still when I tried to delete it, it returnes access denied.
For example:
takeown /f "C:\Program Files\WindowsApps" /a /r
returned an error.
How can I update the existing the App Installer using the msixbundle file with a PowerShell command?
If it's not possible, how can I delete these files without an error?
Thanks a lot!!!
This is my code:
takeown /f “C:\Program Files\WindowsApps” /r
takeown /f “C:\Program Files\WindowsApps” /a /r
takeown /f “C:\Program Files\WindowsApps” /r /d y
icacls “C:\Program Files\WindowsApps” /grant administrators:F /t
Get-Childitem -Path "C:\Program Files\WindowsApps" -Recurse | Where-Object {$_.Name -ilike "*DesktopAppInstaller*"} | Remove-Item -recurse -force
New-Item "C:\new1" -itemType Directory
$URL=The link to Google Drive is here.
$PATH="C:\new1\AppInstaller1.msixbundle"
Invoke-WebRequest -URI $URL -OutFile $Path
Add-AppPackage -path "C:\new1\AppInstaller1.msixbundle"
Remove-Item "C:\new1" -Recurse
the installing error message:
Add-AppxPackage : Deployment failed with HRESULT: 0x80073CF3,
Package failed updates, dependency or conflict validation.
Windows cannot install package Microsoft.DesktopAppInstaller_1.18.2691.0_x64__8wekyb3d8bbwe
because this package depends on a framework that could not be found.
Provide the framework "Microsoft.UI.Xaml.2.7" published by
"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
with neutral or x64 processor architecture and minimum version 7.2109.13004.0,
along with this package to install.
The frameworks with name "Microsoft.UI.Xaml.2.7" currently installed are: {}
NOTE: For additional information, look for [ActivityId] 10f677b2-f6bc-0000-971e-f710bcf6d801 in the Event Log or use
the command line Get-AppPackageLog -ActivityID 10f677b2-f6bc-0000-971e-f710bcf6d801
At line:1 char:1
Add-AppxPackage -path "C:\n1\DesktopAppInstaller.Msixbundle"
CategoryInfo : WriteError: (C:\n1\DesktopAppInstaller.Msixbundle:String) [Add-AppxPackage], IOException
FullyQualifiedErrorId : DeploymentError,Microsoft.Windows.Appx.PackageManager.Commands.AddAppxPackageCommand
I found the solution!
On the download page of the AppInstaller at https://store.rg-adguard.net
(package: https://www.microsoft.com/store/productId/9NBLGGH4NNS1)
There are the missing packages for installation.
I downloaded and installed them and then everything worked.
(no need to delete the old installation).
Thanks to #mklement0 for helping me reach a solution!

Removing regedit key via powershell

I have been removing powershell and/or command line like below. My question is : Is there any equivalent inside powershell for /reg:64 parameter ?
CMD version:
reg delete HKEY_LOCAL_MACHINE\SOFTWARE\TEST /v PropertyToRemove /f /reg:64
powershell version:
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client" -Name "PropertyToRemove"
thanks,
It looks like it's not implemented.
Reading this question: Querying via powershell both 32bit and 64bit registry
it seems you should just be checking whether the os is x32 or x64 first
or run a check if the 64bit registry keys exists
Different topic, same question.. here's stated it's not implemented:
How to access the 64-bit registry from a 32-bit Powershell instance?
look at whenrybruce's reply on the marked answer

Restart environment and script during batch script

I've built a few FFmpeg powershell scripts for me and a few others to use and I'm attempting to make the setup and update process as easy as possible. The end goal is to be able to run 1 batch file that installs Chocolatey, FFmpeg, git, clones the github repo (for updates), and edits the Windows registry to add the actual FFmpeg powershell scripts / console programs to the Windows Explorer contextual menu. This way I just pass them the folder containing everything once and any time I change or add something to the project I can just tell them to run the batch file again, and presto everything is up to date.
However I'm struggling to find a way to install Chocolatey, then git with Chocolatey, and then run a git command with the execution of a single .bat file. From what I can tell after installing Chocolatey I need to restart the shell entirely before I can install git, and then I have to restart the shell again before I can use a git command. As of right now most of the actual processing is happening via Powershell scripts that are launched from the .bat file, and as each step is taken I update a txt file, attempt to restart the batch script, and read the txt file to pick up where I left off:
#echo off
echo Administrative permissions required. Detecting permissions...
echo.
net session >nul 2>&1
if %errorLevel% == 0 (
echo Success: Administrative permissions confirmed.
echo.
) else (
echo Failure: Current permissions inadequate.
PAUSE
exit
)
set relativePath=%~dp0
set relativePath=%relativePath:~0,-1%
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\CheckRequiredPackages.ps1" -relativePath "%relativePath%"
set /p step=<"%relativePath%\Setup\Step.txt"
if %step% == 1 (
(echo 2) > "%relativePath%\Setup\Step.txt"
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\GetChocolatey.ps1"
start "" "%relativePath%\RunMe.bat"
exit
)
if %step% == 2 (
(echo 3) > "%relativePath%\Setup\Step.txt"
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\GetRequiredPackages.ps1"
start "" "%relativePath%\RunMe.bat"
exit
)
if %step% == 3 (
(echo 0) > "%relativePath%\Setup\Step.txt"
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\Update.ps1" -relativePath "%relativePath%"
)
PAUSE
Exit
The problem is using the start command in the batch script doesn't seem to work, I'm guessing since that new process is spawned from the same process that handles the Chocolatey install it doesn't count as actually restarting the shell. Is there any way to actually restart the shell and somehow have the batch file start back up without user intervention?
I'm not sure why I didn't initially think of reloading the path environment variable but that's a whole lot more reasonable than restarting the script 4 times with an intermediary file.
Firstly I moved 99% of the heavy lifting from the .bat file to a Powershell script, as the only reason I'm using Batch is so the user can easily run the file by clicking it in Explorer. I couldn't get RefreshEnv to work, which is a feature of Chocolatey, but running this between each new package worked great:
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
So I have something like this now, and the Batch scrip just launches this Powershell Script:
Write-Host "Installing / updating required packages..."
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol =
[System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
choco install ffmpeg -y
choco install git -y
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
Write-Host "Deleting old files..."
Remove-Item -LiteralPath $relativePath -Force -Recurse
Start-Sleep 2
Write-Host "`nUpdating Files..."
git clone https://github.com/TheNimble1/FFmpegContextCommands.git $relativePath
Which installs Chocolatey, refreshes the path, installs FFmpeg & Git, refreshes the path, deletes the old files, and then clones the git to replace with new files.
Indeed, a start-launched process inherits the calling process' environment rather than reading possibly updated environment-variable definitions from the registry.
Chocolatey comes with batch file RefreshEnv.cmd (C:\ProgramData\chocolatey\bin\RefreshEnv.cmd, but C:\ProgramData\chocolatey\bin should be in the %PATH%) specifically to avoid having to start a new, independent session for environment updates to take effect.
Therefore, something like the following may work:
:: Assumes that Chocolatey was just installed to the default location.
call "%ProgramData%\chocolatey\bin\RefreshEnv.cmd"
:: If Chocolatey was *previously* installed and its installation directory
:: has already been added to %Path%, the following will do:
:: call RefreshEnv.cmd
call "%relativePath%\RunMe.bat"
Since Chocolatey is only being installed during your script's execution and its binaries folder is therefore not yet in %Path%, you'll have to call RefreshEnv.cmd by its full path, as shown above - which assumes the default install directory.
Your own answer now shows how to refresh the $env:Path (%Path%) environment variable using .NET methods directly _from PowerShell, which is a pragmatic solution.
Note, however, that RefreshEnv.cmd is more comprehensive in that it reloads all environment-variable definitions and therefore potentially newly added and modified ones.
Note that calling RefreshEnv.cmd from PowerShell does not work, because it then runs out of process (which means that it cannot update the calling process' environment).
However, Chocolatey offers an Update-SessionEnvironment PowerShell command (aliased to refreshenv), which you can make available immediately after a Chocolatey install as follows:
# Import the module that defines Update-SessionEnvironment aka refreshenv
Import-Module "$env:ProgramData\Chocolatey\helpers\chocolateyProfile.psm1"
# Refresh all environment variables.
Update-SessionEnvironment # or: refreshenv
See this answer for a more robust approach that doesn't rely on assuming that the default location was installed to.

wget not found by PowerShell script?

I have an old notebook with Windows 7 64-bit that executes a PowerShell script perfectly every Sunday. Unfortunately it starts to crash as soon as the load increases and I decided to get a new PC. On this PC I previously installed Windows&nbspe;10 Pro 64-bit and even here the script was executed every Sunday. Due to the update policy of Microsoft I removed Windows 10 from the new PC and installed Windows 7 64-bit. But now the same script crashes as it does not find wget:
$wg = Start-Process wget.exe -wait -NoNewWindow -PassThru -ArgumentList $argList
Gnu Wget is installed correctly (I think). It is placed at:
C:\Program Files (x86)\GnuWin32\bin\wget.exe
It is even entered in the registry under HKEY_LOCAL_MACHINE → SOFTWARE → Wow6432Node → GnuWin32|Wget|1.11.4-1|setup|InstallPath: C:\Program Files (x86)\GnuWin32.
But despite this if I open the CMD console and enter wget (or wget.exe) I get:
The order "wget" is either misspelled or could not be found.
What do I have to do that PowerShell finds wget constantly even after a restart of the PC? Even e.g. Notepad++ cannot be found by the CMD console despite it is installed properly(?). What's wrong here?
If you want to be able to run a command without specifying its path you need to add the directory it resides in to the PATH environment variable. The install path in the SOFTWARE branch of the registry has nothing to do with it.
To add a directory to the PATH for the current and all future sessions you need to do something like this:
$dir = "${env:ProgramFiles(x86)}\GnuWin32\bin"
# set PATH environment variable for current session
$env:Path += ";${dir}"
# set PATH environment variable for future sessions
$path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
$path += ";{$dir}"
[Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
Note, however, that the second step (setting the variable for future sessions) only works correctly if there are no Windows environment variables (%something%) used in $path, because the method saves the value as a REG_SZ in the registry. Windows only expands environment variables in the PATH variable if it's stored as a REG_EXPAND_SZ value.
If you do have regular Windows environment variables somewhere in $path you must manually write the value to the registry with the correct type.
$key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
Set-ItemProperty -Path $key -Name 'Path' -Value $path -Type ExpandString
Addendum:
All of the above applies only if you want to do this programmatically, of course. For a manual approach you can always edit the environment variables via the GUI and restart PowerShell.

Getting error while installing "azure-powershell.0.8.7.msi" through .cmd file

I am trying to install “azure-powershell.0.8.7.msi” through a .cmd file using command
msiexec.exe /i ".\azure-powershell.0.8.7.MSI" /passive
This msi file is part of solution explorer(part of project, I have to do it in this way only).
Although I am able to install/uninstall when this msi file when it’s on local disk ( i.e. on some drive)
I tried to log the error it is:
“This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package.”
It is a known error of Microsoft. I tried each and every proposed solution on internet but it doesn’t work.
Note: The current user/admin of the system have all the access(read,write,modify).
if your MSI-file is in the same directory like the cmd-file you have to us the following command
msiexec /i "%~dp0azure-powershell.0.8.7.MSI" /qb
%~dp0 is refering to cmd-file directory and in this case to the MSI-file.
If you want to create a log-file use the /l and the logfilepath plus name after /qb.
For example:
msiexec /i "%~dp0azure-powershell.0.8.7.MSI" /qb /l*v %temp%\azure-powershell.log