Get upgrade code for product code from registry - powershell

I need a hint how to get the upgrade code from an installed MSI out of the registry. Actually I'm having the product code, which can be retrieved from HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\.
Now I want to retrieve the upgrade code (based on the product code) from HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes.
My problem is that the product code is used as value-name, which means I've a REG_SZ where the name is the product code guid and the value is empty.
One way to retrieve the product code might be:
PS HKLM:\SOFTW...Codes> Get-ItemProperty * | select -First 1 | gm
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
42F79228D77BA4A4EB5150F3DC090CE3 NoteProperty System.String 42F79228D77BA4A4EB5150F3DC090CE3=
...
How can I check if a PSCustomObject has the property 42F79228D77BA4A4EB5150F3DC090CE3?
Does anybody knows if there is a more elegant way?

This is how you can check. Working on that elegant solution...
$properties = Get-ItemProperty * | select -first 1 | Get-Member | Where-object {$_.MemberType -eq "NoteProperty"}
if("42F79228D77BA4A4EB5150F3DC090CE3" -in $properties.Name){
Write-Output "It's in there!"
}
Edit
This is a bit more elegant. It goes to the HKLM path, and checks for a PSChildName (Registry Key) that is the same as the code.
If found, it will return the Name and property. If not found, the variable $codeExists will be $null.
$code = "42F79228D77BA4A4EB5150F3DC090CE3"
$codeExists = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes" | Where-Object {$_.PSChildName -eq $code}
if($codeExists){
Write-Output "It's in there!"
}

Related

Powershell - how to extract a value from Object[]

I'm trying to figure out how to grab the value associated with AzureWebJobsStorage variable from a Object[] in powershell. Here's the logic so far:
$azAppSettingsOutput = az functionapp config appsettings list --name somename --resource-group myResource --subscription subscription | ConvertFrom-Json
Write-Output $azAppSettingsOutput.GetType().name
Write-Output $azAppSettingsOutput | Get-Member
Write-Output $azAppSettingsOutput.name
This is the output I see:
PS C:\Users\me> .\test.ps1
Object[]
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
name NoteProperty string name=APPINSIGHTS_INSTRUMENTATIONKEY
slotSetting NoteProperty bool slotSetting=False
value NoteProperty string value=asdfasdf-asdf-asdf-asdf-asdf-asdf
APPINSIGHTS_INSTRUMENTATIONKEY
AzureWebJobsStorage
FUNCTIONS_EXTENSION_VERSION
FUNCTIONS_WORKER_RUNTIME
WEBSITE_RUN_FROM_PACKAGE
StorageTableName
PS C:\Users\me\>
I know I can loop through like this:
foreach($setting in $azAppSettingsOutput) {
Write-Output $setting.name
$value = $setting.value
And then I can add an if statement to check if the name matches "AzureWebJobsStorage" but just wondering if there's a simpler way.
Thanks.
Use the Where-Object cmdlet to filter your data:
$azAppSettingsOutput |Where-Object name -eq 'AzureWebJobsStorage'
This will filter out any objects except for those where the name property equals "AzureWebJobsStorage"

How use "Where-Object" condition with '[PScustomobject]'?

I have some code:
$output = [PSCustomObject]#{
Name = $ws.UsedRange.Columns.Item(1).Value2
Department = $ws.UsedRange.Columns.Item(3).Value2
}
$output | GM
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Department NoteProperty System.Object[,] Department=System.Object[,]
Name NoteProperty System.Object[,] Name=System.Object[,]
I need to sort and filter my $output, but I can't. Nothing happens. Probably doing something wrong.
PS> $output
Name Department
---- ----------
{Numbers, 1,2,3,4,5,6,7...} {Sales,IT,Accounting,Developers...}
And my condition:
PS> $output | Sort-Object Department -Descending | Where-Object {$_.Department -eq "Sales"}
Name Department
---- ----------
{Numbers, 1,2,3,4,5,6,7...} {Sales,IT,Accounting,Developers...}
You created a single object with 2 properties, each of which contains all values of its associated column. Since Sort-Object and Where-Object sort and filter lists of objects by their properties there's nothing for these cmdlets to do here.
What you actually want to do is create one object per row.
$output = foreach ($row in $ws.UsedRange.Rows) {
[PSCustomObject]#{
Name = $row.Columns.Item(1).Value2
Department = $row.Columns.Item(3).Value2
}
}
Untested, since I don't have MS Office at hand here.

Sort-Object produces different objects when using -Descending

Why does Sort-Object produce different objects when -Descending is used? The NoteProperty members are not the same.
Also, when writing to the console, the Name property does not appear unless -Descending is used. Why is that?
C:>Get-Type | Select-Object -Property BaseType,Name | gm
TypeName: Selected.System.RuntimeType
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty RuntimeType BaseType=System.Object
Name NoteProperty string Name=Registry
C:>Get-Type | Select-Object -Property BaseType,Name | Sort-Object -Property BaseType,Name | gm
TypeName: Selected.System.RuntimeType
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty object BaseType=null
Name NoteProperty string Name=_Activator
C:>Get-Type | Select-Object -Property BaseType,Name | Sort-Object -Property BaseType,Name -Descending | gm
TypeName: Selected.System.RuntimeType
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty RuntimeType BaseType=System.Xml.Xsl.XsltException
Name NoteProperty string Name=XsltCompileException
My apologies for not including information about Get-Type. https://gallery.technet.microsoft.com/scriptcenter/Get-Type-Get-exported-fee19cf7
This is not a cmdlet that is natively part of PoSH. It's either something the OP wrote or got from another source. If from another source than the OP should reach out to that author to ask what is to be expected.
If you do this same things using a built-in cmdlet, say Get-Date, we see all members are the same.
Get-Date |
Select-Object -Property BaseType,Name |
Sort-Object -Property BaseType, Name |
Get-Member
TypeName: Selected.System.DateTime
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty object BaseType=null
Name NoteProperty object Name=null
Get-Date | Select-Object -Property BaseType,Name |
Sort-Object -Property BaseType, Name -Descending |
Get-Member
TypeName: Selected.System.DateTime
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BaseType NoteProperty object BaseType=null
Name NoteProperty object Name=null
So, would seem to point specifically to the implementation of this cmdlet the OP is looking for clarity on.
It is possible that your GetType invocation results in an array starting with $null as it has a try-catch block providing an error value of $null. Then, should any parameters be passed to the Get-Type, that null would be filtered out, but you pass none, so null is still present in the output. Then, as the result is piped to Select-Object, only the first object in the pipe is parsed to check if all the columns are present to display, and should the null be the first object, it has no properties thus nothing gets displayed.
To fix, add the | Where-Object {$_ -ne $null} into the Get-Type.ps1 script right before | Where-Object -FilterScript $WhereBlock in the last significant line. This will filter out any nulls produced by previous try-catch block, and you will only get an array of objects that have values.

why can't I add a member to an ADobject

If I have an object of type Microsoft.ActiveDirectory.Management.ADObject, I can't use Add-Member to add a note property, unless I use -force. If I don't use the force directive, I get an error like:
Add-Member : Cannot add a member with the name "SAMAccountName"
because a member with that name already exists. To overwrite the
member anyway, add the Force parameter to your command.
But, it doesn't already exist. And that happens for any property name. For example:
$domainAccount | Add-Member -NotePropertyName SAMAccountName -NotePropertyValue $account.name
But, this works:
$domainAccount | Add-Member -NotePropertyName SAMAccountName -NotePropertyValue $account.name -force
I can't find documentation that explains this. Can you explain it? And, is there any danger in doing this with the -force directive?
The ADObject class behaves a bit clumsy, in that by simply asking if a property exists, you cause the property to be created if it doesn't.
When Add-Member checks for whether the SAMAccountName property already exists, it incidentally cause it to be created.
Just use the -Force parameter switch.
You can reproduce this behavior yourself:
Import-Module ActiveDirectory
$ADObject = New-Object Microsoft.ActiveDirectory.Management.ADObject
# No SamAccountName property will be listed
$ADObject | Get-Member
Now, try to reference a non-existing property, like "SamAccountName" (the ADObject extends the ADPropertyValueCollection class which is basically a dictionary, so indexing into it's property is totally valid):
$ADObject["SamAccountName"]
# SamAccountName property will now be listed even though we haven't set it
$ADObject | Get-Member
This is not restricted to AD property names, anything goes:
"1 This","2 Is","3 Quite","4 Funky","5 Isn't","6 It?" |ForEach-Object {
[void]$ADObject[$_]
}
$ADObject |Get-Member
Microsoft.ActiveDirectory.Management.ADObject already have SamAccountName property. You don't need to recreate it. Just need define value for it, like this:
$obj = New-Object Microsoft.ActiveDirectory.Management.ADObject
$obj.SamAccountName = 'AccountName'
$obj | Get-Member
Output:
TypeName: Microsoft.ActiveDirectory.Management.ADObject
Name MemberType Definition
---- ---------- ----------
Contains Method bool Contains(string propertyName)
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IDictionaryEnumerator GetEnumerator()
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Item ParameterizedProperty Microsoft.ActiveDirectory.Management.ADPropertyValueCollection Item(string propertyN...
SamAccountName Property Microsoft.ActiveDirectory.Management.ADPropertyValueCollection SamAccountName {get;s...

powershell outputs argument with (#{Name=name}:String)

I'm trying to run the command Get-VMNetworkAdapter on a list of VMs
I'm getting the list with the command:
Get-VM –ComputerName (Get-ClusterNode –Cluster clustername)|select name
and it looks fine, when I'm using
$vmm=Get-VM –ComputerName (Get-ClusterNode –Cluster clustername)|select name
foreach ($item in $vmm)
{Get-VMNetworkAdapter -VMName $item}
it gives me the exception
nvalidArgument: (#{Name=vmname}:String)
like it adds all those symbols..
What is the proper way to lose them?
You need to expand the property. Select doesn't remove the object otherwise:
$vmm = Get-VM –ComputerName (Get-ClusterNode –Cluster clustername) `
| Select-Object -ExpandProperty name
To explain what -ExpandProperty does:
First of all, the drawback of -ExpandProperty is that you can only do it to one property at a time.
Select-Object normally wraps the results in another object so that properties remain properties. If you say $x = Get-ChildItem C:\Windows | Select-Object Name, then you get an object array with one property: Name.
PS C:\> $x = Get-ChildItem C:\Windows | Select-Object Name
PS C:\> $x
Name
----
45235788142C44BE8A4DDDE9A84492E5.TMP
8A809006C25A4A3A9DAB94659BCDB107.TMP
.
.
.
PS C:\> $x[0].Name
45235788142C44BE8A4DDDE9A84492E5.TMP
PS C:\> $x[0].GetType().FullName
System.Management.Automation.PSCustomObject
Notice the header? Name is a property of the object.
Also, the base object with it's type is still kind of there:
PS C:\> $x | Get-Member
TypeName: Selected.System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=45235788142C44BE8A4DDDE9A84492E5.TMP
TypeName: Selected.System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=bfsvc.exe
Normally, that's all great. Especially because we normally want multiple properties of the object.
Sometimes, however, not what we want. Sometimes, we want an array that's the same type as the property we selected. When we use it later we want just that property and nothing else and we want it to be the exact same type as the property and nothing else.
PS C:\> $y = Get-ChildItem C:\Windows | Select-Object -ExpandProperty Name
PS C:\> $y
45235788142C44BE8A4DDDE9A84492E5.TMP
8A809006C25A4A3A9DAB94659BCDB107.TMP
.
.
.
PS C:\> $y[0].Name
PS C:\> $y[0]
45235788142C44BE8A4DDDE9A84492E5.TMP
PS C:\> $y.GetType().FullName
System.Object[]
PS C:\> $y[0].GetType().FullName
System.String
Notice there's no header, and any calls to a Name property fail; there is no Name property anymore.
And, there's nothing left over from the original object:
PS C:\> $y | Get-Member
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
.
.
.
.
Basically, here it's the equivalent of doing this:
$z = Get-ChildItem C:\Windows | ForEach-Object { $_.Name }
Which I think is how you had to do it in PowerShell v1.0 or v2.0... it's been too many years since I've used that to remember right.