New job triggers not working as intended on a looped trigger - powershell

I'm currently trying to setup a script to uninstall Chrome from PCs to upload onto Intune but following the MS documentation on the parameters of new job triggers just pops up with errors on invalid times. I'll paste below the documentation I'm using to get the parameters, not sure where to go in terms of resolving the error
https://learn.microsoft.com/en-us/powershell/module/psscheduledjob/new-jobtrigger?view=powershell-5.1
Register-ScheduledJob -Trigger $Trigger -RunAs32 -ScriptBlock {
$SEARCH = 'chrome$'
$INSTALLED = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, UninstallString
$INSTALLED += Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, UninstallString
$RESULT = $INSTALLED | ?{ $_.DisplayName -ne $null } | Where-Object {$_.DisplayName -match $SEARCH }
if ($RESULT.uninstallstring -like "msiexec*") {
$ARGS=(($RESULT.UninstallString -split ' ')[1] -replace '/I','/X ') + ' /q'
Start-Process msiexec.exe -ArgumentList $ARGS -Wait
} else {
$UNINSTALL_COMMAND=(($RESULT.UninstallString -split '\"')[1])
$UNINSTALL_ARGS=(($RESULT.UninstallString -split '\"')[2]) + ' --force-uninstall'
Start-Process $UNINSTALL_COMMAND -ArgumentList $UNINSTALL_ARGS -Wait
}
} -Name RemoveChrome```

Related

Get-Package - PS CORE no longer provides installed software

I'm moving a yaml pipeline so it uses PS Core. One of the steps is to parse currently installed software and uninstall it if it exists:
$appsToUninstall = Get-Package -Provider Programs -IncludeWindowsInstaller -name "*$Instance*"
if ($appsToUninstall.count -gt 0)
{
Get-Service | Where-Object { $_.Name -match "$Instance" } | Stop-Service
$appsToUninstall | Uninstall-Package -Force -Verbose
Write-Output "Uninstalled $Instance"
}
else
{
Write-Warning "Nothing to uninstall! Could not locate $Instance as an installed instance. Continuing as usual."
}
However, it would seem that the new Get-Package - no longer provides installed software.
Is there any way to use native cmdlets (not CMI/WMI [wmi is deprecated!]) to achieve that in the new PS 7+?
You can parse the registry to achieve this :
$Instance = "MyAppToUninstall"
# Initialize array to avoid errors on 32 bits applications addition
[array]$appsToUninstall = #()
# Get 64 bits programs
$appsToUninstall = Get-ItemProperty `
HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select DisplayName, UninstallString |
Where { $_.DisplayName -like "*$Instance*" }
# Add 32 bits programs
$appsToUninstall += Get-ItemProperty `
HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select DisplayName, UninstallString |
Where { $_.DisplayName -like "*$Instance*" }
if ($appsToUninstall.count -gt 0)
{
Get-Service | Where-Object { $_.Name -match "$Instance" } | Stop-Service
$appsToUninstall | ForEach-Object {
$app = $_.UninstallString.Substring(0, $_.UninstallString.IndexOf(".exe") + 4)
$arguments = $_.UninstallString.Substring($_.Uninstallstring.IndexOf(".exe") + 5)
if ($app -match "(?i)msiexec")
{
# if MSI package, replace /i parameter by /x to uninstall and
# add /passive parameter to automate the uninstallation
$arguments = "$($arguments.Replace("/I", "/x", [system.StringComparison]::InvariantCultureIgnoreCase)) /passive"
}
Start-Process -FilePath $app -ArgumentList $arguments
}
Write-Output "Uninstalled $Instance"
}
else
{
Write-Warning "Nothing to uninstall! Could not locate $Instance as an installed instance. Continuing as usual."
}
I hope this helps :)

Uninstall Applications via Reg Keys

I created a script to uninstall an application silently from the reg keys. It's worked for a few different apps without an issue. I'm trying to uninstall brave browser but, I keep getting this error below. I think the reason is because the uninstall string for brave is
C:\Program Files\BraveSoftware\Brave-Browser\Application\95.1.31.91\Installer\setup.exe" --uninstall --system-level.
The --uninstall and --system-level may be causing the error and I'm not to sure how to get around it.
Any ideas are greatly appreciated.
Error Message Below
Start-Process : This command cannot be run due to the error: The system cannot find the file specified.
At line:11 char:40
... isplayName){Start-Process -Wait -FilePath "$remove32" -ArgumentList "
$appName = "brave"
$32bit = get-itemproperty 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall*'| Where-Object { $.DisplayName -match "^$appName"} | Select-Object DisplayName, DisplayVersion, UninstallString| Where-Object { $.DisplayName -match "^$appName"}
$remove32 = $32bit.UninstallString
$64bit = get-itemproperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall*' | Where-Object { $.DisplayName -match "^$appName"} | Select-Object DisplayName, DisplayVersion, UninstallString | Where-Object { $.DisplayName -match "^$appName"}
$remove64 = $64bit.uninstallstring
if ($appName -like $32bit.DisplayName){Start-Process -Wait -FilePath $remove32 -ArgumentList "/S" -PassThru}
else {Start-Process -Wait -FilePath $remove64 -ArgumentList "/S" -PassThru}enter code here
It's kind of a pain, but you need to parse the command from the arguments somehow. Also the silent install option might not be '/S'. I think I've seen '--force-uninstall'. It's easier with msi installs. You can just use uninstall-package.
$uninstallstring = '"C:\Program Files\BraveSoftware\Brave-Browser\Application\95.1.31.91\Installer\setup.exe" --uninstall --system-level'
$split = -split $uninstallstring
& (-join $split[0..1]) $split[2] $split[3]
Note that you can get the uninstallstring from get-package too.
$uninstallstring = get-package *brave* | % { $_.metadata['uninstallstring'] }

Write-Output inside Invoke-Command scriptblock

Below is a script I'm working on to get all SQL-jobs into a CSV-file.
The script itself is working great but I have trouble with the error-handling.
I can't figure out how to get the Out-File inside the Catch-block to print to the file on my local machine instead of the remote machine I'm running the Invoke-Command to.
How do I accomplish this?
Thanks
PS. The script is written out fully as much as possible for non experienced co-workers convenience
$sqlServers = #("TEST1","TEST2")
$filePath = [Environment]::GetFolderPath("Desktop")
$dateToday = Get-Date -Format “yyMMdd HH:mm"
$dateTodayFile = Get-Date -Format “yyMMdd"
Write-Output "$dateToday $sqlServers" |
Out-File "$filePath\Log$dateTodayFile.txt" -Append
$output = Invoke-Command -ComputerName $sqlServers -ScriptBlock{
Try
{
Import-Module sqlserver -ErrorAction Stop
}
Catch
{
Write-Output "$dateToday ERROR $env:computername" |
Out-File "$filePath\Log$dateTodayFile.txt" -Append
Exit
}
$instances = $env:computername | Foreach-Object {Get-ChildItem -Path "SQLSERVER:\SQL\$_"}
ForEach ($instance in $instances){
Try
{
$instanceName = $instance.InstanceName
Get-SqlAgentJob -ServerInstance "$env:computername\$instanceName" -ErrorAction Stop |
Where-Object {$_.IsEnabled -eq "True" -and $_.LastRunDate -gt [DateTime]::Today.AddDays(-2) -and $_.OwnerLoginName -match "TEST"} |
Select-Object #{Name=‘Job name‘;Expression={$_.Name}},
#{Name=‘Description‘;Expression={$_.Description}},
#{Name=‘Instance‘;Expression={$_.Parent -Replace '[][]'}},
#{Name=‘Run outcome‘;Expression={$_.LastRunOutcome}},
#{Name=‘Run date‘;Expression={$_.LastRunDate}},
#{Name=‘Run duration‘;Expression={$_.LastRunDuration}},
#{Name=‘Job creator‘;Expression={$_.OwnerLoginName}},
#{Name=‘Runs on a schedule‘;Expression={$_.HasSchedule}},
#{Name='Schedule Type';Expression={$_.JobSchedules -join ','}}
}
Catch
{
Write-Output "$dateToday ERROR $env:computername\$instanceName" |
Out-File "$filePath\Log$dateTodayFile.txt" -Append
Exit
}
}
}
$output | Select-Object -Property * -ExcludeProperty PSComputerName,RunSpaceID,PSShowComputerName |
Sort-Object "Job name" |
Export-Csv $filePath\SQLJobInvent$dateTodayFile.csv -NoTypeInformation -Delimiter ";" -Encoding UTF8
Write-Output "$dateToday $filePath" |
Out-File "$filePath\Log$dateTodayFile.txt" -Append
Write-Output "----------------------------------------" |
Out-File "$filePath\Log$dateTodayFile.txt" -Append
Your primary issue is scope.
The $dateToday, $filePath and $dateTodayFile are all declared on the local machine, but you're trying to use them on the remote computer (script block) where they are undefined.
There are a few ways to get your variables passed to the remote computer, below are two:
# Add desired variable to ArgumentList and define it as a parameter
Invoke-Command -ComputerName $sqlServers -ArgumentList $dateToday,$filePath,$dateTodayFile -ScriptBlock {
param(
$folderPath,
$filePath,
$dateTodayFile
)
# Do something with our injected variables
Write-Output "$dateToday ERROR $env:computername" |
Out-File "$filePath\Log$dateTodayFile.txt" -Append
}
OR
# In PS ver >= 3.0 we can use 'using'
Invoke-Command -ComputerName $serverName -ScriptBlock {Write-Output $using:dateToday}

Is it possible to speed up scanning a list of computers for specific service names?

I'm kind of a newbie to PowerShell and I am currently making a simple service monitoring script. Right now I have a list of computer names and a list of service names that I scan for.
I save the scan to a log. I am wondering if there is any way I can speed up my PowerShell code? I'm not sure if I am using the quickest methods for the job.
Are there any known alternatives to this code that would scan services quicker?
$myServices = $PSScriptRoot + "\services.txt" # $PSScriptRoot references current directory
$myServers = $PSScriptRoot + "\servers.txt"
$Log = $PSScriptRoot + "\svclog.csv"
$LogLive = $PSScriptRoot + "\svclogLive.csv"
$serviceList = Get-Content $myServices
Remove-Item -Path $Log
$results = Get-Content $myServers | ForEach-Object {
foreach ($service in $serviceList) {
if ($s=get-service -computer $_ -name $service -ErrorAction SilentlyContinue)
{
$s | select MachineName, ServiceName, Status, StartType
} else {
# "$_ - Service '$service' does not exist."
}
}
}
$results | Export-CSV $Log -notypeinformation
# Create a second current log that Python can read while this script runs
Copy-Item -Path $Log -Destination $LogLive
Use Invoke-command
$serviceList = Get-Content $myServices
#some code
$results = Get-Content $myServers
Invoke-command -ComputerName $results -ScriptBlock {
Param($MyServices)
Get-Service -Name $MyServices | Select-Object -Property ServiceName, Status, StartType
} -ArgumentList $MyServices,$Null | Select-Object -Property ServiceName, Status, StartType,PSComputerName |
Export-Csv -NoTypeInformation -Path $Log
#For getting starttype in Version 2.0
Get-wmiObject -class Win32_Service -Filter "Name='BITS'" | Select-Object -Property Name, State, startMode
You can try capturing all of the target server's services in an array and looking through it rather than calling get-service on every service you are searching for:
$myServices = $PSScriptRoot + "\services.txt" # $PSScriptRoot references current directory
$myServers = $PSScriptRoot + "\servers.txt"
$Log = $PSScriptRoot + "\svclog.csv"
$LogLive = $PSScriptRoot + "\svclogLive.csv"
$serviceList = Get-Content $myServices
Remove-Item -Path $Log
$results = Get-Content $myServers | ForEach-Object {
# All of the services in one grab
$serverServices = #(Get-Service -computer $_ -ErrorAction SilentlyContinue)
if ($serverServices) {
foreach ($service in $serviceList) {
#Note: this inner use of another $_ may confuse PowerShell...
if ($s = ($serverServices | Where {$_.Name -eq $service}))
{
$s | select MachineName, ServiceName, Status, StartType
} else {
# "$_ - Service '$service' does not exist."
}
}
}
}
$results | Export-CSV $Log -notypeinformation
# Create a second current log that Python can read while this script runs
Copy-Item -Path $Log -Destination $LogLive

Remote Registry Query Powershell

I am trying to make a powershell script that gets computer names from a txt file, checks the registry to see what the current version of Flash is installed, and if it is less than 18.0.0.203, run an uninstall exe. Here is what I have been trying:
# Retrieve computer names
$Computers = Get-Content C:\Users\araff\Desktop\FlashUpdater\Servers.txt
# Select only the name from the output
#$Computers = $Computers | Select-Object -ExpandProperty Name
#Sets command to execute if the version is not 18.0.0.203
$command = #'
cmd.exe /C uninstall_flash_player.exe -uninstall
'#
#Iterate through each computer and execute the command if the version is not 18.0.0.203
[Array]$Collection = foreach ($Computer in $Computers){
$AD = Get-ADComputer $computer -Properties LastLogonDate
$ping = Test-Connection -quiet -computername $computer -Count 2
$datetime = Get-Date
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computer)
$RegKey= $Reg.OpenSubKey("SOFTWARE\Macromedia\FlashPlayerActiveX")
$version = $RegKey.GetValue("Version")
if ($version -eq '= 18.0.0.203') {
$installed = "Flash is up to date!"
}
Else {
$installed = "Removing old version..."
Invoke-Expression -Command:$command
}
New-Object -TypeName PSObject -Property #{
TimeStamp = $datetime
ComputerName = $computer
Installed = $installed
OnlineStatus = $ping
LastLogonDate = $AD.LastLogonDate
} | Select-Object TimeStamp, ComputerName, Installed, OnlineStatus, LastLogonDate
}
#Exports csv
$Collection | Export-Csv FlashUpdaterOutput.csv -NoTypeInformation
It exports the CSV just fine, but all the installed columns say "Removing" even if it is the current version. Any ideas on what I am doing wrong? Thanks!
Rather than opening a remote registry key and running cmd /c why not make a [scriptblock] and pipe it to Invoke-Command.
AsJob it and come back for the results later.
[scriptblock]$code = {
$uninst = gci "C:\Windows\System32\Macromed\Flash" -Filter "*.exe" | ?{ $_.Name -ne "FlashUtil64_18_0_0_204_ActiveX.exe" -and $_.Name -like "FlashUtil64_18_0_0_*_ActiveX.exe" }
foreach($flash in $uninst) {
Write-Host $flash.FullName
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo.FileName = $flash.FullName
$proc.StartInfo.Arguments = "-uninstall"
$proc.Start()
$proc.WaitForExit()
Write-Host ("Uninstalling {0} from {1}" -f $flash.BaseName,$env:COMPUTERNAME)
}
}
Invoke-Command -ScriptBlock $code -ComputerName frmxncsge01 -AsJob
Then later just come back and Get-Job | Receive-Job -Keep and see the results.