Powershell to list all the microsoft updates on a computer? - powershell

I am trying to figure out a script that'll help me list all the Microsoft updates installed on my system.
I am using
Get-Hotfix
to do the same but i am not getting desired results. Neither is:
Get-WmiObject -Class "win32_quickfixengineering" |
where $_.name = "Microsoft"
this working for me.
Please help.

You could use this script (didn't find a way to display the description with Get-HotFix).
It lists the programs found in the Uninstall keys of the windows registry, and matches the name againt the $filter string.
You can remotely get this information from another computer by changing $computerName (currently the local host).
#store computer name in a variable
$computerName = $env:COMPUTERNAME
#store filter string in a variable
$filter = "KB"
#declare results array
$result = #()
#store registry key paths in an array
$keyList = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\',
'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\'
#open registry hive HKLM
$hive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $computerName)
#for each key path
foreach($key in $keyList) {
#open key
$uninstallKey = $hive.OpenSubKey($key)
#if key has been opened
if($uninstallKey) {
#list program keys
$programList = $uninstallKey.GetSubKeyNames()
#for each key
foreach($program in $programList) {
#get the program name
$programName = $uninstallKey.OpenSubKey($program).GetValue("DisplayName")
#if the program name is not null and matches our filter
if(($programName) -and ($programName -like "*$filter*")) {
#add program name to results array
$result += $programName
}
}
}
}
#sort and output results array
$result | Sort-Object

Related

Get Driver Install Dates

I am attempting to get a list of installed Drivers and the date they were installed via PowerShell. I have attempted the following:
Get-WmiObject Win32_PnPSignedDriver| select devicename, InstallDate
And the InstallDate property is blank for 100% of the items...
I also tried:
Get-WindowsDriver -Online -All | Sort-Object Date
There is a date there, but it is not the install date. Probably the date the driver was compiled is my assumption.
As a misc note: MMC is not an alternative in this case. Long story.
I found the InstallDate associated with the driver files and not with the driver objects from WMI.
Please find the code bellow for an example:
# Initializing parameters (modify the Output folder by changing the $TargetDirectory define in the first line)
$TargetDirectory = "C:\temp\DriversINFTest\"
$hostname = $ENV:COMPUTERNAME
# Get Third Party drivers used, that are not provided by Microsoft and presumably included in the OS
$drivers = Get-WmiObject Win32_PNPSignedDriver | where {$_.DriverProviderName -ne "Microsoft"}
# Initialize the list of detected driver packages as an array
$DriverFolders = #()
foreach ($d in $drivers) {
# We initialize the list of driver files for each driver
$DriverFiles = #()
# For each driver instance from WMI class Win32_PNPSignedDriver, we compose the related WMI object name from the other WMI driver class, Win32_PNPSignedDriverCIMDataFile
$ModifiedDeviceID = $d.DeviceID -replace "\\", "\\"
$Antecedent = "\\" + $hostname + "\ROOT\cimv2:Win32_PNPSignedDriver.DeviceID=""$ModifiedDeviceID"""
# Get all related driver files for each driver listed in WMI class Win32_PNPSignedDriver
$DriverFiles += Get-WmiObject Win32_PNPSignedDriverCIMDataFile | where {$_.Antecedent -eq $Antecedent}
if ($DriverFiles -ne $null) {
foreach ($i in $DriverFiles) {
# We elliminate double backslashes from the file paths
$path = $i.Dependent.Split("=")[1] -replace '\\\\', '\'
# We elliminate the trailing and ending quotes from the file paths
$path2 = $path.Substring(1,$path.Length-2)
# On the first pass, we only process the INF files as there is a very low chance of existing more than one driver package on the same machine, with the same INF file legth
if ($path2.Split("\\")[$path2.Split("\\").Length-1].split(".")[1] -eq "inf") {
# We get the file attributes for each INF file
$CurrentDeviceID = $d.DeviceID
Write-Host "Current Device ID is $CurrentDeviceID"
$filedetails = $i.Dependent
Write-Host "Current inf file is $filedetails"
$where = $filedetails.split("=")[1]
$query = "select * from CIM_DataFile where Name=$where"
$InstallDate = Get-WmiObject -Namespace "ROOT\CIMV2" -Query $query
$ReadableDate = $InstallDate.ConvertToDateTime($InstallDate.InstallDate)
Write-Host "Installation date is:"
$ReadableDate
} # End of if ($path2.Split("\\")[$path2.Split("\\").Length-1].split(".")[1] -eq "inf")
} # End of foreach ($i in $DriverFiles)
} # End of if ($DriverFiles -ne $null)
} # End of foreach ($d in $drivers)

Search registry in Powershell for specific keys and values within those keys

I'm fairly close to a solution, but I just can't get there. What I'm trying to do is search for installed MS Office updates. The best way I've found is to search the HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall key.
What I then want to do is only look at sub-keys like *{90140000-001* (which indicates Office) and the search each found sub-key's DisplayName property for "(KB*" - which will indicate it is an update to Office rather than a component.
What I have so far is this:
$ErrorActionPreference = "SilentlyContinue"
Get-ChildItem "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\" -Recurse | Where-Object{$_ -like "*{90140000-001*"} | foreach {
Get-ItemProperty $_.DisplayName}
But it produces blank output.
Is anyone able to please help me finish this off?
You can use the GetValue method of the current RegistryKey to retrieve the DisplayName:
Get-ChildItem "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\" -Recurse |
Where Name -like '*{90140000-001*' | foreach {
$_.GetValue("DisplayName")
}
If you want to query remote computers:
#config
$computerName = $env:COMPUTERNAME
$hive = "LocalMachine"
#32-bit Office : SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
#64-bit Office : SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
#Office 2013 : look for keys named "{90140000-001*"
#Office 2016 : look for keys named "{90160000-001*"
$regPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
#if remote computer is reachable
if(Test-Connection $computerName -Quiet -Count 2 -ErrorAction 0) {
try {
#open remote registry
$base = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::$hive, $ComputerName)
#open desired key with edit permission
$key = $base.OpenSubKey($regPath)
foreach($subkeyName in $key.GetSubKeyNames()) {
if($subkeyName -match "{90160000-001") {
$subkey = $key.OpenSubKey($subkeyName)
$displayName = $subkey.GetValue("DisplayName")
if($displayName -match "\(KB") {
$displayName
}
}
}
#close subkey, key and registry connection
$subkey.Close()
$key.Close()
$base.Close()
} catch {
Throw "Remote registry is not accessible (check `$hive and `$regPath, and run this script as administrator)."
}
} else {
Throw "Remote computer is not reachable."
}

Read registry settings recursively with Powershell

I'm trying to read recursively some registry settings with Powershell.
This is what I tried:
$registry = Get-ChildItem "HKLM:\Software\Wow6432Node\EODDashBoard\moveFilesOverflow" -Recurse
Foreach($a in $registry) {
Write-Output $a.PSChildName
$subkeys = (Get-ItemProperty $a.pspath)
Write-Output $subkeys.LastDateTime_EndScript
}
I would like to be able to list all the registry keys with their value, without knowning the registry keys.
With my script I have a variable $subkeys that contains objects I can access. (For example here I can access $subkeys.LastDateTime_EndScript)
However what I would like is to list all the registry keys with their value without knowing the registry keys in my script, ie something like this:
Foreach ($subkey in $subkeys) {
Write-Output $subkey.keyname
Write-Output $subkey.value
}
Is it possible?
Thanks,
You can loop through the properties. Using your idea that would be:
foreach ($a in $registry) {
$a.Property | ForEach-Object {
Write-Output $_
Write-Output $a.GetValue($_)
}
}
Output:
InstallDir
C:\Program Files\Common Files\Apple\Apple Application Support\
UserVisibleVersion
4.1.1
Version
4.1.1
....
That's pretty messy though. The usual way to output data like this in powershell is to create an object with properties for Name and Value so you have one object per registry-value. This is easier to process (if you're going to use it for something in the script) and easier to look at in console.
foreach ($a in $registry) {
$a.Property | Select-Object #{name="Value";expression={$_}}, #{name="Data";expression={$a.GetValue($_)}}
}
or
foreach ($a in $registry) {
($a | Get-ItemProperty).Psobject.Properties |
#Exclude powershell-properties in the object
Where-Object { $_.Name -cnotlike 'PS*' } |
Select-Object Name, Value
}
Output:
Value Data
----- ----
InstallDir C:\Program Files\Common Files\Apple\Apple Application Support\
UserVisibleVersion 4.1.1
Version 4.1.1
....

Select option from Array

I am working on a side project and to make it easier for managment since almost all of out server names are 15 charactors long I started to look for an RDP managment option but none that I liked; so I started to write one and I am down to only one issue, what do I do to manage if the user types not enough for a search so two servers will match the Query. I think I will have to put it in an array and then let them select the server they meant. Here is what I have so far
function Connect-RDP
{
param (
[Parameter(Mandatory = $true)]
$ComputerName,
[System.Management.Automation.Credential()]
$Credential
)
# take each computername and process it individually
$ComputerName | ForEach-Object{
Try
{
$Computer = $_
$ConnectionDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=$computer)" -ErrorAction Stop | Select-Object -ExpandProperty DNSHostName
$ConnectionSearchDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=*$computer*)" | Select -Exp DNSHostName
Write-host $ConnectionDNS
Write-host $ConnectionSearchDNS
if ($ConnectionDNS){
#mstsc.exe /v ($ConnectionDNS) /f
}Else{
#mstsc.exe /v ($ConnectionSearchDNS) /f
}
}
catch
{
Write-Host "Could not locate computer '$Computer' in AD." -ForegroundColor Red
}
}
}
Basically I am looking for a way to manage if a user types server1
that it will ask does he want to connect to Server10 or Server11 since both of them match the filter.
Another option for presenting choices to the user is Out-GridView, with the -OutPutMode switch.
Borrowing from Matt's example:
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$IDX = 0
$(foreach ($item in $selection){
$item | select #{l='IDX';e={$IDX}},Name
$IDX++}) |
Out-GridView -Title 'Select one or more folders to use' -OutputMode Multiple |
foreach { $selection[$_.IDX] }
}
else {$Selection}
This example allows for selection of multiple folders, but can you can limit them to a single folder by simply switching -OutPutMode to Single
I'm sure what mjolinor has it great. I just wanted to show another approach using PromptForChoice. In the following example we take the results from Get-ChildItem and if there is more than one we build a collection of choices. The user would select one and then that object would be passed to the next step.
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$title = "Folder Selection"
$message = "Which folder would you like to use?"
# Build the choices menu
$choices = #()
For($index = 0; $index -lt $selection.Count; $index++){
$choices += New-Object System.Management.Automation.Host.ChoiceDescription ($selection[$index]).Name, ($selection[$index]).FullName
}
$options = [System.Management.Automation.Host.ChoiceDescription[]]$choices
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
$selection = $selection[$result]
}
$selection
-Directory requires PowerShell v3 but you are using 4 so you would be good.
In ISE it would look like this:
In standard console you would see something like this
As of now you would have to type the whole folder name to select the choice in the prompt. It is hard to get a unique value across multiple choices for the shortcut also called the accelerator key. Think of it as a way to be sure they make the correct choice!

Find read-host value in file list

I have now moved on to automating IDRAC. My script below will look at a list of servernames and IDRAC IP's. I would like to have a user enter a single Servername "S0000A01PX' or whatever and have it search in a master list for the server IDRAC IP. Right now, the script as it is opens every server's virtual console in the list. I just need it to select only the user entry. How do I have this search the file, find the IP next to the user entered servername, and open only the IDRAC for that servername?
example of what's in the CSV
computername iPiDRAC
S0000A01PX 10.122.2.11
Script
$machinename = ""
$file = Import-Csv 'c:\temp\powershell\Branch Server IDRAC.csv'
$filelength = $file.length
$machine = Read-Host 'What is your Server?'
foreach ($line in $file){
$DRACip = $line.iPiDRAC
$DRACpw=cscript c:\PassGen1.vbs $machinename
$DRACpw=$DRACpw[3]
$DRACip
$machinename
$DRACPW
$openIDRAC="http://"+$DRACip+"/console"
$openIDRAC
start $openIDRAC
write-host "----------"
}
If you are just looking to have a general match and return launch all DRACs that match a simple change can get you there. Where-Object{$_.computername -match $machine}
$machinename = ""
$file = Import-Csv 'c:\temp\powershell\Branch Server IDRAC.csv'
$machinename = Read-Host 'What is your Server?'
$file | Where-Object{$_.computername -match $machinename} |ForEach-Object{
$DRACip = $_.iPiDRAC
$DRACpw=cscript c:\PassGen1.vbs $machinename
$DRACpw=$DRACpw[3]
$DRACip
$machinename
$DRACPW
$openIDRAC="http://"+$DRACip+"/console"
$openIDRAC
start $openIDRAC
write-host "----------"
}
You are mixing output and write-host which could get you into trouble so I would remove some of that extra fluff which I will assume is just there for testing. I don't see where you are using $DRACpw or $machinename unless that is somehow what you need to see in order to sign into the DRAC
I think you want a bit of Where-Object.
For this CSV file:
computername,iPiDRAC
S0000A01PX,10.122.2.11
Import the file as you have been:
$file = Import-Csv 'c:\temp\powershell\Branch Server IDRAC.csv'
Ask your question:
$machine = Read-Host 'What is your Server?'
Look for the server name in your $file array, which will output an object representing that line in the CSV, then get its iPiDRAC property:
($file | Where-Object {$_.computername -eq $machine}).iPiDRAC
10.122.2.11