Get-Package command includes xml string - Need to convert to PSObject - powershell

Need Windows Update Install Date from Get-Package SwidTagText object. The object is in XML format and everything I have tried to convert doesn't work.
I am trying to switch from WMI because its terribly slow to pull back results.
Tried the ConvertFrom-XML function. Also tried ConvertFrom-String
Get-Package -ProviderName msu | Select-Object *
PropertyOfSoftwareIdentity : PropertyOfSoftwareIdentity
FastPackageReference : Definition Update for Windows Defender Antivirus - KB2267602 (Definition 1.297.486.0)
ProviderName : msu
Source :
Status : Installed
SearchKey :
FullPath : ?
PackageFilename : ?
FromTrustedSource : False
Summary : Install this update to revise the definition files that are used to detect viruses, spyware, and other potentially
unwanted software. Once you have installed this item, it cannot be removed.
SwidTags : {Definition Update for Windows Defender Antivirus - KB2267602 (Definition 1.297.486.0)}
CanonicalId : msu:Definition Update for Windows Defender Antivirus - KB2267602 (Definition 1.297.486.0)
Metadata : {summary,SupportUrl,Date,ResultCode}
SwidTagText : <?xml version="1.0" encoding="utf-16" standalone="yes"?>
<SoftwareIdentity
name="Definition Update for Windows Defender Antivirus - KB2267602 (Definition 1.297.486.0)"
xmlns="http://standards.iso.org/iso/19770/-2/2015/schema.xsd">
<Meta
summary="Install this update to revise the definition files that are used to detect viruses, spyware, and other
potentially unwanted software. Once you have installed this item, it cannot be removed."
SupportUrl="https://go.microsoft.com/fwlink/?LinkId=52661"
Date="7/5/2019 6:17:09 PM"
ResultCode="2" />
</SoftwareIdentity>
Dependencies : {}
IsCorpus :
Name : Definition Update for Windows Defender Antivirus - KB2267602 (Definition 1.297.486.0)
Version :

If you're trying to get the raw dates out of the XML you could do something like this:
$xml = ((Get-Package -ProviderName msu) | Select-Object *).SwidTagText
foreach ($item in $xml)
{
$(
$(
[xml]$item | Select-Object "InnerXml"
).InnerXml | Select-Xml -XPath "//*[#Date]"
).Node.Date
}
This gives you each entry to work with like so:
PS C:\Users\Skuld> $xml[0]
<?xml version="1.0" encoding="utf-16" standalone="yes"?>
<SoftwareIdentity
name="Update for Windows Defender Antivirus antimalware platform - KB4052623
(Version 4.18.1906.3)" xmlns="http://standards.iso.org/iso/19770/-2/2015/schema.xsd">
<Meta
summary="This package will update Windows Defender Antivirus antimalware
platform’s components on the user machine."
SupportUrl="https://go.microsoft.com/fwlink/?linkid=862339"
Date="09/07/2019 10:46:52"
ResultCode="2" />
</SoftwareIdentity>
You then use Select-XML/XPath to pick out the specific date attribute.
My example will give just a list of all the dates, but you could tweak it if you need extra information along side it.

Your code worked and now I make a function that searches for specific items inside the SwidTagText
Truly, Powershell is magical

#Use as much detail as possible. Can't process Arrays yet....
#Example = Get-Package -name "Application Name" | Where-Object { $_.ProviderName -eq "Programs" } | fl *
#Example = Get-Package -name "Application Name" | Where-Object { $_.ProviderName -eq "msi" } | fl *
#Returns any variable from XML SwidTagText
function SwidTagText-XML-Variable ( [string] $PackageName , [string] $XMLName, [string] $PackageType )
{
$PackageObject = Get-Package -name $PackageName | Where-Object { $_.ProviderName -eq $PackageType }
$xml= $PackageObject.SwidTagText
foreach ($item in $xml)
{
$XMLValue = $(
$(
[xml]$item | Select-Object "InnerXml"
).InnerXml | Select-Xml -XPath "//*[#$XMLName]"
).Node.$XMLName
}
return $XMLValue
}
#Example of Ubisoft Program and check for UninstallString
SwidTagText-XML-Variable "*ubisoft*" "UninstallString" "Programs"
So the function will search for an XML item inside the SwidTagText. Parsing and getting XML items from this is so useful

Related

Very new to PS - different results when running commands vs .ps1

trying to get some basic stuff scripted. When I run some commands in a powershell window I get a nice simple list however if I run a .ps1 file with the same command I get an unnecessarily verbose output. Is there a "less verbose" flag or setting I'm not aware of?
while this issue in particular is likely not tied to the commands I'm using, for reference I am using this command in this instance:
invoke-command -computername $servers -scriptblock {Get-Package -Name PSWindowsUpdate}
EDIT: For future readers - I solved the issue by inserting Select-Object Name as well. This provides a concise list:
invoke-command -computername $servers -scriptblock {get-package -name pswindowsupdate | select-object name}
It doesn't format the same over invoke-command. It's a serialized object now, so the type is different. I usually do
icm $comps { get-package program | select name,version }
You won't even see the output of select-string without extra help:
icm $comps { echo hi | select-string hi | select line }
Those without multiple computers can try invoke-command with localhost at the elevated powershell prompt. All the object properties get dumped because the object's serialized.
invoke-command localhost { get-package 'google chrome' }
PropertyOfSoftwareIdentity : PropertyOfSoftwareIdentity
PSComputerName : localhost
RunspaceId : d843e737-577c-4d04-be21-7ab608694c79
FastPackageReference : {E9AB118B-2341-3DD2-BD45-27B55F5F3802}
ProviderName : msi
Source :
Status : Installed
SearchKey : google chrome
FullPath :
PackageFilename : ?
FromTrustedSource : False
Summary :
SwidTags : {Microsoft.PackageManagement.Packaging.SoftwareIdentity}
CanonicalId : msi:Google Chrome/108.0.5359.99
Metadata : {ProductCode}
SwidTagText : <?xml version="1.0" encoding="utf-16" standalone="yes"?>
<SoftwareIdentity
name="Google Chrome"
version="108.0.5359.99"
versionScheme="multipartnumeric"
tagId="E9AB118B-2341-3DD2-BD45-27B55F5F3802"
xmlns="http://standards.iso.org/iso/19770/-2/2015/schema.xsd">
<Meta
ProductCode="{E9AB118B-2341-3DD2-BD45-27B55F5F3802}" />
</SoftwareIdentity>
Dependencies : {}
IsCorpus :
Name : Google Chrome
Version : 108.0.5359.99
VersionScheme : multipartnumeric
TagVersion :
TagId : E9AB118B-2341-3DD2-BD45-27B55F5F3802
IsPatch :
IsSupplemental :
AppliesToMedia :
Meta : {{ProductCode}}
Links : {}
Entities : {}
Payload :
Evidence :
Culture :
Attributes : {name,version,versionScheme,tagId}

Export/Import Printer .DRS file with PowerShell

I have 30+ Windows 2012 r2 servers, each with 30+ Zebra printers installed.
I want to be able to use PowerShell import (and export) the human readable .DRS files that can be generated with the print management UI in both the Preferences and Default locations.
I have seen lots of mentions of this: RUNDLL32.EXE PRINTUI.DLL,PrintUIEntry for import/export settings, but the file is not particularly readable, does not conform with requirements to store .DRS files, and I can't seem to get it to import the settings anyway.
I have admin on these servers, I am open to using the reg. but my understanding is that Zebras do custom things in the Devmode key - so it is not just a simple reg dump and conversion
I can use Get-PrintConfiguration and Get-CimInstance but not seeing Darkness, Speed, etc. in these outputs
$printerName = "Zebra_1"
$compNameSource = $env:COMPUTERNAME
$Printer = Get-CimInstance -ClassName Win32_Printer -Filter "Name='$PrinterName'"
$somePrinterConfig = Get-PrintConfiguration -ComputerName $compNameSource -PrinterName $printerName
$Printer.PSObject.Properties | ForEach-Object {
$_.Name, $_.Value
}
$somePrinterConfig.PSObject.Properties | ForEach-Object {
$_.Name, $_.Value
}
output:
Name : Caption
Value : Zebra_1
CimType : String
Flags : Property, ReadOnly, NotModified
IsValueModified : False
Name : Description
Value :
CimType : String
Flags : Property, ReadOnly, NotModified, NullValue
....
.DRS file:
[FileInfo]
Type=Driver Configuration File
Printer Type=201
[Barcodes]
BarcodeNum=6
[BARFONT0]
Name=EAN 13
UseHeight=1
JoinMode=2
StripTrailingSpaces=1
Kind=0
ID=1
Height=160
Expansion=1
Ratio=16908546
Flags=6164
…
[Parameters]
PaperSize=256
FormName=U
PageWidth=508
PageHeight=254
HDPI=300
VDPI=300
Orientation=3
Quantity=1
Cutter=0
BatchCut=0
Darkness=26
Speed=2
…

How to view a list of descriptions of available COM objects in PowerShell?

When I execute this powershell command to get a list of running COM objects that match the prefix "Python", I get the following output:
PS C:\Users\{path-to-arbitrary-directory}> Get-ChildItem HKLM:\Software\Classes | Where-Object {
$_.PSChildName -match '^Python[\.a-zA-Z]*$' } | Select-Object
Hive: HKEY_LOCAL_MACHINE\Software\Classes
Name Property
---- --------
Python (default) : Python ActiveX Scripting Engine
Python.Dictionary (default) : Python Dictionary
Python.Interpreter (default) : Python Interpreter
Python.TestServer (default) : Python Test COM Server
What I would like to do is just get a list of the Name and Description.
Currently I am able to get the names with this command:
PS C:\Users\{path-to-arbitrary-directory}> Get-ChildItem HKLM:\Software\Classes | Where-Object {
$_.PSChildName -match '^Python[\.a-zA-Z]*$' } | Select-Object PSChildName,Property
PSChildName Property
----------- --------
Python {(default)}
Python.Dictionary {(default)}
Python.Interpreter {(default)}
Python.TestServer {(default)}
But I can't for the life of me figure out how to show the Descriptions that I see when I execute the 1st command?
This is the output I would want:
Name Description
---- --------
Python Python ActiveX Scripting Engine
Python.Dictionary Python Dictionary
Python.Interpreter Python Interpreter
Python.TestServer Python Test COM Server
(if it helps anyone, I am also able to view the description with this command)
PS C:\Users\{path-to-arbitrary-directory}> Get-ChildItem HKLM:\Software\Classes | Where-Object {
$_.PSChildName -match '^Python[\.a-zA-Z]*$' } | Get-ItemProperty
(default) : Python ActiveX Scripting Engine
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Classes\Python
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Classes
PSChildName : Python
PSProvider : Microsoft.PowerShell.Core\Registry
...
One way to do this is the use of calculated properties. You can use Get-ItemProperty to get the value of the (default) registry key property. Then you can show this value as a calculated property.
Get-ChildItem HKLM:\software\Classes\ |
Where-Object {$_.PSChildName -match 'document'} |
Select-Object PSChildName, #{Name = "Default"; Expression = {($_ | Get-ItemProperty)."(default)"}}
Try this out. Things like this are better in functions.
function Get-COMDescription {
Param(
[parameter(Mandatory=$true)][string]$Search
)
Get-ChildItem HKLM:\Software\Classes | Where-Object {
# Match naming convention for COM Object ensure they key has a CLSID folder.
$_.PSChildName -match "^$Search\.\w+$" -and (Test-Path -Path "$($_.PSPath)\CLSID") } |
Select-Object PSChildName,#{l="Description";e={$_ | Get-ItemProperty | select -ExpandProperty "(default)" }}
}
Usage example:
PS C:\> Get-COMDescription -Search GoogleUpdate
PSChildName Description
----------- -----------
GoogleUpdate.CoCreateAsync CoCreateAsync
GoogleUpdate.CoreClass Google Update Core Class
GoogleUpdate.CoreMachineClass Google Update Core Class
GoogleUpdate.CredentialDialogMachine GoogleUpdate CredentialDialog
GoogleUpdate.OnDemandCOMClassMachine Google Update Broker Class Factory
GoogleUpdate.OnDemandCOMClassMachineFallback Google Update Legacy On Demand
GoogleUpdate.OnDemandCOMClassSvc Google Update Legacy On Demand
GoogleUpdate.PolicyStatus Google Update Policy Status Class
GoogleUpdate.ProcessLauncher Google Update Process Launcher Class
GoogleUpdate.Update3COMClassService Update3COMClass
GoogleUpdate.Update3WebMachine Google Update Broker Class Factory
GoogleUpdate.Update3WebMachineFallback GoogleUpdate Update3Web
GoogleUpdate.Update3WebSvc GoogleUpdate Update3Web
The existing answers are helpful, but let me add some background information:
The reason that the default output shows the target keys' values is that the default output formatting enumerates them, as this command reveals:
(Get-FormatData Microsoft.Win32.RegistryKey -PowerShellVersion $PSVersionTable.PSVersion).FormatViewDefinition.Control.Rows.Columns.DisplayEntry.Value
This shows:
PSChildName # column 1 - below is the script block that defines column 2
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
As you can see, Get-ItemProperty is called behind the scenes to enumerate a key's values.
As an aside: This method of enumerating values as part of the formatting leads to incorrect output when retrieving values from a remote registry - see this answer.
While calling Get-ItemProperty in the script block of a calculated property, as shown in the other answers, definitely works, there is a more efficient alternative: You can call the .GetValue() method of the Microsoft.Win32.RegistryKey instances that Get-Item outputs:
Get-ChildItem HKLM:\Software\Classes |
Where-Object PSChildName -match '^Python[\.a-z]*$' |
Select-Object #{ n='Name'; e='PSChildName' },
#{ n='(default)'; e={ $_.GetValue('') } }

Powershell - output results one after the other instead of horizontally in a table

I'm using:
Get-ItemProperty "hklm:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-Object DisplayName,InstallDate
Which returns:
Microsoft Visual C++ 2012 x64 Additional Runtime - 11.0.61030 20180205
Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161 20161005
Microsoft Policy Platform 20170927
Configuration Manager Client 20171019
Screen Pass 64 v6.6.2 20170927
What is the logic to manipulate the object in order to obtain:
Microsoft Visual C++ 2012 x64 Additional Runtime - 11.0.61030
20180205
Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161
20161005
Details are:
$logiciels = Get-ItemProperty "hklm:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-Object DisplayName, InstallDate, Usager | out-string
That variable is being passed to :
$global:infos_finales = #"
DN:$OU2
Lieu: $lieu
Noyau: $noyau - $version_noyau
Rôle: $role
Monté le: $datemontage
Adresse IP: $IPAddress
Adresse MAC: $MACAddress3
Modèle: $modele1 - $modele2
Redémarré le : $BootTimeFinal
__________________________________________________________________
Utilisateur: $user2
Connecté depuis : $temps2
$info_user2
$logiciels
"#
That $ infos_finales object containing all the info is being displayed in a richtextbox in a form (all of this is happening in a powershell GUI) with:
$richtextbox1.Text = $infos_finales
It all works but the formatting is unsuable and ugly, which is why i would prefer it vertically since i don't want to make the textbox suuuper wide to fit all the information.
EDIT AFTER ANSWER, FINAL SOLUTION USED :
$global:logiciels = Get-ItemProperty "hklm:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" |
ForEach-Object {
if ($_.DisplayName)
{
"$($_.DisplayName)"
"$($_.InstallDate)"
"$($_.Usager)"
""
}
} | out-string
If you really want to nail it to that format you could use something like this:
Get-ItemProperty "hklm:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" |
ForEach-Object {
If($_.DisplayName -and $_.InstallDate ){
"$($_.DisplayName)"; "$($_.InstallDate)"; ''
}
}

Installing executables with Powershell DSC

I am trying to install Visual Studio 2013 using PowerShell DSC but I'm running into a few issues and hoping that you guys could clear it up for me. Is DSC capable for rebooting the node and then resuming an installation of VS? Does anyone know what this error means? "vs_ultimate.exe was installed, but the specified ProductId and/or Name does not match package details"
Does anyone have any more specific examples of trying to install .exe with this method?
How does someone find out the ProductID?
Does anyone know the exact syntax of the ReturnCode?
Any help would be great!
If you have a system where the software was already installed you can find the ProductID using:
Get-WmiObject -Class Win32_Product | fl Name,Version,InstallDate,InstallSource,PackageName,IdentifyingNumber
Example output:
Name : Dell OpenManage Systems Management Software (64-Bit)
Version : 7.3.0
InstallDate : 20131009
InstallSource : c:\Installs\OMSA\
PackageName : SysMgmtx64.msi
IdentifyingNumber : {7CB08DC5-EA02-4076-BA7D-AD7736A3DE71}
Name : Microsoft ASP.NET MVC 4 Runtime
Version : 4.0.40804.0
InstallDate : 20141111
InstallSource : C:\windows\TEMP\IXP000.TMP\
PackageName : AspNetMVC4.msi
IdentifyingNumber : {3FE312D5-B862-40CE-8E4E-A6D8ABF62736}
Where IdentifyingNumber is the GUID you should use in the package resource. Example for the above Dell software:
package OMSA
{
Name = 'Dell OpenManage Systems Management Software (64-Bit)'
...
ProductId = '7CB08DC5-EA02-4076-BA7D-AD7736A3DE71'
Arguments = ...
}
Quoting Heath Stewart's comment:
the ProductId is the ProductCode of the MSI, which you can get by
opening the MSI in Orca (part of the Windows SDK) or you can install
my module from http://psmsi.codeplex.com and get it like so:
get-msitable <yourmsi.msi> -table Property | where { $_.Property -eq "ProductCode" }
The error means you have a mismatch in the Name or the ProductId of your Package resource against the msi content.
Easiest way in my experience to find both value is to use the Carbon powershell module.
Install-Module Carbon
Then simply run from powershell console:
msi "[path to your msi]"
Note: msi is an alias for Get-Msi
Example:
PS C:\Users\gigi\Downloads> msi .\node-v6.10.0-x64.msi
ProductName ProductVersion Manufacturer ProductCode
----------- -------------- ------------ -----------
Node.js 6.10.0 Node.js Foundation 84f68739-3b44-4d36-abdb-2151a23c9c3d
Copy and paste ProductName and ProductCode to your DSC package configuration and you are done.
I wrote a PowerShell function to find the product information
Function Get-InstallerProductProperty
{
# Define parameters
Param($installerFilePath,
$PropertyName)
# Verify file exists
if((Test-Path -Path $installerFilePath) -eq $true)
{
$path = $installerFilePath
$comObjWI = New-Object -ComObject WindowsInstaller.Installer
$MSIDatabase = $comObjWI.GetType().InvokeMember("OpenDatabase","InvokeMethod",$Null,$comObjWI,#($Path,0))
$Query = "SELECT Value FROM Property WHERE Property = '$PropertyName'"
$results = $View = $MSIDatabase.GetType().InvokeMember("OpenView","InvokeMethod",$null,$MSIDatabase,($Query))
$View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
$Record = $View.GetType().InvokeMember("Fetch","InvokeMethod",$null,$View,$null)
$Value = $Record.GetType().InvokeMember("StringData","GetProperty",$null,$Record,1)
# Return the product id
return $Value.Replace("{", "").Replace("}", "")
}
}