I want to search for values in a Table created with Powershell - windows

So i have the following code to generate the entire Windows Update History:
$Session = New-Object -ComObject "Microsoft.Update.Session"
$Searcher = $Session.CreateUpdateSearcher()
$historyCount = $Searcher.GetTotalHistoryCount()
$Searcher.QueryHistory(0, $historyCount) | Select-Object Title, Description, Date,
#{name="Operation"; expression={switch($_.operation){
1 {"Installation"}; 2 {"Uninstallation"}; 3 {"Other"}
}}}
My question is how can i search in this table for updates containing KB. So for instance i want to know if the update KB4052623 has been installed. How can i write a simple piece of code to tell me KB4052326 has been found in the output of above code? Thanks in advance.

You need to assign the array returned by QueryHistory to a variable, then loop through that array to get the objects. Probably easiest to use Regex to find the KB numbers.
This code just list the found KB numbers, but you should be able test it as needed. Also, you may want to check the Description for KB numbers as well, it may tell you that it replaces old KB numbers or other important info.
$Session = New-Object -ComObject "Microsoft.Update.Session"
$Searcher = $Session.CreateUpdateSearcher()
$historyCount = $Searcher.GetTotalHistoryCount()
$MSUpdateHistory = $Searcher.QueryHistory(0, $historyCount) | Select-Object Title, Description, Date,
#{name="Operation"; expression={switch($_.operation){
1 {"Installation"}; 2 {"Uninstallation"}; 3 {"Other"}
}}}
$MSUpdateHistory | ForEach-Object {
if ($_.Title -match '(?<KBNum>KB\d+)') {
Write-Host "Found $($Matches.KBNum)"
#$_.Description
#$_.Date
#$_.Operation
}
}

Related

How to display the installed applications in alphabetical order of application name using winget?

Using winget,
winget list command displays the list of the applications currently installed in my computer, but it doesn't display the applications in alphabetical order of application name just like in the control panel,
Is there a way to display the installed applications in alphabetical order of application name using winget?
Note: The two images are from different machines.
Thanks.
As Demetrius mentioned in his comment, there isn't an ability to sort built into the client currently. However, in your screenshot I see you are using PowerShell. You can use PowerShell variables and commands to effectively sort the output. By chaining a few commands together, it is possible to re-create the table. This seemed to work for me -
$a=winget list;$a|select -First 3;$a|select -Skip 3|Sort-Object|select -First 9
I was trying to see if there was a parameter/option to accompany the winget command, and really wanted to just comment on the answer by Trenly; I had been using a similar piped command (just shorter), so he should still get the credit!
However, apparently, I must have a certain reputation score to even comment on his (or any other) answer... Yet, I can provide an answer without any rating whatsoever; go figure. So, the shorter version, similar to his answer, but without the unnecessary nested piping:
winget list|Sort-Object
You can check for ConvertFrom-FixedColumnTable function at here to convert the result of winget list to a table.
I created a function winget_list_OrderBy in order to make it simple:
function winget_list_OrderBy {
<#
.EXAMPLE
winget_list_OrderBy
.EXAMPLE
winget_list_OrderBy -OrderBy 'Name' -Arguments "--id=Git.Git"
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[string[]]
$OrderBy = 'Name', # $OrderBy can be equal to 'Name'/'Id'/'Version'/'Source' (and 'Available' if exist).
[Parameter(ValueFromPipeline)]
[string[]]
$Arguments = ''
)
# Backup the original [Console]::OutputEncoding
$encoding = [Console]::OutputEncoding
# Make PowerShell interpret winget.exe's output as UTF-8
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
(winget list $Arguments) -match '^(\p{L}|-)' | # filter out progress-display lines
ConvertFrom-FixedColumnTable | # parse output into objects
Sort-Object $OrderBy | # sort by the ID property (column)
Format-Table # display the objects in tabular format
# Restore the original [Console]::OutputEncoding afterwards
[Console]::OutputEncoding = $encoding
}
Usage is simple: winget_list_OrderBy -OrderBy $OrderBy -Arguments $Arguments or winget_list_OrderBy.
thoughts on this? It may need a little clean up, but I just converted the results to an object Array.
$apps = #("Microsoft Visual Studio Code", "Microsoft Visual Studio Code Insiders", "Visual Studio Community 2022")
$global:foundapps = [System.Collections.Generic.List[object]]::new()
foreach ($app in $apps) {
$Applist = winget search $app
$header = $Applist[1]
$nameposition = $header.indexof('Name')
$idPosition = $header.indexof('Id')
$versionPosition = $header.indexof('Version')
$sourceposition = $header.indexof('Source')
$name = $header.substring($nameposition, $idPosition).replace(' ', '')
$id = $header.substring($idPosition, ($versionPosition - $idPosition)).replace(' ', '')
$iVersiond = $header.substring($versionPosition, ($sourceposition - $versionPosition)).replace(' ', '')
$source = $header.substring($sourceposition, ($header.length - $sourceposition)).replace(' ', '')
$appstoadd = $Applist | select-object -skip 3
foreach ($AppToAdd in $appstoadd) {
$foundapps.Add([PSCustomObject] #{
"Name" = $AppToAdd.substring($nameposition, $idPosition).replace(' ', '')
"Version" = $AppToAdd.substring($versionPosition, ($sourceposition - $versionPosition)).replace(' ', '')
"ID" = $AppToAdd.substring($idPosition, ($versionPosition - $idPosition)).replace(' ', '')
"Source" = $AppToAdd.substring($sourceposition, ($header.length - $sourceposition)).replace(' ', '')
})
}
}
$foundapps |fl

How save png as jpg without saving the file in dir

I'm using FromFile to get the image out of files, and it has the following error for the png's on the FromFile line:
Exception calling "FromFile" with "1" argument(s): "The given path's
format is not supported."
So, I'm trying to convert the bmp's to jpg, (see convert line above FromFile below) but all the examples I see (that seem usable) are saving the file. I don't want to save the file in the dir. All I need is the image format, so FromFile can use it like this example. I saw ConvertTo-Jpeg, but I don't think this is a standard powershell module, or don't see how to install it.
I saw this link, but I don't think that would leave the image in the format needed by FromFile.
This is my code:
$imageFile2 = Get-ChildItem -Recurse -Path $ImageFullBasePath -Include #("*.bmp","*.jpg","*.png") | Where-Object {$_.Name -match "$($pictureName)"} #$imageFile | Select-String -Pattern '$($pictureName)' -AllMatches
Write-Host $imageFile2
if($imageFile2.Exists)
{
if($imageFile2 -Match "png")
{
$imageFile2 | .\ConvertTo-Jpeg #I don't think this will work with FromFile below
}
$image = [System.Drawing.Image]::FromFile($imageFile2) step
}
else {
Write-Host "$($imageFile2) does not exist"
}
And then I put it in excel:
$xlsx = $result | Export-Excel -Path $outFilePath -WorksheetName $errCode -Autosize -AutoFilter -FreezeTopRow -BoldTopRow -PassThru # -ClearSheet can't ClearSheet every time or it clears previous data ###left off
$ws = $xlsx.Workbook.Worksheets[$errCode]
$ws.Dimension.Columns #number of columns
$tempRowCount = $ws.Dimension.Rows #number of rows
#only change width of 3rd column
$ws.Column(3).Width
$ws.Column(3).Width = 100
#Change all row heights
for ($row = 2 ;( $row -le $tempRowCount ); $row++)
{
#Write-Host $($ws.Dimension.Rows)
#Write-Host $($row)
$ws.Row($row).Height
$ws.Row($row).Height = 150
#place the image in spreadsheet
#https://github.com/dfinke/ImportExcel/issues/1041 https://github.com/dfinke/ImportExcel/issues/993
$drawingName = "$($row.PictureID)_Col3_$($row)" #Name_ColumnIndex_RowIndex
Write-Host $image
$picture = $ws.Drawings.AddPicture("$drawingName",$image)
$picture.SetPosition($row - 1, 0, 3 - 1, 0)
if($ws.Row($row).Height -lt $image.Height * (375/500)) {
$ws.Row($row).Height = $image.Height * (375/500)
}
if($ws.Column(3).Width -lt $image.Width * (17/120)){
$ws.Column(3).Width = $image.Width * (17/120)
}
}
Update:
I just wanted to reiterate that FromFile can't be used for a png image. So where Hey Scripting Guy saves the image like this doesn't work:
$image = [drawing.image]::FromFile($imageFile2)
I figured out that the $imageFile2 path has 2 filenames in it. It must be that two met the Get-ChildItem/Where-Object/match criteria. The images look identical, but have similar names, so will be easy to process. After I split the names, it does FromFile ok.

Powershell Performance

i have a Problem with powershell Performance while searching a 40gb log file.
i Need to check if any of 1000 email adresses are included in this 40gb file. This would take 180 hours :D any ideas?
$logFolder = "H:\log.txt"
$adressen= Get-Content H:\Adressen.txt
$ergebnis = #()
foreach ($adr in $adressen){
$suche = Select-String -Path $logFolder -Pattern "\[\(\'from\'\,.*$adr.*\'\)\]" -List
$aktiv= $false
$adr
if ($suche){
$aktiv = $true
}
if ($aktiv -eq $true){
$ergebnis+=$adr + ";Ja"
}
else{
$ergebnis+=$adr + ";Nein"
}
}
$ergebnis |Out-File H:\output.txt
Don't read the file 1000 times.
Build a regexp line with all 1000 addresses (it's gonna be a huge line, but hey, much smaller than 40TB). Like:
$Pattern = "\[\(\'from\'\,.*$( $adressen -join '|' ).*\'\)\]"
Then do your Select-String, and save the result to do an address-by-address search in it. Hopefully, the result will be much smaller than 40Gb, and should be much faster.
As mentioned in the comments, replace
$ergebnis = #()
with
$ergebnis = New-Object System.Collections.ArrayList
and
$ergebnis+=$adr + ";Ja"
with
$ergebnis.add("$adr;Ja")
or respective
$ergebnis.add("$adr;Nein")
This will speed up your script quite a bit.

Change the background color of a Word file via PowerShell

How can I change the background color of a Word file via PowerShell?
$wd = New-Object -COM 'Word.Application'
$wd.Visible = $true # set to $false for production
Get-ChildItem 'C:\1\*.doc' | % {
$doc = $wd.Documents.Open($_.FullName)
# Here's the problem
$doc.Background.Fill.ForeColor.RGB = RGB(192, 192, 192)
# Switch doc view to Online Layout view
$doc.ActiveWindow.View.Type = 6
$doc.Save($true)
$doc.Close()
}
$wd.Quit()
[Runtime.InteropServices.Marshal]::ReleaseComObject($wd)
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
I'm getting 2 errors :
* RGB : The term 'RGB' is not recognized as the name of a cmdlet...
* Cannot find an overload for "Save" and the argument count: "1".
So lets address the couple of issues I know about...
RGB : The term 'RGB' is not recognized as the name of a cmdlet...
Of course you know why that is not working since PowerShell does not have a RGB cmdlet. However if you look at the input for $doc.Background.Fill.ForeColor.RGB it is looking for an integer. Referring to this related article you can see how we can make the transition.
$doc.Background.Fill.ForeColor.RGB = [long](192 + (192* 256) + (192 * 65536))
There is a caveat that needs to be addressed. While the above code does not cause an error you are going to notice that the colour is not displayed on the document. That is because backround colour is not displayed by default. You will need to enable it.
$doc.Background.Fill.Visible = $true
Ok, that one is done now how about....
Cannot find an overload for "Save" and the argument count: "1".
We use Get-Member to see exactly why
PS C:\users\Cameron\Downloads> $doc | gm
TypeName: Microsoft.Office.Interop.Word.DocumentClass
...
RunLetterWizard Method void RunLetterWizard([ref] System.Object LetterContent, [ref] System.Object WizardMode), void _Document.Ru...
Save Method void Save(), void _Document.Save()
SaveAs Method void SaveAs([ref] System.Object FileName, [ref] System.Object FileFormat, [ref] System.Object LockComments...
...
It does not take any parameters so we simply need to remove $true. The other entries are there just to show some other methods do take parameters.
$doc.Save()
.... so .... for the last one
True : The term 'True' is not recognized as the name of a cmdlet...
I did not see that error and will assume it was a typo that didnt get carried over into the code of your question.
Still miss RGB?
A crude PowerShell function that replicates the functionality with a little data validation
Function Get-RGB
{
Param(
[Parameter(Mandatory=$false)]
[ValidateRange(0,255)]
[Int]
$Red = 0,
[Parameter(Mandatory=$false)]
[ValidateRange(0,255)]
[Int]
$Green = 0,
[Parameter(Mandatory=$false)]
[ValidateRange(0,255)]
[Int]
$Blue = 0
)
Process
{
[long]($Red + ($Green * 256) + ($Blue * 65536))
}
}
Example
PS C:\users\Cameron\Downloads> Get-RGB 129 0 54
3539073
To clarify, I'm posting the final script here.
This script will loop through ALL .doc files in the selected path.
I encountered a problem only when the doc files were read-only, So I changed save() to SaveAs([ref]$name) at another folder, and that fixed the problem.
$wd = New-Object -COM 'Word.Application'
$wd.Visible = $true # set to $false for production
Get-ChildItem 'C:\songs\*.doc' | % {
$doc = $wd.Documents.Open($_.FullName)
$doc.Background.Fill.ForeColor.RGB = [long](249 + (232* 256) + (163 * 65536))
$doc.Background.Fill.Visible = $true
# Switch doc view to Online Layout view, otherwise the changes won't appear in normal view
$doc.ActiveWindow.View.Type = 6
# Replace folder path to solve "read-only" problem
$Name=($doc.Fullname).replace("songs","songs_edited")
$doc.SaveAs([ref]$Name)
$doc.Close()
}
$wd.Quit()
[Runtime.InteropServices.Marshal]::ReleaseComObject($wd)
[GC]::Collect()
[GC]::WaitForPendingFinalizers()

Windows Script to consolidate files

I have to work with a huge number of text files. I am able to consolidate the files into one single file. But I also have the use of the file name in my work and I would like to have it before the text of the file itself in excel format, preferably the first column should contain the names of files and the columns afterwards can contain the data.
Any help would be appreciated. Thanks.
Here's the Powershell script. You might need to modify it a bit to look for specific file extensions as now it's only looking for PS1 files
[System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object System.Globalization.CultureInfo("en-US")
$excel = new-Object -comobject Excel.Application
$excel.visible = $false
$workBook = $excel.Workbooks.Add()
$sheet = $workBook.Sheets.Item(1)
$sheet.Name = "Files"
$sheet.Range("A1", "B1").Font.Bold = $true
$sheet.Range("A1","A2").ColumnWidth = 40
$sheet.Range("B1","B2").ColumnWidth = 100
$sheet.Cells.Item(1,1) = "Filename"
$sheet.cells.Item(1,2) = "Content"
$files = get-childitem C:\PST -recurse | where {$_.extension -eq ".ps1"}
$index = 2
foreach($file in $files)
{
$sheet.Cells.Item($index,1) = $file.FullName
$sheet.Cells.Item($index,2) = [System.IO.File]::ReadAllText($file.FullName)
$index++
}
$workBook.SaveAs("C:\PST\1.xlsx")
$excel.Quit()
Note: I'm not pretending that it's perfect, you still need to polish it and refactor it, but at least it will give you direction

Resources