I am currently trying to change the proceedure from this site into a script to automate the process. http://www.freenode-windows.org/resources/vista-7/windows-update
When I check Control Panel> System & Security>Windows Update > View Update History-- updates KB3020369, KB3172605, and KB3125574 do not show up as installed. Is there something wrong with my foreach loop?
<########
CONFIGURATION TO STOP WINDOWS UPDATES
#########>
$rmpth = 'c:\windows\softwaredistribution\WuRedir'
$ws = get-service wuauserv
if($ws.Status -eq "Stopped"){
msg * "Update Service Stopped"
}else{
stop-service wuauserv -Force
msg * "Stopping Update Service, Update Service Stopped"
}
if(test-path $rmpth){
remove-item $rmpth -Force -Confirm:$false
}
<###########
CONFIGURATION TO INSTALL WINDOWS PATCH
###########>
$pathofupdates = #("KB3020369", "KB3172605", "KB3125574")
Foreach($item in $pathofupdates)
{
$wusainit = "/quiet /norestart C:\temp\Windows /extract C:\temp\Windows\${item}.msu"
$disminit = "/online /quiet /norestart /add-package /PackagePath:C:\temp\Windows\${disminit}.cab"
$SB={ Start-Process -FilePath 'wusa.exe' -ArgumentList $wusainit.ToString() -Wait -PassThru }
Invoke-Command -ScriptBlock $SB
$SB={ Start-Process -FilePath 'dism.exe' -ArgumentList $disminit.ToString() -Wait -PassThru }
Invoke-Command -ScriptBlock $SB
}
foreach loop and .msu file order was the issue. Updates had to be in a certain order. renamed updates to 1.KB3020369.msu, 2.KB3172605.msu, and 3.KB3125574.msu.
Found new method for applying .msu updates on
https://harshilsharma63.wordpress.com/2014/12/27/how-to-install-multiple-msu-windows-update-files-with-powershell-script/
<###########
CONFIGURATION TO INSTALL WINDOWS PATCH
###########>
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$dir = (Get-Item -Path $dir -Verbose).FullName
Foreach($item in (ls $dir *.msu -Name))
{
echo $item
$item = $dir + "\" + $item
wusa $item /quiet /norestart | Out-Null
}
Related
Looking to execute a powershell command that will uninstall an application and clear some registry entries if either a file/folder is not present on the system.
Currently looking at something like this:
if(!(test-path “c:\Windows\mdm” )
{
$productdetails=(Get-WmiObject -Query “Select IdentifyingNumber from win32_product where name = ‘Tesapp’“).identifyingnumber
$proc=(Start-Process -FilePath “msiexec.exe” -ArgumentList “/x $productdetails /qn /l*v c:\temp\Uninstall-testapp.log” -Wait -PassThru).ExitCode
if (!($proc -eq 0 -or $proc -eq 3010))
{
exit(1)
}
$RegKey=“HKLM:\SOFTWARE\Microsoft\TestAppManagement\S-0-0-00-0000000000-0000000000-000000000-000\MSI\{0}” -f $productdetails
if(test-path $RegKey)
{
$op= Get-Item $RegKey | Out-File “c:\windows\temp\TestApp_Reg_backup.reg” -Force
$op= Remove-Item $RegKey -Force
}
}
Interestingly if I run the test path it will return false which is expected, or if run the app uninstall/reg clear on its own it will work as intended - however i cannot run them together.
I assume i am making some sort of syntax/ basic logic error?
I am trying to start a process and wait for the exit code. The process starts msiexec with an argument list. When i run my script it comes up with the argument help wizard, however if i run the command directly in CMD generated by:
write-host $command
It works as expected. Here is my full script:
# Get uninstall strings from registry, looking for the msiexec option
$applist = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
Get-ItemProperty |
Where-Object {$_.DisplayName -match "Microsoft Visio Standard 2013" -and $_.UninstallString -match "msiexec"} |
Select-Object -Property DisplayName, UninstallString
# Check for any aplications requiring uninstall and output their names
if ($applist) {
foreach ($app in $applist) {
Write-host "$($app.DisplayName) has been detected for uninstall"
}
Write-host "Attempting to uninstall application(s)"
# Uninstall each application that has been identified
foreach ($app in $applist) {
try {
$uninst = $app.UninstallString
$pos = $uninst.IndexOf(" ")
$leftPart = $uninst.Substring(0, $pos)
$rightPart = $uninst.Substring($pos+1)
$command = """$rightPart /qn /L*V ""C:\UninstallVisio.txt"""""
write-host $command
$uninstall = (Start-Process "msiexec.exe" -ArgumentList $command -Wait -Passthru).ExitCode
if($uninstall.ExitCode -ne 0) {
write-host "attempting XML config uninstall"
#**still to be worked on**
}
} catch {
write-host "Unable to uninstall $_.Name Please view logs"
Continue
}
}
}
Exit
# Exit script as no apps to uninstall
else {
write-host "No application(s) detected for uninstall"
Exit
}
Instead of this
$command = "$uninst /qn /L*V ""C:\UninstallVisio.txt"""
try
$command = #(
$uninst
"/qn"
"/L*V"
'"C:\UninstallVisio.txt"'
)
Source: see the last example
https://kevinmarquette.github.io/2016-10-21-powershell-installing-msi-files/
#Get uninstall strings from registry, looking for the msiexec option
$applist = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
Get-ItemProperty |
Where-Object {$_.DisplayName -match "Microsoft Visio Standard 2013" -and $_.UninstallString -match "msiexec"} |
Select-Object -Property DisplayName, UninstallString
#Check for any aplications requiring uninstall and output their names
if ($applist){
foreach ($app in $applist){
Write-host "$($app.DisplayName) has been detected for uninstall"
}
Write-host "Attempting to uninstall application(s)"
#Uninstall each application that has been identified
foreach ($app in $applist){
try
{
$uninst = $app.UninstallString
$pos = $uninst.IndexOf(" ")
$leftPart = $uninst.Substring(0, $pos)
$rightPart = $uninst.Substring($pos+1)
$command = #(
$rightPart
"/qn"
"/L*V"
'"C:\UninstallVisio.txt"'
)
write-host $command
$uninstall = (Start-Process "msiexec.exe" -ArgumentList $command -Wait -Passthru).ExitCode
If($uninstall.ExitCode -ne 0){
write-host "attempting XML config uninstall"
}
}
catch{
write-host "Unable to uninstall $_.Name Please view logs"
Continue
}
Exit
}
}
#Exit script as no apps to uninstall
else {
write-host "No application(s) detected for uninstall"
Exit
}
It looks like you are trying to run msiexec.exe with ArgumentList "msiexec.exe /x {ProductCode}".
Powershell is trying to run your command as "msiexec.exe msiexec.exe /x {ProductCode}"
HI every one I Have the following scripts which i am working on but not sure how to put a wait for a zip to finish and than move on to the next block of code. following are the two scripts which i am using. The first script is a backup script which is calling another script for zipping
$Date = Get-Date
$folder_date = $Date.ToString("yyyy-MM-dd_HHmm")
$backup_folder_name = 'c:\Russia\' + $folder_date
$V3_folder_name = 'C:\121RussiaScaled\products'
$Admin_folder_name = 'C:\inetpub\wwwroot\admin'
$Tablet_folder_name = 'C:\inetpub\wwwroot\tabl'
$Service_folder_name = 'C:\Russia\dll'
if (!(Test-Path -path $backup_folder_name)) {
New-Item $backup_folder_name -type directory
} # if (!(Test-Path -path D:Data))
if ((Test-Path -path $V3_folder_name)) {
Start-job -scriptblock {gi $V3_folder_name | .\Library\out-zip.ps1
$backup_folder_name\V3.zip $_}
Wait-job -Id $Job.Id
}
if ((Test-Path -path $Service_folder_name)) {
$Job = Start-job -scriptblock {gi $Service_folder_name | .\Library\out-zip.ps1
$backup_folder_name\Services.zip $_}
Wait-job -Id $Job.Id
}
if ((Test-Path -path $Admin_folder_name)) {
$Job = Start-job -scriptblock {gi $Admin_folder_name | .\Library\out-zip.ps1
$backup_folder_name\admin.zip $_}
Wait-job -Id $Job.Id
}
if ((Test-Path -path $Tablet_folder_name)) {
$Job = Start-job -scriptblock {gi $Tablet_folder_name | .\Library\out-zip.ps1
$backup_folder_name\tablet.zip $_}
Wait-job -Id $Job.Id
}
This is my out.zip script
$path = $args[0]
$files = $input
write-output $path
if (-not $path.EndsWith('.zip')) {$path += '.zip'}
if (-not (test-path $path)) {
set-content $path ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
}
$ZipFile = (new-object -com shell.application).NameSpace($path)
$files | foreach {$zipfile.CopyHere($_.fullname)}
while using the above script i am getting an error "Start-Job missing an argument for the paratmeter script block."
or is there another way so that i can put a wait for these zips to finish one by one
Try
$Job = Start-Job -ScriptBlock .....
Wait-Job -Id $Job.Id
For the ScriptBlock error, try specifying the starting brace at the same line like:
$Job = Start-Job -ScriptBlock {
# Job code here
}
The title says it all i have a Start-Job with a script block that instead of executing the commands it output the info about the job.
The order in which this gets executed is the following
$location = $(Get-Location).toString()
$oldModulePath = $env:PSModulePath
$env:PSModulePath = $env:PSModulePath + ";" + $location + "\LongitudePowershellModules"
$env:PSModulePath
$configXML = [xml](Get-Content $location"\InstallationConfig.XML")
$config = $configXML.root
Import-Module CreateComponentsFolder -PassThru
Import-Module CreateTransferFolder -PassThru
Import-Module ConfigureDB -PassThru
Import-Module FastProxyTools -PassThru
Import-Module WspTools -PassThru
Import-Module CopySharepointPowershellXML -PassThru
Import-Module SearchCenterTools -PassThru
Import-Module ConfigureFAST -PassThru
# 1 - CreateComponentsFolder
CreateLongitudeComponentsFolder -currentLocation $location
And the module with the start-job
function CreateLongitudeComponentsFolder
{
Param(
[Parameter(Mandatory=$True, Position=1)]
[string]$currentLocation
)
$scriptBlock = {Write-Host "Copying files to '"C:\Program Files\Ba-insight\Longitude Fast Component"'"`
Copy-Item $currentLocation"\Longitude Fast Component" "C:\Program Files\Ba-insight\Longitude Fast Component" -recurs`
}
Start-Job -ScriptBlock $scriptBlock -ArgumentList $currentLocation -Name "CopyingFiles"
$scriptBlock = {$fileAndFolderList = Get-ChildItem $currentLocation"\Longitude Fast Component" -recurs
$targetLocationFileAndFolderList = Get-ChildItem "C:\Program Files\Ba-insight\Longitude Fast Component" -recurs
$timer = new-object System.Timers.Timer
$timer.Interval = 500 #0.5 sec
$timer.AutoReset = $true
$itemsPresent = $fileAndFolderList | Measure-Object
$action = {foreach($item in $fileAndFolderList)`
{`
if($itemsPresent -ne 0)`
{`
if($targetLocationFileAndFolderList.IndexOf($item) -ne -1)`
{`
$itemsPresent = $itemsPresent - 1`
}`
else`
{`
$itemsPresent = $fileAndFolderList | Measure-Object`
}`
}`
else`
{`
$timer.stop()`
}`
}`
}
Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action $action | Out-Null
$timer.Enabled = $true
}
Start-Job -ScriptBlock $scriptBlock -ArgumentList $currentLocation -Name "CheckingFiles"
Wait-Job -Name "CheckingFiles"
Write-Host "All files have been copied."
}
I don't get any errors but instead of executing the commands it writes the following for both start-jobs
HasMoreData : True
StatusMessage :
Location : localhost
Command : Write-Host "Copying files to '"C:\Program Files\Ba-insight\Longitude Fast Component"'"`
Copy-Item $currentLocation"\Longitude Fast Component" "C:\Program Files\Ba-insight\Longitud
e Fast Component" -recurs`
JobStateInfo : Running
Finished : System.Threading.ManualResetEvent
InstanceId : 70ab6414-0ca4-467e-b283-20057e4141ad
Id : 1
Name : CopyingFiles
ChildJobs : {Job2}
Output : {}
Error : {}
Progress : {}
Verbose : {}
Debug : {}
Warning : {}
State : Running
It probably has to do with how i wrote the script blocks but i cannot figure out what is different from all the other examples I've seen.
Also is it possible to have a start-job wait for user input using the read-host command?
EDIT: I found the problem with why the job was not executing. The parameter was not being passed correctly. Here is a link for where i found the solution.
Passing parameters to start-job
The last example works and you have to do it like that even for a single parameter.
I still do have one question. The stuff i wrote above still gets outputed to the console. Is there any way to suppress unwanted output from the start-job cmdlet?
I think you need to include the parameters in the script block so that you can pass that down to the job using the argumentlist parameter, as it stands I believe $currentLocation will be null in the script block.
So you need something like this in your script blocks:
$scriptBlock = {
param([string]$currentLocation)
$source - Join-Path -Path $currentLocation -ChildPath "Longitude Fast Component"
$destination = "C:\Program Files\Ba-insight\Longitude Fast Component"
Write-Host "Copying files to [$destination]"
Copy-Item $source $destination -recurse
}
I'm scripting the installation and configuration procedure for my company's desktop application. We send out a kiosk and can't put everything in the installer... moving right along! I'm using Start-Process to wait for msiexec to complete.
function Run-Installer
{
param
(
[string] $msi = $(throw "Required parameter: 'msi'"),
)
if(-not(Test-Path $msi -Type Leaf))
{
throw "The installer could not be found: '$msi'"
}
$name = (Get-Item $msi).BaseName
Write-Host "Installing $name"
$p =
#(
"/I `"$msi`"", # Install this MSI
"/QN", # Quietly, without a UI
"/L*V `"$ENV:TEMP\$name.log`"" # Verbose output to this log
)
Start-Process -FilePath "msiexec" -ArgumentList $p -Wait
}
Where I want to get fancy is with the log output from msiexec. I want to stream the contents of the log to the console while the installer is running. I'm guessing there are multiple parts to the solution
Running the installer in a background, waitable manner
Tailing the log file until some condition is met (installer job completes)
Optional: Filtering the output or writing to debug/verbose for fun
function Start-FileTail {
param($path)
# Get unique source ID
$sourceID = "FileTailLine-" + [guid]::NewGuid()
$job = Start-Job -ArgumentList $path, $sourceID {
param($path,$sid)
Register-EngineEvent -SourceIdentifier $sid -Forward
do{}until(Test-Path $path)
$fs = New-Object IO.FileStream ($path, [IO.FileMode]::Open,
[IO.FileAccess]::Read, [IO.FileShare]::ReadWrite)
$sr = New-Object IO.StreamReader ($fs)
$lines = #()
while(1) {
$line = $sr.ReadLine()
$lines += $line
# Send after every 100 reads
if($lines.Count -gt 100) {
# Join lines into 1 string
$text = #($lines| where {$_} ) -join "`n"
# Only send if text was found
if($text){New-Event -SourceIdentifier $sid -MessageData $text}
$lines = #()
}
}
}
$event = Register-EngineEvent -SourceIdentifier $sourceID -Action {
Write-Host $event.MessageData
}
New-Object Object|
Add-Member -Name Job -Type NoteProperty -Value $job -PassThru|
Add-Member -Name SourceIdentifier -Type NoteProperty -Value $sourceID -PassThru
}
function Stop-FileTail {
param($TailInfo)
Remove-Job $TailInfo.Job -Force
Unregister-Event -SourceIdentifier $tail.SourceIdentifier
}
You can remove the job, and unregister the event once the install is done.
Change Write-Host to Write-Verbose for -Verbose support
EDIT: I tested my answer while installing an application, and found that it was pretty slow when reading the log file. I updated the Get-Content call to use -ReadCount 100 to send the data as arrays of lines. The Write-Host line was updated to handle the arrays.
I also found that using the -Wait switch on Start-Process caused all of the log output to be written after the install was finished. This can be fixed by using:
$msi = Start-Process -FilePath "msiexec" -ArgumentList $p -PassThru
do{}until($msi.HasExited)
EDIT 2: Hmm, I don't get all of the log file when I use -Wait and -ReadCount together. I put the reading of the log file back to how I originally had it. I'm not sure what to do about the speed yet.
EDIT 3: I've updated the code to use a StreamReader instead of Get-Content, and put the code into functions. You would then call it like:
$path = "$ENV:TEMP\$name.log"
if(Test-Path $path){Remove-Item $path}
$msi = Start-Process -FilePath "msiexec" -ArgumentList $p -PassThru
$tail = Start-FileTail $p
do{}until($msi.HasExited)
sleep 1 # Allow time to finish reading log.
Stop-FileTail $tail