SourceTree silent installation is non-blocking - atlassian-sourcetree

I am trying to install SourceTree on Windows non-interactively with my script.
I could find an option -s to install silently.
However, I kicked the SourceTreeSetup.exe with the option, it would return as soon as it run.
The setup process seems to be running on background.
I want to wait for the installation completed on my script, I couldn't find that way out.
I put a part of my powershell script here:
$url = "https://www.sourcetreeapp.com/"
$response = Invoke-WebRequest -Uri $url -UseBasicParsing
$downloadUrl = $response.Links | %{ $_.Href } | ?{ $_ -match "^.*/SourceTreeSetup-(\.?[0-9]+)+\.exe" } | Select-Object -First 1
$uri = New-Object System.Uri($downloadUrl)
$file = Split-Path $uri.AbsolutePath -Leaf
Write-Host "Downloading $file ..."
$ExePath = "$env:TEMP\$file"
if (Test-Path -Path "$ExePath")
{
Remove-Item -Path "$ExePath"
}
Invoke-WebRequest -Uri $downloadUrl -OutFile "$ExePath"
Write-Host "Installing..."
$process = Start-Process -FilePath "`"$ExePath`"" -ArgumentList #("-s") -Wait -PassThru
$exitCode = $process.ExitCode
if ($exitCode -eq 0 -or $exitCode -eq 3010)
{
Write-Host -Object "Installation successful"
Remove-Item "$ExePath"
Write-Host -Object "Cleaned up file: `"$ExePath`""
}
else
{
Write-Host -Object "Non zero exit code returned by the installation process : $exitCode."
}

Related

PowerShell Script Error in command but works in ISE

I am running a script in the ISE that essentially downloads a file from a public site:
#This PowerShell code scrapes the site and downloads the latest published file.
Param(
$Url = 'https://randomwebsite.com',
$DownloadPath = "C:\Downloads",
$LocalPath = 'C:\Temp',
$RootSite = 'https://publicsite.com',
$FileExtension = '.gz'
)
#Define the session cookie used by the site and automate acceptance. $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$cookie = New-Object System.Net.Cookie
$cookie.Name = "name"
$cookie.Value = "True"
$cookie.Domain = "www.public.com"
$session.Cookies.Add($cookie);
$FileNameDate = Get-Date -Format yyyyMMdd
$DownloadFileName = $DownloadPath + $FileNameDate + $FileExtension
$DownloadFileName
TRY{
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$WebSite = Invoke-WebRequest $Url -WebSession $session -UseBasicParsing #this gets the links we need from the main site.
$Table = $WebSite.Links | Where-Object {$_.href -like "*FetchDocument*"} | fl href #filter the results that we need.
#Write-Output $Table
$FilterTable=($Table | Select-Object -Unique | sort href -Descending) | Out-String
$TrimString = $FilterTable.Trim()
$FinalString = $RootSite + $TrimString.Trim("href :")
#Write-Verbose $FinalString | Out-String
#Start-Process powershell.exe -verb RunAs -ArgumentList "-File C:\some\path\base_server_settings.ps1" -Wait
Invoke-WebRequest $FinalString -OutFile $DownloadFileName -TimeoutSec 600
$ExpectedFileName = Get-ChildItem | Sort-Object LastAccessTime -Descending | Select-Object -First 1 $DownloadPath.Name | SELECT Name
$ExpectedFileName
Write-Host 'The latest DLA file has been downloaded and saved here:' $DownloadFileName -ForegroundColor Green
}
CATCH{
[System.Net.WebException],[System.IO.IOException]
Write "An error occured while downloading the latest file."
Write $_.Exception.Message
}
Expectation is that it downloads a file into the downloads folder and does in fact download the file when using the ISE.
When I try to run this as a command however (PowerShell.exe -file "/path/script.ps1) I get an error stating:
An error occurred while downloading the latest file. Operation is not valid due to the current state of the object.
out-lineoutput : The object of type
"Microsoft.PowerShell.Commands.Internal.Format.GroupEndData" is not
valid or not in the correct sequence. This is likely caused by a
user-specified "format-*" command which is conflicting with the
default formatting. At
\path\to\file\AutomatedFileDownload.ps1:29
char:9
$FilterTable=($Table | Select-Object -Unique | sort href -Des ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidData: (:) [out-lineoutput], InvalidOperationException
FullyQualifiedErrorId : ConsoleLineOutputOutOfSequencePacket,Microsoft.PowerShell.Commands.OutLineOutputCommand
I found several articles describing using the MTA or STA switch and I have tried to add in -MTA or -STA to the command, but it still gives me the same error in the command.
As commented, you are trying to get one link from the website, but pipe your commande to things like Format-List and Out-String, rendering the result to either nothing at all or as a single multiline string.. In both cases, this won't get you what you are after.
Not knowing the actual values of the linksof course, I suggest you try this:
Param(
$Url = 'https://randomwebsite.com',
$DownloadPath = "C:\Downloads",
$LocalPath = 'C:\Temp',
$RootSite = 'https://publicsite.com',
$FileExtension = '.gz'
)
# test if the download path exists and if not, create it
if (!(Test-Path -Path $DownloadPath -PathType Container)){
$null = New-Item -Path $DownloadPath -ItemType Directory
}
#Define the session cookie used by the site and automate acceptance.
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$cookie = New-Object System.Net.Cookie
$cookie.Name = "name"
$cookie.Value = "True"
$cookie.Domain = "www.public.com"
$session.Cookies.Add($cookie);
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
try {
$WebSite = Invoke-WebRequest -Uri $Url -WebSession $session -UseBasicParsing -ErrorAction Stop #this gets the links we need from the main site.
# get the file link
$lastLink = ($WebSite.Links | Where-Object {$_.href -like "*FetchDocument*"} | Sort-Object href -Descending | Select-Object -First 1).href
# create the file URL
$fileUrl = "$RootSite/$lastLink"
# create the full path and filename for the downloaded file
$DownloadFileName = Join-Path -Path $DownloadPath -ChildPath ('{0:yyyyMMdd}{1}' -f (Get-Date), $FileExtension)
Write-Verbose "Downloading $fileUrl as '$DownloadFileName'"
Invoke-WebRequest -Uri $fileUrl -OutFile $DownloadFileName -TimeoutSec 600 -ErrorAction Stop
# test if the file is downloaded
if (Test-Path -Path $DownloadFileName -PathType Leaf) {
Write-Host "The latest DLA file has been downloaded and saved here: $DownloadFileName" -ForegroundColor Green
}
else {
Write-Warning "File '$DownloadFileName' has NOT been downloaded"
}
}
catch [System.Net.WebException],[System.IO.IOException]{
Write-Host "An error occured while downloading the latest file.`r`n$($_.Exception.Message)" -ForegroundColor Red
}
catch {
Write-Host "An unknown error occured while downloading the latest file.`r`n$($_.Exception.Message)" -ForegroundColor Red
}

Using PowerShell to detect and remove certain type of software

I wish to modify the below script that used to be working, but now it is not working.
The purpose of this script is to uninstall and remove all Microsoft Office 2010, 2013, 2016 any version (Standard or Professional) 32 and 64 bit.
Write it to the log file when failed.
This is the script below that I've tried to modify but not working:
$AppDisplayName = 'Office'
$TARGETDIR = 'C:\LOGS'
$global:uninstallLog = Join-Path $TARGETDIR uninst.log
$WhatIfPreference = $true
$global:ShouldProcess = $false
$savedVerbosePreference = $VerbosePreference
$VerbosePreference = 'Continue'
$VerbosePreference = $savedVerbosePreference
$WhatIfPreference = $false
function Write-Log {
Param (
[Parameter(Mandatory)]
[String]$Message,
$Color = 'Green',
[string]$logfile = $global:uninstallLog
)
Process {
$msg = '[{0:dd\MM\yyy HH:mm}{1}' -f [datetime]::Now, $Message
if($logfile){$msg | Out-File $logfile -Encoding ascii}
$msg | Write-Host -ForegroundColor $Color
}
}
function Get-UninstallInfo {
Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
Get-ItemProperty |
Where-Object { $_.DisplayName -match $AppDisplayName}
}
New-Item $TARGETDIR -ItemType Directory -Force
Get-UninstallInfo |
ForEach-Object{
Write-Verbose "Processing $($_.DisplayName)"
Try {
if ($_.DisplayName -match '365') {
Write-Log "$($ENV:COMPUTERNAME) is already using O365 , no need to uninstall"
} else {
if($_.UninstallString -match '{(.*)}'){
$productCode = $matches[1]
Write-Log "Non Office 365 Detected - $productcode - Querying Uninstall command"
$arglist = '/x', $productCode, '/qn', '/norestart', '/L*v', $uninstallLog
Write-Verbose "ProductCode is set to $productCode"
if($ShouldProcess){
Write-Log "Uninstall command detected as $($_.UninstallString) Attempting silent uninstall"
Start-Process msiexec.exe -ArgumentList $arglist -Wait -NoNewWindow -ErrorAction Stop
} else {
Write-Verbose '"$ShouldProcess" is set to "$false"'
}
} else {
Write-Log 'Product code not found' -Color Red
}
}
}
Catch {
Write-Log "Error: $($_.Exception.Message)" -Color Red
}
}
This is the error log:
The "=" operator is missing after a named argument.
At line:7 char:29
Missing function body in function declaration.
At line:17 char:2
Missing function body in function declaration.
At line:23 char:2

Unable to run PowerShell uninstall script

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}"

Powershell script to install software

I am trying to make a powershell script that I can use with an RMM tool. So, basically this powershell script would be executed on the local machine. It would need to check to see if the version of the application is installed and at least version number xx. If not installed, or version is less, it would then download executable and silently install it.
I found an example online for Adobe Reader that does work, but it doesn't do the check before hand. So, this script would install Adobe Reader every time it is ran.
$tempFolder=$Env:TEMP
function runProcess ($cmd, $params) {
$p = new-object System.Diagnostics.Process
$p.StartInfo = new-object System.Diagnostics.ProcessStartInfo
$exitcode = $false
$p.StartInfo.FileName = $cmd
$p.StartInfo.Arguments = $params
$p.StartInfo.UseShellExecute = $False
$p.StartInfo.RedirectStandardError = $True
$p.StartInfo.RedirectStandardOutput = $True
$p.StartInfo.WindowStyle = 1;
$null = $p.Start()
$p.WaitForExit()
$output = $p.StandardOutput.ReadToEnd()
$exitcode = $p.ExitCode
$p.Dispose()
$exitcode
$output
}
#download installer
invoke-webrequest "ftp://ftp.adobe.com/pub/adobe/reader/win/AcrobatDC/1500720033/AcroRdrDC1500720033_en_US.msi" -OutFile "$tempFolder\AcroRdrDC1500720033_en_US.msi" -ErrorAction Stop
#run installer
$res = runProcess msiexec "/i $tempFolder\AcroRdrDC1500720033_en_US.msi /qb"
#check if return code is 0
if(0 -ne $res[0]){
return "Failed to install Adobe Reader: $($res[0]) $($res[1])"
}
#download the patch
invoke-webrequest "ftp://ftp.adobe.com/pub/adobe/reader/win/AcrobatDC/1502320070/AcroRdrDCUpd1502320070.msp" -OutFile "$tempFolder\AcroRdrDCUpd1502320070.msp" -ErrorAction Stop
#install it
$res = runProcess msiexec "/p $tempFolder\AcroRdrDCUpd1502320070.msp /qb"
#check if return code is 0
if(0 -ne $res[0]){
return "Failed to install Adobe Reader DC Patch: $($res[0]) $($res[1])"
}else{
#Attempt to silently disable the auto updater if set in this script
new-itemproperty "HKLM:\Software\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown" -name bUpdater -value 0 -ErrorAction SilentlyContinue
}
I think there are some things that may not be needed in this script. It also doesn't have the check for version.
Found this on another site, but not sure how to implement it. Also, it doesn't look like it checks for version number.
function Is-Installed( $program ) {
$x86 = ((Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall") |
Where-Object { $_.GetValue( "DisplayName" ) -like "*$program*" } ).Length -gt 0;
$x64 = ((Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall") |
Where-Object { $_.GetValue( "DisplayName" ) -like "*$program*" } ).Length -gt 0;
return $x86 -or $x64;
}
Ideally, I would like to set the parameters at the top so I could use a template for other executables. For instance
$app_name
$app_version
$app_url
$app_filename
$app_executable
$app_arguments
Any help would be greatly appreciated.
I did some more digging and found this post: How to check if a program is installed and install it if it is not?
Which allows for matching Name and Version:
$tempdir = Get-Location
$tempdir = $tempdir.tostring()
$appName = '*AirParrot*'
$appVersion = "2.6.8"
$msiFile = $tempdir+"\microsoft.interopformsredist.msi"
$msiArgs = "-qb"
function Get-InstalledApps
{
if ([IntPtr]::Size -eq 4) {
$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
$regpath = #(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |Sort DisplayName
}
$result = Get-InstalledApps | where {$_.DisplayName -like $appName -and $_.DisplayVersion -ge $appVersion}
If ($result -eq $null) {
(Start-Process -FilePath $msiFile -ArgumentList $msiArgs -Wait -Passthru).ExitCode
}

How can I catch Browsererrors like 504 in a SharePoint2013 Warm-Up Skript?

I am a freshman at PowerShell and I try to implement a SharePoint Warm-Up Skript.
This Skript is not originally mine, i only edited a existing one. Now it run nicely. But if a error occured like a DNS look up fail i want a Output in my Logfile like: "A DNS look up fail occured at URL..."
I want to catch the Browsererrors 400, 404 and 504. How can I catch them?
Here my code so far:
Function WarmUp() {
# Get URL list
Add-PSSnapIn Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$was = Get-SPWebApplication -IncludeCentralAdministration
$was |? {$_.IsAdministrationWebApplication -eq $true} |% {$caTitle = Get-SPWeb $_.Url | Select Title}
# Warm up SharePoint web applications
Write-Host "Opening Web Applications..."
$global:ie = New-Object -Com "InternetExplorer.Application"
$global:ie.Navigate("about:blank")
$global:ie.Visible = $true
$global:ieproc = (Get-Process -Name iexplore)|? {$_.MainWindowHandle -eq $global:ie.HWND}
#Navigate here to all Applications, Collections and sites
# Close IE window
if ($global:ie) {
Write-Host "Closing IE"
$global:ie | Stop-Process -Force -ErrorAction SilentlyContinue
}
$global:ieproc | Stop-Process -Force -ErrorAction SilentlyContinue
# Clean Temporary Files
Remove-item "$env:systemroot\system32\config\systemprofile\appdata\local\microsoft\Windows\temporary internet files\content.ie5\*.*" -Recurse -ErrorAction SilentlyContinue
Remove-item "$env:systemroot\syswow64\config\systemprofile\appdata\local\microsoft\Windows\temporary internet files\content.ie5\*.*" -Recurse -ErrorAction SilentlyContinue
}
Function IENavigateTo([string] $url, [int] $delayTime = 500) {
# Navigate to a given URL
if ($url) {
if ($url.ToUpper().StartsWith("HTTP")) {
Write-Host " Navigating to $url"
try {
$global:ie.Navigate($url)
#If the certificate is invalid, bypass the error to show the context.
if ($global:ie.document.url -Match "invalidcert")
{
"Bypassing SSL Certificate Error Page";
$sslbypass=$global:ie.Document.getElementsByTagName("a") | where-object {$_.id -eq "overridelink"};
$sslbypass.click();
"Sleep for 5 seconds while final page loads";
start-sleep -s 5;
}
} catch {
try {
$pid = $global:ieproc.id
} catch {}
Write-Host " IE not responding. Closing process ID $pid"
$global:ie | Stop-Process -Force -ErrorAction SilentlyContinue
$global:ieproc | Stop-Process -Force -ErrorAction SilentlyContinue
$global:ie = New-Object -Com "InternetExplorer.Application"
$global:ie.Navigate("about:blank")
$global:ie.Visible = $true
$global:ieproc = (Get-Process -Name iexplore)|? {$_.MainWindowHandle -eq $global:ie.HWND}
}
IEWaitForPage $delayTime
}
}
}
Function IEWaitForPage([int] $delayTime = 500) {
# Wait for current page to finish loading
$loaded = $false
$loop = 0
$maxLoop = 20
while ($loaded -eq $false) {
$loop++
# Wait until the page is loaded.
While ($global:ie.ReadyState -ne 4) {
Start-Sleep -Seconds 2
Write-Host "Busy!"
}
if ($loop -gt $maxLoop) {
$loaded = $true
}
[System.Threading.Thread]::Sleep($delayTime)
# If the browser is not busy, the page is loaded
if (-not $global:ie.Busy)
{
$loaded = $true
}
}
}
#Main
if(-not(Test-Path "C:\Logs\SharePoint\WarmUpLogTest")){New-Item -ItemType Directory -Path "C:\Logs\SharePoint\WarmUpLogTest"}
Start-Transcript -Path ("C:\Logs\SharePoint\WarmUpLogTest\SPWarmUp{0:yyyy-MM-dd}.txt" -f $(get-date)) -Append
WarmUp
Stop-Transcript
If you run powershell V3 or newest you can add this on top of your IENavigateTo
try{invoke-webrequest $url}
catch{$_.errordetails|select -expand message}
without V3 you can try something like
$request = [System.Net.WebRequest]::Create($url)
try{$response = $request.GetResponse() }catch{$_.exception.message}