This script asks the user if they want to install a program.
$program = Read-Host -Prompt 'Would you like to install program? Please type Yes or No'
It is then lead to an If statement below
if ( $program -eq 'Yes' )
{
start-process <exe file>
}
else
{
Write-Host "Install <application> was skipped"
}
This is asked for multiple applications when prompted to install. The problem I have is that after it askes, The installation of all the applications happen at once. Is there a way to use a loop to install the first application then move on to the next? I am new to PowerShell so I am trying to make this effective.
Set an array of links of apps that you want to install. You can use CSV
$import_CSV = Import-CSV "Path_to_file"
foreach($link in $import_CSV){ your code here }
This would be kind of easy way to get you going. start-process <$link> and you are good to go.
Related
I am using the following code to uninstall google chrome software from my remote machines, But this script is executed with no output. And when i check the control panel, still the google chrome program exists. Can someone check this code?
foreach($computer in (Get-Content \path\to\the\file))
{
$temp1 = Get-WmiObject -Class Win32_Product -ComputerName $computer | where { $_.name -eq "Google Chrome"}
$temp1.Uninstall()
}
You shouldn't use the Win32_Product WMI class, one of the side effects of enumeration operations is it checks the integrity of each installed program and performs a repair installation if the integrity check fails.
It is safer to query the registry for this information instead, which also happens to contain the uninstall string for removing the product with msiexec. The uninstall string here will be formatted like MsiExec.exe /X{PRODUCT_CODE_GUID}, with PRODUCT_CODE_GUID replaced with the actual product code for that software.
Note: This approach will only work for products installed with an MSI installer (or setup executables which extract and install MSIs). For pure executable installers which make no use of MSI installation, you'll need to consult the product documentation for how to uninstall that software, and find another method (such as a well-known installation location) of identifying whether that software is installed or not.
Note 2: I'm not sure when this changed but ChromeSetup.exe no longer wraps an MSI as it used to. I have modified the code below to handle the removal of both the MSI-installed and EXE-installed versions of Chrome.
# We need to check both 32 and 64 bit registry paths
$regPaths =
"HKLM:\SOFTWARE\Wow6432node\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
# Since technically you could have 32 and 64 bit versions of the same
# software, force $uninstallStrings to be an array to cover that case
# if this is reused elsewhere. Chrome usually should only have one or the
# other, however.
$productCodes = #( $regPaths | Foreach-Object {
Get-ItemProperty "${_}\*" | Where-Object {
$_.DisplayName -eq 'Google Chrome'
}
} ).PSPath
# Run the uninstall string (formatted like
$productCodes | ForEach-Object {
$keyName = ( Get-ItemProperty $_ ).PSChildName
# GUID check (if the previous key was not a product code we'll need a different removal strategy)
if ( $keyName -match '^{[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}}$' ) {
# Use Start-Process with -Wait to wait for PowerShell to finish
# /qn suppresses the prompts for automation
$p = Start-Process -Wait msiexec -ArgumentList "/l*v ""$($pwd.FullName)/chromeuninst.log"" /X${keyName} /qn" -PassThru
# 0 means success, but 1638 means uninstallation is pending a reboot to complete
# This should still be considered a successful execution
$acceptableExitCodes = 0, 1638
}
else {
Write-Host "Stopping all running instances of chrome.exe, if any are running"
Get-Process chrome.exe -EA Ignore | Stop-Process -Force
# The registry key still has an uninstall string
# But cannot be silently removed
# So we will have to get creating and control the uninstall window with PowerShell
# We need to use the undocumented --force-uninstall parameter, added to below command
$uninstallString = "$(( Get-ItemProperty $_).UninstallString )) --force-uninstall"
# Break up the string into the executable and arguments so we can wait on it properly with Start-Process
$firstQuoteIdx = $uninstallString.IndexOf('"')
$secondQuoteIdx = $uninstallString.IndexOf('"', $firstQuoteIdx + 1)
$setupExe = $uninstallString[$firstQuoteIdx..$secondQuoteIdx] -join ''
$setupArgs = $uninstallString[( $secondQuoteIdx + 1 )..$uninstallString.Length] -join ''
Write-Host "Uninstallation command: ${setupExe} ${setupArgs}"
$p = Start-Process -Wait -FilePath $setupExe -ArgumentList $setupArgs -PassThru
# My testing shows this exits on exit code 19 for success. However, this is undocumented
# behavior so you may need to tweak the list of acceptable exit codes or remove this check
# entirely.
#
$acceptableExitCodes = 0, 19
}
if ( $p.ExitCode -notin $acceptableExitCodes ) {
Write-Error "Program exited with $($p.ExitCode)"
$p.Dispose()
exit $p.ExitCode
}
exit 0
}
Incidentally, if you already know the MSI ProductCode of a given program you don't have to find the uninstall string this way. You can simply execute msiexec /X{PRODUCT_CODE_GUID}.
If you have further problems which aren't caused by the syntax of the above this would be an operational issue and would be better troubleshot over at the https://superuser.com site.
Edit
As discovered via our chat conversation, you are installing per user and with the 79.0.3945.130 version. You can remove per user Chrome with the following command if installed per user (if the version is different you will need the correct version path):
&"C:\Users\username\AppData\Local\Google\Chrome\Application\79.0.3945.130\Installer\setup.exe" --uninstall --channel=stable --verbose-logging --force-uninstall
In the future, it is not recommended to use ChromeSetup.exe or ChromeStandaloneSetup64.exe to install and manage Chrome in an enterprise environment, you should instead use the Enterprise MSI and install system-wide so you can manage Chrome more efficiently. This is the supported way to deploy Chrome in an enterprise environment, and the script I provided will work to uninstall Chrome via msiexec and searching the registry for the {PRODUCT_CODE} as provided.
Assuming it's an msi install and remote powershell is enabled:
invoke-command -computername comp001 { uninstall-package 'google chrome' }
For the programs provider (all users), it's something like:
get-package *chrome* | % { $_.metadata['uninstallstring'] }
"C:\Program Files\Google\Chrome\Application\95.0.4638.54\Installer\setup.exe" --uninstall --channel=stable --system-level --verbose-logging
And then run that uninstallstring, but you'd have to figure out the silent uninstall option (--force-uninstall). It also runs in the background.
How to download latest Git for Windows using cmd or powershell ? (or other built-in windows software)
Currently, i have a script to check if Windows is x32 or x64 :
# eq is equal
# ne is not equal
if ((gwmi win32_operatingsystem | select osarchitecture).osarchitecture -ne "64-bit")
{
#32 bit logic here
Write "32-bit OS"
Read-Host -Prompt "Press Enter to continue"
}
else
{
#64 bit logic here
Write "64-bit OS"
Read-Host -Prompt "Press Enter to continue."
}
Thanks in advance!
The easiest approach would be to use chocolatey on the target machine. After chocolatey is installed a simple:
choco install git
downloads and installs git for windows. Check the package site first, if the latest version has been packaged. It might sometimes take a couple of days after the release of a new version before that happens.
If you are looking for a manual way to download the latest release from github, you can use the github api.
First figure out the name of the asset, that you want to download. They are listed here : https://github.com/git-for-windows/git/releases/latest. The assets you are interested in look like this:
Git-2.25.1-64-bit.exe
Git-<version>-<architecture>.exe
Now get the page you were just looking at as a json object. The github api does that for you: https://api.github.com/repos/git-for-windows/git/releases/latest
Finally find your asset in there and pass the download link to Invoke-WebRequest.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$architecture = '64-bit'
$assetName = "Git-*-$architecture.exe"
$gitHubApi = 'https://api.github.com/repos/git-for-windows/git/releases/latest'
$response = Invoke-WebRequest -Uri $gitHubApi -UseBasicParsing
$json = $response.Content | ConvertFrom-Json
$release = $json.assets | Where-Object Name -like $assetName
Invoke-WebRequest $release.browser_download_url -OutFile ".\$($release.name)"
Note: Depending on the scale on which you want to use this, you should know that the github api allows only 60 calls per hour, if you are an unauthenticated user: https://developer.github.com/v3/#rate-limiting
This question was asked a few times before but I could not find a solution to my scenario in any of them.
Basically I need the script to continue after it reboots if needed. It will check a few registry keys and will determine if the machine needs to be rebooted.
I tried using 'workflow' in various ways but could not get it to work successfully.
Here is the rough description of my code:
function (check-if-computer-needs-a-reboot){
if(this and that)
try{
return $true
}
catch{}
return $false
}
if(check-if-computer-needs-a-reboot = $true){
Write-Host "The machine is rebooting..."
Restart-Computer -Wait -Force
Write-Host "The machine was successfully rebooted"
}
else
{
Write-Host "No pending reboot"
}
Hoping the wizards of Stack-overflow can help.
Any help will be greatly appreciated!!!
To continue doing something after a reboot, you need to add a value to the Runonce registry key to specify what to do after the reboot.
Break break your script into two parts (Preboot and Postboot). Put the following at the end of Preboot.ps1:
if(check-if-computer-needs-a-reboot){
REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Runonce" /V "Postboot" /d "\"%~dpPostboot.ps1\" MyParameters" /t REG_SZ /f
Write-Host "The machine is rebooting..."
Restart-Computer -Wait -Force
}
# Fall through if reboot was not needed, so continue with part 2
Write-Host "No pending reboot... continuing"
"%~dpPostboot.ps1"
Notes:
I copied this from a .bat file. In a .bat file "%~dp0" means "the drive and path that the current script is running from". The syntax in Powershell might be a little different.
While Powershell can run other programs such as REG.EXE, Powershell has its own built-in HKLM: PSdrive. That should be more efficient than using REG.EXE. I leave it to you to do the conversion.
In your code, you test if a Boolean value "equals" $True. This comparison is never necessary unless the value isn't really Boolean and could be something other than True or False. Just test the Boolean value itself as I've shown above.
So I currently have a Powershell script that installs various software silently
What I have at the moment are all of the commands which work perfectly:
Start-Process 'C:\Files\Install files\Firefox' -ArgumentList "/S" -Wait
Start-Process 'C:\Files\Install files\Office' -ArgumentList "/s" -Wait
Start-Process 'C:\Files\Install files\Zeb' -ArgumentList "/S" -Wait
What I now want to implement is 'checking if the installation is succeeded'.
I start the program by checking the install files:
Write-Host "Checking if install files are present..."
if(Test-Path 'C:\Files\Install files\')
{
Write-Host "Files located. Installation will begin"
Write-Host ""
} else
{
Write-Host "Files not located, please check the directory"
Write-Host ""
break
}
And since the code of above is working I thought that I could maybe to the same for installing my software? But using the software directory as a test-path function:
Write-Host "Installing Firefox"
Start-Process 'C:\Files\Install files\Firefox' -ArgumentList "/S" -Wait
if (Test-Path C:\Program Files(x86)\Firefox)
{
Write-Host "Firefox succesfully installed!"
}else
{
Write-Host "Error, FireFox hasn't been installed"
}
.... And continue like this for the other programs
I thought 'why not?' since all of the PC's are the same and I must admit that the code also works.
But how would someone would judge this method? And what are alternatives?
Thanks
Just a note, I think this is more a question for Server Fault or Super User.
There are some high end tools like Desired State Configuration that handle all this but it really depends on the environment. We build standalone servers in an offsite datacentre and it's so much more convenient and less setup to just have a script that installs and configures everything that can't be imaged. Sure it takes two hours to complete but it's unattended, and since the OS is installed from a template we guarantee they're all exactly the same.
Perhaps I wouldn't recommend this method if the hardware were drastically different or if not imaging the OS to run the script on, I used to have a batch file to deploy customer desktops (back in WinXP days) but eventually cut it down to just detect the model and install drivers because it was too much hassle to maintain. I would also not recommend this method if it will be maintained by more than one person, or if the person maintaining it will be maybe/definitely not long term staff.
Overall it's up to the company. DSC is certainly a more professional and complete approach and much more easily maintained by multiple people, so put it to them and if they won't devote resources for a deployment server and training then you don't have much choice either way.
I would recommend to have a look at something more professionell like "PowerShell App Deployment Toolkit". (http://psappdeploytoolkit.com/)
It will take some time for you to start, but it is not that complex.
It outputs logs and so on.
I would like to have a Windows 2003 server fire a script to fire another script in a separate Windows Server 2008 computer.
I have been told that Powershell can do that, and that's fine, but I need more specific details.
Does anyone have any tips for this?
Thanks!
psexec from SysInternals
Look into the syntax for the AT command. You can use it to schedule a process to run on a remote machine.
The AT command schedules commands and programs to run on a computer at
a specified time and date. The Schedule service must be running to use
the AT command.
AT [\\computername] [ [id] [/DELETE] | /DELETE [/YES]]
AT [\\computername] time [/INTERACTIVE]
[ /EVERY:date[,...] | /NEXT:date[,...]] "command"
\\computername Specifies a remote computer. Commands are scheduled on the
local computer if this parameter is omitted.
id Is an identification number assigned to a scheduled
command.
/delete Cancels a scheduled command. If id is omitted, all the
scheduled commands on the computer are canceled.
/yes Used with cancel all jobs command when no further
confirmation is desired.
time Specifies the time when command is to run.
/interactive Allows the job to interact with the desktop of the user
who is logged on at the time the job runs.
/every:date[,...] Runs the command on each specified day(s) of the week or
month. If date is omitted, the current day of the month
is assumed.
/next:date[,...] Runs the specified command on the next occurrence of the
day (for example, next Thursday). If date is omitted, the
current day of the month is assumed.
"command" Is the Windows NT command, or batch program to be run.
easiest way that is use will be in two steps
a. installing cygwin to remote pc
b. run ssh hudson#mcs '/cygdrive/c/path_to_script.bat'
Speaking about PsExec, I would strongly suggest to use Cygwin/OpenSSH instead.
SSH has multiple advantages (over tools like PsExec or even custom-made services).
For example, try to use with PsExec and implement what these bash / ssh command lines do:
ssh user#remotehost "find . -name something" 2> all.errors.txt
ssh user#remotehost "grep -r something ."
if [ "$?" == "0" ]
then
echo "FOUND"
else
echo "NOT FOUND"
fi
Good Luck!
SSH transfers (!) remote stdout / stderr / exit status to local shell for inspection
(killer feature and common requirement to integrate remote execution into logic of local scripts)
Cygwin/OpenSSH provides standard POSIX shell environment
(efficient time investment, fundamental tools, cross-platform ready, compatible habits, etc.)
You can still/always run all native Windows application
(including automatic execution of *.bat files by cmd processor)
You can configure password-less auth using public keys
(think about unattended automated tasks)
Tip
There was one requirement I had problems with initially:
background sshd service had to execute apps in user's graphical session
(to make application window appear in desktop environment).
The acceptable solution for me was running sshd service directly in user's GUI session
(start automatically when user logs in, follow the link to see configuration file changes):
/usr/sbin/sshd -f /home/user/sshd_config
The accepted solution from http://www.experts-exchange.com/OS/Microsoft_Operating_Systems/Q_22959948.html is:
What I provide was a script that takes
parameters... In this case it takes 4.
1) Server: if you pass -server it will
only do that one server 2) List: You
can provide a list file of servers.
3) Service: Name of the service you
want to modify 4) Verbose: is not
used here.
I did have some mistakes that I
changed in the following code. To use
cut/paste the code into a file called
Set-RemoteService.ps1. Make sure to
set your executionpolicy to run
scripts... it will not by default. You
do that by using the
set-executionpolicy cmdlet. PS>
Set-Executionpolicy "RemoteSigned" to
run the script you do PS>
C:\PathToScript\Set-RemoteService.ps1
-list c:\ServerList.txt -service "DHCP"
######################### Param($server,$list,$service,[switch]$verbose)
if($Verbose){$VerbosePreference =
"Continue"} if($list) {
foreach($srv in (get-content $list))
{
$query = "Select * from Win32_Service where Name='$service'"
$myService = get-WmiObject -query $query -computer $srv
$myService.ChangeStartMode("Automatic")
$myService.Start()
} } if($server) {
$query = "Select * from Win32_Service where Name='$service'"
$myService = get-WmiObject -query $query -computer $server
$myService.ChangeStartMode("Automatic")
$myService.Start() }