-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGet-RemoteInstalledPrograms.ps1
282 lines (258 loc) · 23.6 KB
/
Get-RemoteInstalledPrograms.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
Function Get-RemoteInstalledPrograms
{
<#
.SYNOPSIS
Retrieves installed programs from remote systems via the registry.
.DESCRIPTION
Retrieves installed programs from remote systems via the registry.
.PARAMETER ComputerName
Specifies the target computer for data query.
.PARAMETER ThrottleLimit
Specifies the maximum number of systems to inventory simultaneously
.PARAMETER Timeout
Specifies the maximum time in second command can run in background before terminating this thread.
.PARAMETER ShowProgress
Show progress bar information
.EXAMPLE
PS > Get-RemoteInstalledPrograms
Description
-----------
Lists all of the programs found in the registry of the localhost.
.NOTES
Author: Zachary Loeber
Site: http://www.the-little-things.net/
Requires: Powershell 2.0
Version History
1.1.0 - 09/26/2013
- Added another base key into results for 64bit applications
1.0.1 - 08/30/2013
- Changed output to only include a single instance of a computer
as one property of an object with an array of objects that contains
all program install data within another property.
1.0.0 - 08/28/2013
- Initial release
#>
[CmdletBinding()]
Param
(
[Parameter(HelpMessage="Computer or computers to gather information from",
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[ValidateNotNullOrEmpty()]
[Alias('DNSHostName','PSComputerName')]
[string[]]
$ComputerName=$env:computername,
[Parameter(HelpMessage="Maximum number of concurrent runspaces.")]
[ValidateRange(1,65535)]
[int32]
$ThrottleLimit = 32,
[Parameter(HelpMessage="Timeout before a runspaces stops trying to gather the information.")]
[ValidateRange(1,65535)]
[int32]
$Timeout = 120,
[Parameter(HelpMessage="Display progress of function.")]
[switch]
$ShowProgress,
[Parameter(HelpMessage="Set this if you want the function to prompt for alternate credentials.")]
[switch]
$PromptForCredential,
[Parameter(HelpMessage="Set this if you want to provide your own alternate credentials.")]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
Begin
{
# Gather possible local host names and IPs to prevent credential utilization in some cases
Write-Verbose -Message 'Remote Installed Programs: Creating local hostname list'
$IPAddresses = [net.dns]::GetHostAddresses($env:COMPUTERNAME) | Select-Object -ExpandProperty IpAddressToString
$HostNames = $IPAddresses | ForEach-Object {
try {
[net.dns]::GetHostByAddress($_)
} catch {
# We do not care about errors here...
}
} | Select-Object -ExpandProperty HostName -Unique
$LocalHost = @('', '.', 'localhost', $env:COMPUTERNAME, '::1', '127.0.0.1') + $IPAddresses + $HostNames
Write-Verbose -Message 'Remote Installed Programs: Creating initial variables'
$runspacetimers = [HashTable]::Synchronized(@{})
$runspaces = New-Object -TypeName System.Collections.ArrayList
$bgRunspaceCounter = 0
if ($PromptForCredential)
{
$Credential = Get-Credential
}
Write-Verbose -Message 'Remote Installed Programs: Creating Initial Session State'
$iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
foreach ($ExternalVariable in ('runspacetimers', 'Credential', 'LocalHost'))
{
Write-Verbose -Message "Remote Installed Programs: Adding variable $ExternalVariable to initial session state"
$iss.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $ExternalVariable, (Get-Variable -Name $ExternalVariable -ValueOnly), ''))
}
Write-Verbose -Message 'Remote Installed Programs: Creating runspace pool'
$rp = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, $ThrottleLimit, $iss, $Host)
$rp.ApartmentState = 'STA'
$rp.Open()
# This is the actual code called for each computer
Write-Verbose -Message 'Remote Installed Programs: Defining background runspaces scriptblock'
$ScriptBlock = {
[CmdletBinding()]
Param
(
[Parameter(Position=0)]
[string]
$ComputerName,
[Parameter()]
[int]
$bgRunspaceID
)
$runspacetimers.$bgRunspaceID = Get-Date
try
{
Write-Verbose -Message ('Remote Installed Programs: Runspace {0}: Start' -f $ComputerName)
$WMIHast = @{
ComputerName = $ComputerName
ErrorAction = 'Stop'
}
if (($LocalHost -notcontains $ComputerName) -and ($Credential -ne $null))
{
$WMIHast.Credential = $Credential
}
#region Installed Programs
Write-Verbose -Message ('Remote Installed Programs: Runspace {0}: Gathering registry information' -f $ComputerName)
$hklm = '2147483650'
$basekey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
$basekey64 = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
$regkeys = @($basekey,$basekey64)
$Programs = @()
$wmi_data = Get-WmiObject @WMIHast -Class StdRegProv -Namespace 'root\default' -List:$true
Foreach ($basekey in $regkeys)
{
$allsubkeys = $wmi_data.EnumKey($hklm,$basekey)
foreach ($subkey in $allsubkeys.sNames)
{
# $keydata = $wmi_data.EnumValues($hklm,"$basekey\$subkey")
$displayname = $wmi_data.GetStringValue($hklm,"$basekey\$subkey",'DisplayName').sValue
if ($DisplayName)
{
$publisher = $wmi_data.GetStringValue($hklm,"$basekey\$subkey",'Publisher').sValue
$uninstallstring = $wmi_data.GetExpandedStringValue($hklm,"$basekey\$subkey",'UninstallString').sValue
$ProgramProperty = @{
'DisplayName' = $displayname
'Publisher' = $publisher
'UninstallString' = $uninstallstring
}
$Programs += New-Object PSObject -Property $ProgramProperty
}
}
}
If ($Programs.Count -gt 0)
{
$ResultProperty = @{
'PSComputerName' = $ComputerName
'ComputerName' = $ComputerName
'Programs' = $Programs
}
$ResultObject = New-Object PSObject -Property $ResultProperty
Write-Output -InputObject $ResultObject
}
}
catch
{
Write-Warning -Message ('Remote Installed Programs: {0}: {1}' -f $ComputerName, $_.Exception.Message)
}
Write-Verbose -Message ('Remote Installed Programs: Runspace {0}: End' -f $ComputerName)
}
function Get-Result
{
[CmdletBinding()]
Param
(
[switch]$Wait
)
do
{
$More = $false
foreach ($runspace in $runspaces)
{
$StartTime = $runspacetimers[$runspace.ID]
if ($runspace.Handle.isCompleted)
{
Write-Verbose -Message ('Remote Installed Programs: Thread done for {0}' -f $runspace.IObject)
$runspace.PowerShell.EndInvoke($runspace.Handle)
$runspace.PowerShell.Dispose()
$runspace.PowerShell = $null
$runspace.Handle = $null
}
elseif ($runspace.Handle -ne $null)
{
$More = $true
}
if ($Timeout -and $StartTime)
{
if ((New-TimeSpan -Start $StartTime).TotalSeconds -ge $Timeout -and $runspace.PowerShell)
{
Write-Warning -Message ('Remote Installed Programs: Timeout {0}' -f $runspace.IObject)
$runspace.PowerShell.Dispose()
$runspace.PowerShell = $null
$runspace.Handle = $null
}
}
}
if ($More -and $PSBoundParameters['Wait'])
{
Start-Sleep -Milliseconds 100
}
foreach ($threat in $runspaces.Clone())
{
if ( -not $threat.handle)
{
Write-Verbose -Message ('Remote Installed Programs: Removing {0} from runspaces' -f $threat.IObject)
$runspaces.Remove($threat)
}
}
if ($ShowProgress)
{
$ProgressSplatting = @{
Activity = 'Getting installed programs'
Status = 'Remote Installed Programs: {0} of {1} total threads done' -f ($bgRunspaceCounter - $runspaces.Count), $bgRunspaceCounter
PercentComplete = ($bgRunspaceCounter - $runspaces.Count) / $bgRunspaceCounter * 100
}
Write-Progress @ProgressSplatting
}
}
while ($More -and $PSBoundParameters['Wait'])
}
}
Process
{
foreach ($Computer in $ComputerName)
{
$bgRunspaceCounter++
$psCMD = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock)
$null = $psCMD.AddParameter('bgRunspaceID',$bgRunspaceCounter)
$null = $psCMD.AddParameter('ComputerName',$Computer)
$null = $psCMD.AddParameter('Verbose',$VerbosePreference)
$psCMD.RunspacePool = $rp
Write-Verbose -Message ('Remote Installed Programs: Starting {0}' -f $Computer)
[void]$runspaces.Add(@{
Handle = $psCMD.BeginInvoke()
PowerShell = $psCMD
IObject = $Computer
ID = $bgRunspaceCounter
})
Get-Result
}
}
End
{
Get-Result -Wait
if ($ShowProgress)
{
Write-Progress -Activity 'Remote Installed Programs: Getting program listing' -Status 'Done' -Completed
}
Write-Verbose -Message "Remote Installed Programs: Closing runspace pool"
$rp.Close()
$rp.Dispose()
}
}