Why my PowerShell script is not respecting the steps order? [duplicate] - powershell

This question already has answers here:
Unable to Pause or Sleep after Select-Object
(2 answers)
Closed 3 years ago.
I tried to find the solution by myself but now I'm out of ideas. I wrote a script that I want to use to check if Erlang is installed on a machine:
# Check if a Software ins installed
function Check_Program_Installed($programName) {
$x86_check = ((Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall") |
Get-ItemProperty |
Where-Object {$_.DisplayName -like "*$programName*" } |
Select-Object -Property DisplayName, UninstallString)
if(Test-Path 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
{
$x64_check = ((Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall") |
Get-ItemProperty |
Where-Object {$_.DisplayName -like "*$programName*" } |
Select-Object -Property DisplayName, UninstallString)
}
if ($x86_check -and $x64_check -eq $null){
write-host "$programName is not installed on this computer" -ForegroundColor Green
#continue
}
elseif ($x86_check -or $x64_check -ne $null){
write-host "On this computer is installed " -ForegroundColor Red
$x86_check
$x64_check
}
}
# Erlang check
Write-Host "Checking if Erlang exist " -NoNewline
Check_Program_Installed("Erlang")
Write-Host "The End: the script ends here" -ForegroundColor Yellow
But if I execute it as result the very last line is executed before Check_Program_Installed("Erlang"). Why PowerShell is not respecting the step priority?
Checking if Erlang exist On this computer is installed
The End: the script ends here
DisplayName UninstallString
----------- ---------------
Erlang OTP 21 (10.2) C:\Program Files\erl10.2\Uninstall.exe

Just add Format-Table to $x86_check and $64_check at the end. Link to answer.
# Check if a Software ins installed
function Check_Program_Installed($programName) {
$x86_check = ((Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall") |
Get-ItemProperty |
Where-Object {$_.DisplayName -like "*$programName*" } |
Select-Object -Property DisplayName, UninstallString) | Format-Table
if(Test-Path 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
{
$x64_check = ((Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall") |
Get-ItemProperty |
Where-Object {$_.DisplayName -like "*$programName*" } |
Select-Object -Property DisplayName, UninstallString) | Format-Table
}
if ($x86_check -and $x64_check -eq $null){
write-host "$programName is not installed on this computer" -ForegroundColor Green
#continue
}
elseif ($x86_check -or $x64_check -ne $null){
write-host "On this computer is installed " -ForegroundColor Red
$x86_check
$x64_check
}
}
# Erlang check
Write-Host "Checking if Erlang exist " -NoNewline
Check_Program_Installed("Erlang")
Write-Host "The End: the script ends here" -ForegroundColor Yellow

I have tried copying your code and testing it, however I cannot make the actual Check_Program_Installed function work, so it is hard for me to validate.
However, for PowerShell it is important to understand the pipeline.
Normally with everything you do, it is send to the pipeline and will appear in your console in the order that the pipeline got it.
However Write-Host does not send your text to the pipeline, it sends it directly to the console, so it could be a simple race condition where the text gets to the console faster than the pipeline object.
If you try to test using Write-Output instead of Write-Host I would guess you get it in the correct order, since Write-Output sends an object to the console through the pipeline.

Related

Part of this script that should get the ports of all running services doesnt give an output?

This script that iam working on used to work perfectly fine, but now the part that should give the ports of the running services isnt giving me an output for some reason and as iam pretty new to powershell i cant figure out why?
#Laufende Dienste
$Service = Get-Service | where{$_.Status -eq "Running"}
Write-Host "Laufende Services:" -ForegroundColor Red
foreach($x in $Service){
Write-Host $x.ServiceName
}
# Standard Dienste importen
$csvfile = Import-CSV -Path "K:\Example\Example.csv"
Foreach ($x in $csvfile) {
Write-Host $x.Name
}
#Vergleichen der Liste und laufenden Dienst
Compare-Object -ReferenceObject (get-service|where{$_.Status -eq "Running"}).servicename -DifferenceObject (import-csv K:\Example\Example.csv ).name -IncludeEqual
Write-Host "Nur in Laufenden Diensten(<=) | In Laufenden Diensten und der Liste(=) | Nur in der Liste(=>)(WICHTIG) " -ForegroundColor Green
#Ports finden
Get-CimInstance Win32_Service -Filter "State='Running'" | ForEach {
$service = $_
# TCP first
Get-NetTCPConnection -OwningProcess $_.ProcessId -ErrorAction SilentlyContinue |
Select #{l="ServiceName";e={$service.Name}}, LocalPort}

Why is the output not showing here?

So I have an odd powershell issue that I'm not sure about here, perhaps someone with more experience than me can advise?
Basically I'm searching servers for any Services & Scheduled Tasks that are not Microsoft, then checking config files for a certain name....
The code used is slightly older because some of the servers are 2008 & powershell hasn't been updated on them (long story involving ransomware)..
If I separate the code, I get the expected results, but if I combine the code, some of the output is missing....
#$ErrorActionPreference = "SilentlyContinue"
Write-Host "********* Server: $env:computername *********"
Function RunSearch() {
$searchWords = "user1","user2"
Foreach ($sw in $searchWords)
{
Write-Host "********* Searching: C Drive for: $sw *********"
Get-Childitem -Path "C:\" -Recurse -Force -Include "*.ini","*.config","*.js","*.bat","*.xml" |
Select-String -Pattern "$sw" |
Select Path,LineNumber
}
Write-Host "********* Finished Searching: $env:computername *********"
}
Function RunGetTasksSrv() {
Get-WmiObject Win32_Service | ? {$_.StartName -notlike "*localsystem*" -and $_.StartName -notlike "" -and $_.StartName -notlike "*sql_*" -and $_.StartName -notlike "NT Authority\*" -and $_.StartName -notlike "NT Service\*"} | Select Name, StartName | ft -Auto
$schtask = schtasks.exe /query /s localhost /V /FO CSV | ConvertFrom-Csv | Where-Object {$_.TaskPath -notlike "*microsoft*" -and $_.Author -notlike "*microsoft*"} | Select TaskName,"Run As User"
$schtask | where { $_."Run As User" -ne "SYSTEM" -and $_."Run As User" -ne "NETWORK SERVICE" -and $_."Run As User" -ne "INTERACTIVE" -and $_."Run As User" -ne "LOCAL SERVICE" -and $_."Run As User" -ne "Run As User" -and $_."Run As User" -notlike "*User*"}
}
RunGetTasksSrv
RunSearch
Now if I rem out #RunSearch - in the output, there will be included:
TaskName Run As User
-------- -----------
But if I unrem it out, that bit is missing (along with users listed below)...
If I take out the functions & run it as one script, again the TaskName is missing.... i.e.
Write-Host "********* Server: $env:computername *********"
Get-WmiObject Win32_Service | ? {$_.StartName -notlike "*localsystem*" -and $_.StartName -notlike "" -and $_.StartName -notlike "*sql_*" -and $_.StartName -notlike "NT Authority\*" -and $_.StartName -notlike "NT Service\*"} | Select Name, StartName | ft -Auto
$schtask = schtasks.exe /query /s localhost /V /FO CSV | ConvertFrom-Csv | Where-Object {$_.TaskPath -notlike "*microsoft*" -and $_.Author -notlike "*microsoft*"} | Select TaskName,"Run As User"
$schtask | where { $_."Run As User" -ne "SYSTEM" -and $_."Run As User" -ne "NETWORK SERVICE" -and $_."Run As User" -ne "INTERACTIVE" -and $_."Run As User" -ne "LOCAL SERVICE" -and $_."Run As User" -ne "Run As User" -and $_."Run As User" -notlike "*User*"}
$searchWords = "user1","user2"
Foreach ($sw in $searchWords)
{
Write-Host "********* Searching: C Drive for: $sw *********"
Get-Childitem -Path "C:\" -Recurse -Force -Include "*.ini","*.config","*.js","*.bat","*.xml" |
Select-String -Pattern "$sw" |
Select Path,LineNumber
}
Write-Host "********* Finished Searching: $env:computername *********"
It's really odd & I can't see why that bit is being left out....
Is it likely just because powershell is so old on one of the affected servers?
$psversiontable
Name Value
---- -----
CLRVersion 2.0.50727.8762
BuildVersion 6.1.7601.17514
PSVersion 2.0
WSManStackVersion 2.0
PSCompatibleVersions {1.0, 2.0}
SerializationVersion 1.1.0.1
PSRemotingProtocolVersion 2.1
Any advice welcomed!
Thanks
I happened to have an old 2008 VM and was able to test this. Try adding -and $_.HostName -notlike "HostName" like this to the line below. I got no hits because of the -notlike filters you have but that's likely because MS didn't have as many tasks running under the user context back then, especially on servers. Adding that last -notlike strips out the headers for the nested tasks in other folders.
$schtask = schtasks.exe /query /s localhost /V /FO CSV | ConvertFrom-Csv | Where-Object {$_.TaskPath -notlike "*microsoft*" -and $_.Author -notlike "*microsoft*" -and $_.HostName -notlike "HostName"} | Select TaskName,"Run As User"

powershell color output if matches condition

Firstly, I have a csv file with a list of programs that should be included with the default installation of Windows. The CSV looks like this.
Name;Version;Vendor;InstallDate
64 Bit HP CIO Components Installer;18.2.4;Hewlett-Packard;20210902
7-Zip 18.05 (x64 edition);18.05.00.0;Igor Pavlov;20210812
Adobe Acrobat Reader DC;20.006.20034;Adobe Systems Incorporated;20210903
Secondly, I have a powershell script that tries to compare the list of programs on a remote computer with the list of programs in the CSV file (by Name, Version and Vendor). It is supposed to output only the non matching programs.
The comparing part works perfectly, but now I would like to color the lines of output which match by Name and Vendor, but not by Version. How would I go about doing that?
This is my powershell script.
$programs =#()
$programs = Get-WmiObject –computername <ComputerName> -Class Win32_Product
foreach($program in $programs) {
foreach($defprogram in Import-Csv -Path "...\defprograms.csv" -Delimiter ';') {
if ($program.Name -eq $defprogram.Name -And $program.Version -eq $defprogram.Version -And $program.Vendor -eq $defprogram.Vendor) {
$programs = $programs -ne $program }
}}
$programs | sort-object Name | format-table -AutoSize Name,Version,Vendor,InstallDate
And this is the output of fore mentioned script.
In the example in the output, I would like to make the '64 Bit HP CIO Components Installer' colored red.
64 Bit HP CIO Components Installer 15.2.1 Hewlett-Packard 20210909
Canon Laser Printer/Scanner/Fax Extended Survey Program 2.1.2 CANON INC. 20210216
Not a nice solution, but do the job :
$a = #"
Name;Version;Vendor;InstallDate
64 Bit HP CIO Components Installer;18.2.4;Hewlett-Packard;20210902
7-Zip 18.05 (x64 edition);18.05.00.0;Igor Pavlov;20210812
Adobe Acrobat Reader DC;20.006.20034;Adobe Systems Incorporated;20210903
"#
$b = $a | Convertfrom-Csv -Delimiter ';'
$b | % {Write-Host "$($_.name);" -ForegroundColor Red -NoNewline; Write-Host "$($_.Vendor);" -NoNewline; Write-Host $($_.Installdate) ;}
You can do this by reading the CSV with default programs you need to colorize.
Then create a regex string of their .Name property. Use Format-Table as usual, but append Out-String -Stream so you can capture the resulting lines in a variable
# your code here:
$programs = #(Get-WmiObject –computername <ComputerName> -Class Win32_Product)
foreach($program in $programs) {
foreach($defprogram in Import-Csv -Path "...\defprograms.csv" -Delimiter ';') {
if ($program.Name -eq $defprogram.Name -And $program.Version -eq $defprogram.Version -And $program.Vendor -eq $defprogram.Vendor) {
$programs = $programs -ne $program }
}
}
# read the default programs and create a regex string of their Name fields
$defprograms = Import-Csv -Path "...\defprograms.csv" -Delimiter ';'
$redprograms = '({0})' -f (($defprograms.Name | ForEach-Object { [regex]::Escape($_) }) -join '|')
# Format-Table the array of objects and capture the resulting lines in variable `$table`
$table = $programs | Sort-Object Name | Format-Table -AutoSize Name,Version,Vendor,InstallDate | Out-String -Stream
# next loop through these lines and find lines that match any of the default program names
switch -Regex ($table) {
"^$redprograms\s*" {
Write-Host $_.Substring(0, $matches[1].Length) -NoNewline -ForegroundColor Red
Write-Host $_.Substring($matches[1].Length)
}
default { $_ }
}
Output would then look something like

Powershell reg enquiry and table formation

$array = #()
$path="C:\Reports\Software.txt"
$programs = Get-Content -Path $Path;
foreach ($program in $programs)
{
$system64=Get-ItemProperty
HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select DisplayName, DisplayVersion | where {$_.DisplayName -like
"*$program*"};
$system32=Get-ItemProperty
HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select
DisplayName, DisplayVersion| where {$_.DisplayName -like "*$program*"};
if ($system64)
{
Write-Host "npp query = "$system64""
Write-Host "npp query = "$system32""
Write-Host -ForegroundColor green "$program already installed!"
$array += $system64
}
elseif ($system32)
{
Write-Host "npp query = "$system64""
Write-Host "npp query = "$system32""
Write-Host -ForegroundColor green "$program already installed!"
$array += $system32
}
else
{
$array += $program
}
}
$array |Export-Csv C:\Reports\Software.csv -NoType
I am trying to create a table with specific software I have on my computer and their Version.
I would like if the Software doesn't exit to put software name under the header DisplayName and under the header DisplayVersion to put Not Installed.
It works fine if the software exists but if it doesn't it doesn't put the name under the header and put a blank line on the excel file.
example what I am taking now is :
DisplayVersion DisplayName
Gimp 2.8.22
7-Zip 18.01
Notepad++ 7.5.8
What i Want
DisplayVersion DisplayName
Gimp 2.8.22
7-Zip 18.01
Adobe Acrobat Not Installed
Notepad++ 7.5.8
any suggestions ?
Give this one a try in your else statement:
$array += #(1 | Select-Object #{Name="DisplayName";Expression={$program}},#{Name="DisplayVersion";Expression={'not installed'}})
With this, you create a custom object and add it to your array.
Hope it helps.

Like in Where-Object doesn't work in PowerShell 2

I have a piece of script to do some stuff on VMs. Here it is:
$powered = Get-VM VM-TEST4-* | Where-Object { $_.PowerState -eq 'PoweredOn'
ForEach-Object -InputObject $powered { Shutdown-VMGuest -VM $_ }
# taking snapshots etc etc
# BELOW FAILS
# start up machines in order
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-DB*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-AUX*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
Where-Object -Like -Property Name -InputObject $powered -Value "VM-TEST4-WEB*" | ForEach-Object { if ($_ -ne $null) { Start-VM -VM $_ } }
sleep -Seconds 180
My problem with this code is that nothing gets started and I only wait. Now the idea behind it was to filter out correct server type so that DB starts earlier than AUX and WEB while $_ -ne $null check protects me if Where-Object returns no results (we have an enviroment with no AUX servers).
Any idea how to make it work properly?
The simplified where-object syntax (including the -like switch parameter) was introduced in 3.0. The -like operator works fine in the standard where-object syntax. You want something like this:
Where-Object {$_.Name -like "VM-TEST4-WEB*"}
EDIT:
The -Like parameter for where-object (which looks like an operator in the simplified syntax) was added in 3.0. The -Like operator which does string comparisons has been in PowerShell since 1.0