PowerShell list installed third party application update - powershell

Is there a powershell command to return a list of installed non-microsoft application updates?
Example: after installing softwareA and installing update to softwareA -> one can see the installed update in the Control Panel - View Installed Updates section
Is there a PowerShell command or simple script to return true if update is installed or false if update is not installed?

Try this and replace the program name string with your program. Use wildcards to help narrow down the program name. An expected output will be the uninstall string last write time. Making the assumption that the last write time of the files are changing when there's a patch.
$programname = "Notepad++*"
$32bit = Get-ItemProperty
HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
$64bit = Get-ItemProperty
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*
$programs = $32bit + $64bit
foreach ($program in $programs){
$program = Write-Output $program | where Displayname -like $programname
if ($program.DisplayName -ne $null) {
$LastModified = (Get-Item $program.uninstallstring).lastwritetime
$properties = #{
ProgramName = $program.DisplayName
Publisher = $program.Publisher
Version = $program.DisplayVersion
UninstallString = $program.UninstallString
LastModified = $LastModified
}
$obj = New-Object -TypeName PSObject -Property $properties
Write-Output $obj
}
}

Get-ItemProperty HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object { $_.ParentDisplayName -like "*foo*" }
This returns an object where ParentDisplayName is your program and DisplayVersion is the update version.

Related

Change a Windows product key remotely with PowerShell

I'm trying to install/activate a MAK key on remote servers. All of them have RemotePS enabled and firewall exception rules in place.
$Results = Invoke-Command -ComputerName Server1 {
$Props = #{ComputerName = $env:ComputerName}
slmgr.vbs /ipk "12345-12345-12345-12345-12345"
$LicStatus = slmgr.vbs /dlv
$Props.Add('LicenseStatus',$LicStatus)
New-Object -TypeName PSObject -Property $Props
}
$Results | Select-Object ComputerName,LicenseStatus
The above does install the MAK key but I don't get any confirmation of this process which is why I've tried adding in the license check option (/dlv) but get nothing returned in the LicenseStatus field. I'm assuming this is because it returns a multi-value maybe!?
Ultimately I'm just trying to get confirmation that the key was installed. There are articles out there about performing this using RemotePS but they all say a notification message is returned for each computer which isn't the case in my experience: https://4sysops.com/archives/change-a-product-key-remotely-with-powershell/
Any ideas how I can check this?
I would call the slmgr.vbs script using Cscript.exe in order to get the results as string array. Otherwise the system will default to using Wscript.exe which is designed to output everything in a messagebox.
Unfortunately, all output of slmgr is localized, so using a regex or something on the LicenseStatus is a no go (on a Dutch NL machine it reads 'Licentiestatus')
What you can do is using switch /dli, because that returns a string array where the last (not empty) value has the status.
Try
$Results = Invoke-Command -ComputerName Server1 {
# install MAK key
$null = cscript.exe "$env:SystemRoot\System32\slmgr.vbs" /ipk "12345-12345-12345-12345-12345"
# test LicenseStatus
$LicStatus = (((cscript.exe "$env:SystemRoot\System32\slmgr.vbs" /dli) |
Where-Object { $_ -match '\S' })[-1] -split ':', 2)[1].Trim()
# return an object
[PsCustomObject]#{
ComputerName = $env:COMPUTERNAME
LicenseStatus = $LicStatus
}
}
$Results

Calculate days since last program installed using PowerShell

What is the way to find all recently installed/repaired/modified software?
What I have been trying is to get the list from the registry, then filter it to find only relevant programs, and then try to calculate which of them was installed before a certain threshold.
This is what I have been doing:
$Installed_Software=Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate
$SoftwareInstall_Days = 40
$TheDate = (([datetime]::Now))
$Installed_recently= #()
$Installed_recently=($Installed_Software |
Where-Object {($_.DisplayName -match ('Something') -or $_.Publisher -match ('SomeOtherThing'))} |
where (($TheDate - $_.InstallDate) -le $SoftwareInstall_Days)
if ($Installed_recently) {
Write-Output "Relevant software was recently installed and/or repaired / modified.`nThese are the items:`n$Installed_recently"
}
else {
Write-Output "No relevant software was recently installed and/or repaired / modified."
}
Of course, the following condition is just an illustration of what I am failing to do:
where (($TheDate - $_.InstallDate) -le $SoftwareInstall_Days)
How can this be done?
Am I even in the right direction here?
In order to be able to compare dates, you need to parse the InstallDate string that you get from registry and then subtract it from the current date.
There are couple of formats that this string can be. On my machine, English Win10 x86, I see 2 date formats: yyyyMMdd and MM/dd/yyyy.
In the code below, I created an array that you can add additional formats to parse.
Here is the modified code - I used "Microsoft" string to test:
# Add additional property InstallDateObj that will hold the parsed DateTime object
$Installed_Software=Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate, InstallDateObj
$SoftwareInstall_Days = 40
$TheDate = (([datetime]::Now))
# Try to parse dates.
$Installed_Software.ForEach({
# add more formats if you need
[string[]] $formats = #("yyyyMMdd","MM/dd/yyyy")
$installDate = $_.InstallDate
$installedDateObj = $null;
$formats.ForEach({ [DateTime] $dt = New-Object DateTime; if([datetime]::TryParseExact($installDate, $_, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::None, [ref]$dt)) { $installedDateObj = $dt} });
$_.InstallDateObj = $installedDateObj
})
$Installed_recently= #()
$Installed_recently=($Installed_Software |
Where-Object {($_.DisplayName -match ('Microsoft') -or $_.Publisher -match ('Microsoft') -and ($_.InstallDateObj -ne $null) -and ($TheDate - $_.InstallDateObj).Days -le $SoftwareInstall_Days)})
if($Installed_recently.Count -gt 0) {
Write-Output "Relevant software was recently installed and/or repaired / modified.`nThese are the items:`n"
Write-Output $Installed_recently
}
else {
Write-Output "No relevant software was recently installed and/or repaired / modified."
}
There are methods for datetime object to add or remove minutes,hours,days,years from datetime, see script below.
For your current exmaple of getting software from registry, InstallDate need to be converted to a datetime format and then compared to a needed date from report.
#Report date 40 days
$SoftwareInstall_Days = (([datetime]::Now)).AddDays(-40)
#Getting installed software and filtering based on report date
$Installed_recently=Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, #{Name = "InstalledDate" ; Expression = {[DateTime]::ParseExact($_.InstallDate, 'yyyyMMdd', [Globalization.CultureInfo]::InvariantCulture) } }| where {$_.installeddate -ge $SoftwareInstall_Days}
#Output
$Installed_recently
Installed software also can be gathered from different places:
#Using WMI
Get-WmiObject -Class Win32_Product
#Using PS Software provider
Get-Package

Property has a value but cannot select it

I have a function that checks the registry for an uninstall key called Get-InstalledApps
Function Get-InstalledApps {
param (
[Parameter(ValueFromPipeline=$true)]
[string[]]$ComputerName = $env:COMPUTERNAME,
[string]$NameRegex = ''
)
foreach ($comp in $ComputerName) {
$keys = '','\Wow6432Node'
foreach ($key in $keys) {
try {
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $comp)
$apps = $reg.OpenSubKey("SOFTWARE$key\Microsoft\Windows\CurrentVersion\Uninstall").GetSubKeyNames()
} catch {
continue
}
foreach ($app in $apps) {
$program = $reg.OpenSubKey("SOFTWARE$key\Microsoft\Windows\CurrentVersion\Uninstall\$app")
$name = $program.GetValue('DisplayName')
if ($name -and $name -match $NameRegex) {
[pscustomobject]#{
ComputerName = $comp
DisplayName = $name
DisplayVersion = $program.GetValue('DisplayVersion')
Publisher = $program.GetValue('Publisher')
InstallDate = $program.GetValue('InstallDate')
UninstallString = $program.GetValue('UninstallString')
Bits = $(if ($key -eq '\Wow6432Node') {'64'} else {'32'})
Path = $program.name
}
}
}
}
}
}
and then I grab the DisplayName/Version for what I need. My current problem is that it only seems to work on certain machines. Example:
Get-InstalledApps | Where-Object {$_.Displayname -like "*Citrix Receiver*"}
Name Value
---- -----
InstallDate
ComputerName Computer
DisplayName Citrix Receiver 4.7
Bits 64
UninstallString C:\ProgramData\Citrix\Citrix Receiver 4.7\TrolleyExpress.exe /uninstall /cleanup
Path HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\CitrixOnlinePluginPackWeb
Publisher Citrix Systems, Inc.
DisplayVersion 14.7.0.13011
So this is great, I get what I want. Now I normally just pipe in | Select-Object Displayname -ExpandProperty Displayname and it would return "Citrix Receiver 4.7" just like I want. My problem is that on certain machines I'm getting this:
Get-InstalledApps | Where-Object {$_.Displayname -like "*Citrix Receiver*"} | Select-Object DisplayName
DisplayName
-----------
And that's it. Why is there no value listed? If I try to expandproperty I get an error because it says nothing is there, but clearly there is something there or the Where-Object would not have found it in my search. Again, in a lot cases this code works just fine and I get the value I want but on a lot of machines I'm getting what you see above.
Edited in from comments:
I run this on a user's machine and I get the results I posted. If I run it on my machine I'll get the value "Citrix Receiver 4.7" every time. Also, on my machine I don't get the Name and Value columns. Only about 1/4 of the machines I ran this code on actually gave me the value I expected. Windows 7 vs Windows 10 thing?
It looks to me like your function returns a [hashtable], but you're using it like it's an object with properties.
That happens to work fine with Where-Object because the .Member syntax works for accessing [hashtable] values, but it's not going to work with Select-Object because it's operating on actual properties.
So what can you do?
If you want to keep it as a [hashtable], and insist on doing it in a pipeline, you can use ForEach-Object:
Get-InstalledApps |
Where-Object {$_.Displayname -like "*Citrix Receiver*"} |
ForEach-Object -Process { $_.DisplayName }
or
Get-InstalledApps |
Where-Object {$_.Displayname -like "*Citrix Receiver*"} |
ForEach-Object -MemberName Item -ArgumentList DisplayName
Another thing you can do is change your function to return an object.
This is really easy to do with a [hashtable]; so say your function is about to return $hash, instead return:
New-Object -TypeName PSObject -Property $hash
Now you can use the normal suite of cmdlets and have them work as expected.
Edit: after seeing your code, it looks like you are converting your hashtable to an object already, but your output says otherwise. It wouldn't display as Name and Value columns if that were the case, so I still think something is wrong and the output is a [hashtable].
Edit 2: with info from comments about the platform differences, this seems to be happening because the object conversion is being done with the [pscustomobject] type accelerator which was added in PowerShell v3. Since the problematic machine is running Windows 7, it may be running v2 (which is what Win 7 shipped with).
Recommendations:
Get rid of Windows 7.
If you can't do that, upgrade PowerShell (Windows Management Framework) on that machine.
Either way, use New-Object as posted above.

Functions tabular output changing on some remote computers

I have this function I'm using a foreach statement block to run against a number of machines:
function Get-InstalledApps ($appStr) {
$appWC = "*$appStr*"
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\*'
)
}
$getapps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }}
Foreach ($app in $getapps | where {$_.DisplayName -like $appWC}) {
[pscustomobject]#{Computer = ($env:COMPUTERNAME + "." + $env:USERDNSDOMAIN)
AppName = ($app.displayname)
Publisher = ($app.Publisher)
DisplayVersion = ($app.DisplayVersion)
InstallDate = ($app.InstallDate)
UninstallString = ($App.UninstallString)}
}
}
Locally, it looks like this:
PS C:\windows\system32> Get-InstalledApps ibm | ft
Computer AppName Publisher DisplayVersion InstallDate UninstallString
-------- ------- --------- -------------- ----------- ---------------
Computer.domain.COM IBM Tivoli Storage Manager Client IBM 06.04.0001 20140807 MsiExec.exe /I{FF99015E-71B4-41AB-8985-67D99383A72A}
But when run remotely on some computers
(i.e:)
Invoke-Command -ComputerName $computer -ScriptBlock
${function:Get-InstalledApps} -ArgumentList $appStr
I get the above, however on others I get this:
Name Value
---- -----
UninstallString MsiExec.exe /I{68C09095-AC00-4541-B46B-0835F2BDB0CE}
Computer comp1.domain.com
Publisher IBM
InstallDate 20150122
DisplayVersion 07.01.0000
AppName IBM Tivoli Storage Manager Client
UninstallString MsiExec.exe /X{1316AC9A-7A5D-4866-B41F-4B3CF03CE52A}
Computer comp2.domain.com
Publisher IBM Corp.
InstallDate 20170226
DisplayVersion 9.2.7.53
AppName IBM BigFix Client
Without having a chance to verify PowerShell versions of some of the computers yet, I'm guessing the 2nd set of results may be as a result of being run against computers running < version 3.0.
Any way to force the output to display as a table (1st example output) on all computers?
I'm guessing the 2nd set of results may be as a result of being run against computers running < version 3.0.
If you are running that on systems that are not at least version 3 then your [pscustomobject] cast would fail since that was introduced in v3. I would have expected that to just trigger an error but instead it appears to be returning the hashtable. A compatible solution would be to use new-object instead.
New-Object -TypeName PSCustomObject -Property #{
Computer = ($env:COMPUTERNAME + "." + $env:USERDNSDOMAIN)
AppName = ($app.displayname)
Publisher = ($app.Publisher)
DisplayVersion = ($app.DisplayVersion)
InstallDate = ($app.InstallDate)
UninstallString = ($App.UninstallString)
}
Thanks Matt.
That worked, which is my preferred method.
if the app wasn't installed or the host was offline, a couple of variations of IF statements didn't seem to pick up the output at another point in the script (only displayed if it was installed) and returned as a blank line, however this seemed to be picked up by the statement blocks:
function Get-InstalledApps ($appStr) {
$appWC = "*$appStr*"
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\*'
)
}
$getapps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }}
$getapps | where {$_.DisplayName -like $appWC} | Select #{n='Computer';e={$env:COMPUTERNAME + "." + $env:USERDNSDOMAIN}},Displayname,Publisher,DisplayVersion,InstallDate,UninstallString
}

How to get server information from VMware

I have access to the VMWare GUI and I can easily export all the columns such as UPtime, IPAddress, Notes, DNS, GuestOs, State, Name and so on.
I want to right a script that can automatically get this information daily. So gar I was only able to get the server name, power state and VMhost. for some reason VMware is making it so hard to extract that information. I used the script below and I thought by adding the columns I mentioned above to this script, I should be able to retireve the data I need. But it doesn't work that way. Can someone please tell me how I can get this information?
Thanks,
Add-PSSnapin vmware.vimautomation.core
Connect-VIServer SERVERNAME
Get-VM|Select Name, VMHost, Id, PowerState
Exit 0
After digging into the system and many hours of research I found the solution. I just wish VMWare would make it easier to retrieve data or at least improve the manual.
The following code creates two files; one with the server information and another one with Uptime information.
Get-VM | select name,VMHost, #{ Name = "IP Addresses"; Expression = { $_.Guest.IPAddress }}, #{ Name = "PowerState"; Expression = { $_.Guest.State }} , #{ Name = "GuestOS"; Expression = { $_.Guest }}, Notes | Export-Csv -Path "HQstat.csv"
Get-Stat -Entity * -Stat sys.uptime.latest -Realtime -MaxSamples 1| Export-Csv -Path "HQtime.csv"
Why not use the views? They have all the information that you need. Code below assumes you are connected to the vCenter.
$vmView = Get-View -ViewType VirtualMachine -Property Name,Config,Guest,Runtime
$hostView = Get-View -ViewType HostSystem -Property Name
$date = Get-Date
Foreach ($vm in $vmView)
{
If ($vm.Runtime.BootTime -ne $null)
{
$dateDiff = $date.Subtract($vmView.Runtime.BootTime)
}
Else
{
$dateDiff = $null
}
foreach ($h in $hostView)
{
If ($vm.Runtime.Host -eq $h.MoRef)
{
$tempHost = $($h.Name)
Break
}
}
$global:Export += #([PSCustomObject]#{
VMName = $($vm.Name)
ID = $($vm.Config.Uuid) #or use $($vm.MoRef)
Host = $tempHost
PowerState = $($vm.Guest.GuestState)
IPAddress = $($vm.Guest.IPAddress)
Notes = $($vm.Config.Annotations)
UptimeMinutes = $($dateDiff.TotalMinutes)
})
$dateDiff = $null
$tempHost = $null
}
$exportFileName = "C:\temp\VMInformation.csv"
$Export | Export-Csv $exportFileName -Force -NoTypeInformation