Looping through each NoteProperty in a custom object - powershell

I'm creating a PSObject in Powershell like this:
$NetworkInfo = New-Object PSObject
$NetworkInfo | Add-Member -type NoteProperty -Name IPAddress -Value "10.10.8.11"
$NetworkInfo | Add-Member -type NoteProperty -Name SubnetMask -Value "255.255.255.0"
$NetworkInfo | Add-Member -type NoteProperty -Name Gateway -Value "10.10.8.254"
$NetworkInfo | Add-Member -type NoteProperty -Name Network -Value "10.10.8.0"
$NetworkInfo | Add-Member -type NoteProperty -Name DNS1 -Value "10.10.10.200"
$NetworkInfo | Add-Member -type NoteProperty -Name DNS2 -Value "10.10.10.201"
$NetworkInfo
I want to do a specific action for each NoteProperty in this PSObject, but I'm not sure how to go about doing that. For example (even though I know it's the wrong way to do it) this code:
ForEach ($noteProperty in $NetworkInfo) {
Write-Host $noteProperty
}
Only outputs the entire object as an array:
#{IPAddress=10.10.8.11; SubnetMask=255.255.255.0; Gateway=10.10.8.254; Network=10.10.8.0; DNS1=10.10.10.200; DNS2=10.10.10.201}
EDIT: Each NoteProperty is of the variable type System.String - if that's pertinent.
Am I creating the object incorrectly, or do I just not understand how to go through the object?

You can use Get-Member as mentioned by Mike; an alternative that keeps the order is to use the PSObject property that is present on all objects:
#View details for all properties on a specific object
$NetworkInfo.PSObject.Properties
#similar code to Mike's, working with name and value:
$NetworkInfo.PSObject.Properties | foreach-object {
$name = $_.Name
$value = $_.value
"$name = $value"
}

This should get you what you need:
$NetworkInfo | get-member -type NoteProperty | foreach-object {
$name=$_.Name ;
$value=$NetworkInfo."$($_.Name)"
write-host "$name = $value"
}

Try:
ForEach ($noteProperty in $NetworkInfo.PSObject.Properties)
{
Write-Host $noteProperty.Name
Write-Host $noteProperty.Value
}

Short (even Faster) version:
$NetworkInfo.psobject.properties.foreach({"$($_.Name) = $($_.Value)"})
Output:
IPAddress = 10.10.8.11
SubnetMask = 255.255.255.0
Gateway = 10.10.8.254
Network = 10.10.8.0
DNS1 = 10.10.10.200
DNS2 = 10.10.10.201
Or another one:
$NetworkInfo.foreach({$_})
Will give you:
IPAddress : 10.10.8.11
SubnetMask : 255.255.255.0
Gateway : 10.10.8.254
Network : 10.10.8.0
DNS1 : 10.10.10.200
DNS2 : 10.10.10.201
You can also select 1 property like:
$NetworkInfo.DNS1
Which will give you:
10.10.10.200
Or select all DNS entries:
$NetworkInfo.foreach({$_}) | Select-Object DNS*
Result:
DNS1 DNS2
---- ----
10.10.10.200 10.10.10.201
Or if you wish:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).Value
Will give:
10.10.10.200
10.10.10.201
Combining foreach and where:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
"$($_.Name) = $($_.Value)"})
Wil give:
DNS1 = 10.10.10.200
DNS2 = 10.10.10.201
Transform it into a 2 dimensional array:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value))})
Do NOT forget the "," in "{,"
Output:
DNS1
10.10.10.200
DNS2
10.10.10.201
Which is two dimensional
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value)) })[0]
DNS1
10.10.10.200
And the following:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value)) })[0][0]
DNS1
And...
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value)) })[0][1]
10.10.10.200

Related

How to Capture Cluster info Remotely from a non-Clustered Node

I've been banging my head on this for a few days now, and I just can't figure out the best way to do it. I've got a script where I collect a bunch of data and output it to an html file (using PSWriteHTML Module, which is where the New-HTMLTable at the end comes from).
I've piecemealed the script together over time so I can gather the data from multiple servers at once, and for the most part, it all works great. As I've added data to the script to collect new info, there's a few parts that I just can't get to work right remotely. I know the piecemeal approach has left me with some redundant code, but I'm just trying to make it all work right before I re-write it again to clean it up, so my apologies for its current state.
The following code works great when I run the script from a server in a Windows Cluster, but I want things to work from any server, not necessarily a Cluster Node.
Here's orig code for this section:
try
{
$ClusterIPInfo = Invoke-command -computer $Computer {
Get-Cluster | Get-ClusterResource | %{
$_ | select Name,
#{ Name = "Address"; Expression = { $_ | Get-ClusterParameter -Name Address -ErrorAction SilentlyContinue | select -ExpandProperty Value } },
#{ Name = "SubnetMask"; Expression = { $_ | Get-ClusterParameter -Name SubnetMask -ErrorAction SilentlyContinue | select -ExpandProperty Value } }
}
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ClusterResourceInfo = Invoke-command -computer $Computer {
Get-ClusterResource | Select Cluster, Name, State, ResourceType, OwnerGroup, OwnerNode, ID, IsCoreResource, IsNetworkClassResource, IsStorageClassResource | Sort-Object -Property OwnerGroup, Name
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ResourceInfo = #()
foreach ($rec in $ClusterResourceInfo)
{
$Owner = (Get-ClusterResource | Sort-Object -Property OwnerGroup, Name | Get-ClusterOwnerNode | %{
$_ | select #{ Name = "Name"; Expression = { $_.ClusterObject } },
#{ Name = "PossibleOwners"; Expression = { $_.OwnerNodes } }
} | Where { $_.Name -eq $rec.Name }).PossibleOwners
$Dependency = (Get-ClusterResource | Sort-Object -Property OwnerGroup, Name | Get-ClusterResourceDependency | %{
$_ | select #{ Name = "Name"; Expression = { $_.Resource } },
#{ Name = "Dependency"; Expression = { $_ | Select-Object -ExpandProperty "DependencyExpression" } }
} | Where { $_.Name -eq $rec.Name }).Dependency
$address = ($ClusterIPInfo | Where { $_.Name -eq $rec.Name }).Address
$subnetmask = ($ClusterIPInfo | Where { $_.Name -eq $rec.Name }).SubnetMask
$recObj = New-Object PSObject
$recObj | Add-Member NoteProperty -Name "Cluster" -Value $rec.Cluster
$recObj | Add-Member NoteProperty -Name "Name" -Value $rec.Name
$recObj | Add-Member NoteProperty -Name "State" -Value $rec.State
$recObj | Add-Member NoteProperty -Name "Resource Type" -Value $rec.ResourceType
$recObj | Add-Member NoteProperty -Name "Owner Group" -Value $rec.OwnerGroup
$recObj | Add-Member NoteProperty -Name "Owner Node" -Value $rec.OwnerNode
$recObj | Add-Member NoteProperty -Name "Possible Owners" -Value $Owner
$recObj | Add-Member NoteProperty -Name "Dependency" -Value $Dependency
$recObj | Add-Member NoteProperty -Name "IP Address" -Value $address
$recObj | Add-Member NoteProperty -Name "Subnet Mask" -Value $subnetmask
$recObj | Add-Member NoteProperty -Name "Is Core Resource" -Value $rec.IsCoreResource
$recObj | Add-Member NoteProperty -Name "Is Network Resource" -Value $rec.IsNetworkClassResource
$recObj | Add-Member NoteProperty -Name "Is Storage Resource" -Value $rec.IsStorageClassResource
$ResourceInfo += $recObj
}
New-HTMLTable -DataTable $ResourceInfo -HideFooter -HideButtons -DisableSearch
The parts that don't work correctly remotely are the Dependency and PossibleOwners. I know the reason it doesn't work is because when the server running the script isn't a Cluster Node, it doesn't recognize the command under the Foreach loop Get-ClusterResource. But I just can't figure out how to make those pass correctly from within the Foreach loop, but still use the info from $ClusterResourceInfo.
I've re-written this a hundred different ways, i.e. make a single Invoke-command with basically one big Get-Cluster variable (couldn't get it to capture the Dependency/PossOwners, always $null), splitting up the Dependency and PossOwners to their own separate Invoke-Command (best I can get it to do is display System.Object[], or when I did get it to display, it captured ALL of the Dependencies for all objects and displayed on every line instead of splitting it up correctly).
I've tried every possible way I can think of or found online, but just can't get it to work correctly remotely.
Here's how the orig code above outputs (which is what I want, but I just want to fix it so it works remotely):
I am desperately hoping for some brilliance or guidance to set me on the right track. I tried so many ways, but just never quite got it where it needs to be, so any help is most appreciated and welcome. Thanks.
Couple of things i can suggest.
The "Get-ClusterResource" cmdlet fails because it is not installed on the server.
You may try to load the Failover cluster module using Import-Module, and if it fails (on a non-cluster Node), you can add the Failover Cluster Module for Windows PowerShell Feature, using the following PowerShell cmd:
Add-WindowsFeature RSAT-Clustering-PowerShell
You may try connecting to to the remote cluster node where the resource is hosted, using WMI ?
You have enough info about the resource to be able to write a filtered WMI query.
So the piecemeal approach is what got me in trouble. In trying to merge things together, I kept breaking it (mainly because I think had doubled up the %{}). So instead of merging, I just replicated the parts that were already working as intended.
Ultimately this code worked fine:
$ClusterInfo = Invoke-command -computer $Computer {
Get-Cluster | Get-ClusterResource | %{
$_ | select Name,
#{ Name = "Address"; Expression = { $_ | Get-ClusterParameter -Name Address -ErrorAction SilentlyContinue | select -ExpandProperty Value } },
#{ Name = "SubnetMask"; Expression = { $_ | Get-ClusterParameter -Name SubnetMask -ErrorAction SilentlyContinue | select -ExpandProperty Value } },
#{ Name = "PossibleOwners"; Expression = { $_ | Get-ClusterOwnerNode | select OwnerNodes | select -ExpandProperty OwnerNodes } },
#{ Name = "Dependency"; Expression = { $_ | Get-ClusterResourceDependency | select -ExpandProperty "DependencyExpression" } }
}
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ClusterResourceInfo = Invoke-command -computer $Computer {
Get-ClusterResource | Select Cluster, Name, State, ResourceType, OwnerGroup, OwnerNode, IsCoreResource, IsNetworkClassResource, IsStorageClassResource | Sort-Object -Property OwnerGroup, Name
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ResourceInfo = #()
foreach ($rec in $ClusterResourceInfo)
{
$Owner = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).PossibleOwners
$Dependency = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).Dependency
$address = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).Address
$subnetmask = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).SubnetMask
$recObj = New-Object PSObject
$recObj | Add-Member NoteProperty -Name "Cluster" -Value $rec.Cluster
$recObj | Add-Member NoteProperty -Name "Name" -Value $rec.Name
$recObj | Add-Member NoteProperty -Name "State" -Value $rec.State
$recObj | Add-Member NoteProperty -Name "Resource Type" -Value $rec.ResourceType
$recObj | Add-Member NoteProperty -Name "Owner Group" -Value $rec.OwnerGroup
$recObj | Add-Member NoteProperty -Name "Owner Node" -Value $rec.OwnerNode
$recObj | Add-Member NoteProperty -Name "Possible Owners" -Value $Owner
$recObj | Add-Member NoteProperty -Name "Dependency" -Value $Dependency
$recObj | Add-Member NoteProperty -Name "IP Address" -Value $address
$recObj | Add-Member NoteProperty -Name "Subnet Mask" -Value $subnetmask
$recObj | Add-Member NoteProperty -Name "Is Core Resource" -Value $rec.IsCoreResource
$recObj | Add-Member NoteProperty -Name "Is Network Resource" -Value $rec.IsNetworkClassResource
$recObj | Add-Member NoteProperty -Name "Is Storage Resource" -Value $rec.IsStorageClassResource
$ResourceInfo += $recObj
}
New-HTMLTable -DataTable $ResourceInfo -HideFooter -HideButtons -DisableSearch

Displaying the content of Subnet in a form of table in PowerShell

I am trying to display the PowerShell data in the following format.
Vnet Subnet
---- ------
AZRWUSVNET1 default
Corpvnet MgmtSubnet
Corpvnet Devsubnet
Corpvnet QASubnet
Corpvnet ProdSubnet
I have tried this below script
$a = Get-AzVirtualNetwork
$b = $a | Select-Object Name,Subnets
$ResourceList = #()
foreach ($item in $b)
{
$vnetname = ($b | Where-Object -Property Name -EQ ($item.Name))
$subnet = $vnetname.Subnets.Name
$vnetname = $item.Name
$VMObject = New-Object PSObject
$VMObject | Add-Member -MemberType NoteProperty -Name "Vnet" -Value $vnetname
$VMObject | Add-Member -MemberType NoteProperty -Name "Subnet" -Value $subnet
$ResourceList += $VMObject
}
$ResourceList
But I am getting the data in the following format
Vnet Subnet
---- ------
AZRWUSVNET1 default
Corpvnet {MgmtSubnet, Devsubnet, QASubnet, ProdSubnet…}
Can anyone please help here.
You can simplify your code significantly by using Select-Object to extract and annotate the Subnets values:
Get-AzVirtualNetwork |Select-Object -Property #{Name='Vnet';E=
Name'} -ExpandProperty Subnets |Select-Object Vnet,#{Name='Subnet';Expression={$_.Name}}
If you insist on using loops, you'll need a nested loop (since the individual subnets are nested inside each vnet):
$vNets = Get-AzVirtualNetwork | Select-Object Name, Subnets
$ResourceList=#()
foreach($vNet in $vNets){
foreach($subnet in $vNet.Subnets){
$VMObject = New-Object PSObject
$VMObject | Add-Member -MemberType NoteProperty -Name "Vnet" -Value $vNet.Name
$VMObject | Add-Member -MemberType NoteProperty -Name "Subnet" -Value $subnet.Name
$ResourceList += $VMObject
}
}
$ResourceList
I just tried this and it resolved
$vNets = Get-AzVirtualNetwork | Select-Object Name, Subnets
$ResourceList=#()
foreach($vNet in $vNets){
foreach($subnet in $vNet.Subnets){
#$subnatename=$vnet.subnets.Name
$VMObject = New-Object PSObject
$VMObject | Add-Member -MemberType NoteProperty -Name "Vnet" -Value $vNet.Name
$VMObject | Add-Member -MemberType NoteProperty -Name "Subnet" -Value $subnet.Name
$ResourceList += $VMObject
}
}
$ResourceList
Only change is $subnet.Name
Thanks

Selecting from objects within objects

I have an array of information in Powershell that is comprised of objects within objects. It goes down 4 objects deep. Is there a way to select a property from the bottom most object while remaining at the top level? Does this make sense what I am asking?
$result
active : active
security : #{waf=; acls=}
sealloaction :
siteDualFactorSettings : #{enabled=False; version=0}
login_protect : #{enabled=False; url_patterns=System.Object[]}
performance_configuration : #{advanced_caching_rules=; acceleration_level=standard; cache300x=False; cache_headers=System.Object[]}
$result.security
waf acls
--- ----
#{rules=System.Object[]} #{rules=System.Object[]}
$result.security.waf
rules
-----
{#{action=api.threats.action.block_request; action_text=Block; id=api.threats.sql_injection; name=SQL Injection}, #{action=api.threats.action.alert; action_text=Alert Only;}
To produce a mixed main/nested level selection:
foreach on the main array
select of the nested stuff + calculated properties to reference the iterated main array element
$results | ForEach {
$r = $_
$r.security.waf.rules | select *, #{N='domain'; E={$r.domain}}
}
Or as a one-liner:
$results | %{ $r = $_; $r.security.waf.rules | select *, #{N='domain'; E={$r.domain}} }
$myArray | where { $_.x.y.z -eq 'something' }
which is functionally equivalent to
foreach ($object in $myArray)
{
if ($object.x.y.z -eq 'something')
{
$object # Writes $object to the output stream
}
}
The easier way should be with $result | select ${L='label';E={$_.security.waf}} then you drill down to where you want.
For example:
$obj1_child = New-Object -TypeName psobject
$obj1_child | add-member -Name obj1_child -MemberType NoteProperty -Value "obj1_child"
$obj1_child | add-member -Name value1 -MemberType NoteProperty -Value "child_value1"
$obj1 = New-Object -TypeName psobject
$obj1 | add-member -Name obj1 -MemberType NoteProperty -Value "obj1"
$obj1 | add-member -Name child -MemberType NoteProperty -Value $obj1_child
$obj2 = New-Object -TypeName psobject
$obj2 | add-member -Name value1 -MemberType NoteProperty -Value "value1"
$parentObj = New-Object -TypeName psobject
$parentObj | Add-Member -MemberType NoteProperty -name string -value "parentObject"
$parentObj | Add-Member -MemberType NoteProperty -name OBJ1 -value $obj1
$parentObj | Add-Member -MemberType NoteProperty -name OBJ2 -value $obj2
$parentObj | select string,obj2, #{L='value of obj1 child';E={$_.obj1.child.value1}}
I create a $parentObj inside there's a string, and two objects ($obj1,$obj2), and inside $obj1 there's another object ($obj1_child). Now I want the string from $parentObj and the last string from $obj1_child this should work:
$parentObj | select string, #{L='value of obj1 child';E={$_.obj1.child.value1}}
Output:
>> $parentObj | select string, #{L='value of obj1 child';E={$_.obj1.child.value1}}
string value of obj1 child
------ -------------------
parentObject child_value1
Not sure if it's what you want. but it was what I was looking for when landed here, so may help someone else.
Reference:
https://devblogs.microsoft.com/scripting/access-objects-inside-other-objects-in-powershell-pipeline/
Two ways to do the same thing
$x = [PSCustomObject]#{
active = 'active';
security = [PSCustomObject]#{
waf = [PSCustomObject]#{
rules = #(
[PSCustomObject]#{
action='api.threats.action.block_request';
action_text='Block';
id='api.threats.sql_injection';
name='SQL Injection'},
[PSCustomObject]#{
action='api.threats.action.alert';
action_text='Alert Only';}
)
}
acls = [PSCustomObject]#{
rules = #(
[PSCustomObject]#{
action='api.threats.action.block_request';
action_text='Block';
id='api.threats.sql_injection';
name='SQL Injection'},
[PSCustomObject]#{
action='api.threats.action.alert';
action_text='Alert Only';}
)
}
}
}
$x.security.waf.rules | select *, #{name='domain'; ex={'foo'}}
$x | select -ExpandProperty security | select -ExpandProperty waf | select -ExpandProperty rules | select *, #{name='domain'; ex={'foo'}}

Combine columns from 2 CSV files

This seems very basic yet I can't find or figure out anywhere
I have 2 CSV files I would like to create a new one that will have matched columns.
Huge.csv
"Share","Group","Username","Name","LogonScript"
"\\SHARE\TEST","Group Test","administrator","Administrator name","(no-script)"
"\\SHARE\TEST","Group Test","user1","user name1","logon.bat"
"\\SHARE\TEST","Group Test","user2","user name2","logon.bat"
Little.csv
"Username","Computer","NetworkDrives"
administrator,PC100,M:\\share\it#N:\\share\test
user2,PC102,M:\\share\it#N:\\share\test
Desired output:
output.csv
"Share","Group","Username","Name","LogonScript","Computer","NetworkDrives"
"\\SHARE\TEST","Group Test","administrator","Administrator name","(no-script)",PC100,M:\\share\it#N:\\share\test
"\\SHARE\TEST","Group Test","user1","user name1","logon.bat",,
"\\SHARE\TEST","Group Test","user2","user name2","logon.bat",PC102,M:\\share\it#N:\\share\test
Here's the code I'm working with:
$HugeFile = Import-Csv -Path .\Huge.csv
$LittleFile = Import-Csv -Path .\Little.csv
ForEach ($entryh in $HugeFile) {
$o | add-member NoteProperty -Name "Share" -Value ($entryh.Share)
$o | add-member NoteProperty -Name "Group" -Value ($entryh.Group)
$o | add-member NoteProperty -Name "Username" -Value ($entryh.Username)
$o | add-member NoteProperty -Name "Name" -Value ($entryh.Name)
$o | add-member NoteProperty -Name "LogonScript" -Value
($entryhu.LogonScript)
ForEach ($entryl in $LittleFile) {
If ($($entryh.Username) -eq $($entryl.Username)) {
$o | add-member NoteProperty -Name "Computer" -Value ""
($entryl.Computer)
$o | add-member NoteProperty -Name "NetworkDrives" -Value ""
($entryl.NetworkDrives)
} Else {
$o | add-member NoteProperty -Name "Computer" -Value "," -Force
$o | add-member NoteProperty -Name "NetworkDrives" -Value "," -Force
}
$o | Export-Csv -NoTypeInformation .\output.csv
}
}
}
My code is not working. :/
My second problem is that I am thinking if there a better option because for each "Username" in Huge.csv I have to compare with "Username" in Little.csv.
Maybe creating a hashtable could be more optimal.
Concat Computer and Network a create a value?
Like:
key Computer+NetworkDrive
----------- ---------------------
administrator PC100,M:\\share\it#N:\\share\test
user2 PC102,M:\\share\it#N:\\share\test
Thanks a lot!
Edit
Thanks #Ansgar-Wiechers
Yes, a hashtable is probably the best way to go about this. I'd do it like this:
$additionalData = #{}
Import-Csv .\Little.csv | % {
$additionalData[$_.Username] = $_.Computer, $_.NetworkDrives
}
Import-Csv .\Huge.csv `
| select Share, Group, Username, Name, LogonScript, #{n='Computer';e={}},
#{n='NetworkDrives';e={}} `
| % {
if ( $additionalData.ContainsKey($_.Username) ) {
$_.Computer = $additionalData[$_.Username][0]
$_.NetworkDrives = $additionalData[$_.Username][1]
}
$_
} | Export-Csv -NoTypeInformation .\output.csv
Or, following #mjolinor's suggestion, using separate hashtables for computers and network drives:
$computers = #{}
$netDrives = #{}
Import-Csv .\Little.csv | % {
$computers[$_.Username] = $_.Computer
$netDrives[$_.Username] = $_.NetworkDrives
}
Import-Csv .\Huge.csv `
| select Share, Group, Username, Name, LogonScript, #{n='Computer';e={}},
#{n='NetworkDrives';e={}} `
| % {
if ( $computers.ContainsKey($_.Username) ) {
$_.Computer = $computers[$_.Username]
}
if ( $netDrives.ContainsKey($_.Username) ) {
$_.NetworkDrives = $netDrives[$_.Username]
}
$_
} | Export-Csv -NoTypeInformation .\output.csv

PowerShell to fetch installed programs

I will be hosting a file on a remote server (read-only) and asking people to run the file on their machines to gather installed program information. I want the file to be saved to their Desktop in their user space, so that I can then have them send it to us.
I have the script, but I'm not managing to obtain information from both "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", and "Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" in the same output file. I'm obviously missing something inherently obvious, as PowerShell is clearly able to do this, and I'm asking that someone please save me from my PEBKAC issue!
Thank you in advance, appreciated!
Here is my code;
$computers = "$env:computername"
$array = #()
foreach($pc in $computers){
$computername=$pc
$UninstallKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
$UninstallKey="Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$computername)
$regkey=$reg.OpenSubKey($UninstallKey)
$subkeys=$regkey.GetSubKeyNames()
Write-Host "$computername"
foreach($key in $subkeys){
$thisKey=$UninstallKey+"\\"+$key
$thisSubKey=$reg.OpenSubKey($thisKey)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $computername
$obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($thisSubKey.GetValue("DisplayName"))
$obj | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($thisSubKey.GetValue("DisplayVersion"))
$obj | Add-Member -MemberType NoteProperty -Name "InstallLocation" -Value $($thisSubKey.GetValue("InstallLocation"))
$obj | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($thisSubKey.GetValue("Publisher"))
$array += $obj
}
}
$array | Where-Object { $_.DisplayName } | select ComputerName, DisplayName, DisplayVersion, Publisher | export-csv C:\Users\$env:username\Desktop\Installed_Apps.csv
Right now the following two lines set the same variable:
$UninstallKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
$UninstallKey="Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
Use this:
$UninstallKey = #(
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
)
Then wrap the real logic in:
$UninstallKey | ForEach-Object {
$regkey=$reg.OpenSubKey($_)
# the rest of your logic here
}