ADAppFinder.psm1
#Region '.\Private\Get-ADHostManageability.ps1' 0 function Get-ADHostManageability { [OutputType([System.Collections.Hashtable])] # $this[0] : Name = Computername; Value = [array]RemotableHostNames # $this[1] : Name = Computername; Value = [array]Non-RemotableHostNames param ( [int]$DaysInactive = 90, [switch]$Servers, [switch]$Workstations, [String]$OperatingSystemSearchString, [string]$SearchBase ) if ($SearchBase) { $Time = (Get-Date).Adddays( - ($DaysInactive)) $AllComputers = Get-ADComputer -SearchBase $SearchBase -Filter { (LastLogonTimeStamp -gt $time) } -Properties Ipv4address $Comps = $allComputers.name $Params = @{} $Params.ComputerName = @() $NoRemoteAccess = @{} $NoRemoteAccess.NoRemoteAccess = @() foreach ($comp in $comps) { $testRemoting = Test-WSMan -ComputerName $comp -ErrorAction SilentlyContinue if ($null -ne $testRemoting ) { $params.ComputerName += $comp } else { $NoRemoteAccess.NoRemoteAccess += $comp } } } else { if ($Servers) { $Search = "*server*" } elseif ($Workstations) { $Search = "*windows 1*" } elseif ($OperatingSystemSearchString) { $Search = "*" + $OperatingSystemSearchString + "*" } $Time = (Get-Date).Adddays( - ($DaysInactive)) $AllComputers = Get-ADComputer -Filter { (LastLogonTimeStamp -gt $time) -and (OperatingSystem -like $search) } -Properties Ipv4address $Comps = $allComputers.name $Params = @{} $Params.ComputerName = @() $NoRemoteAccess = @{} $NoRemoteAccess.NoRemoteAccess = @() foreach ($comp in $comps) { $testRemoting = Test-WSMan -ComputerName $comp -ErrorAction SilentlyContinue if ($null -ne $testRemoting ) { $params.ComputerName += $comp } else { $NoRemoteAccess.NoRemoteAccess += $comp } } } return $Params, $NoRemoteAccess } #EndRegion '.\Private\Get-ADHostManageability.ps1' 59 #Region '.\Private\SearchForRegUninstallKey.ps1' 0 function SearchForRegUninstallKey { [OutputType([System.Management.Automation.PSCustomObject])] param( [Parameter( Mandatory = $true, Position = 0 )] [string[]]$SearchFor, [Parameter( Position = 1 )] [switch]$Wow6432Node ) $output = @() foreach ($item in $SearchFor) { $matched = @() $results = @() Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | ` ForEach-Object { $obj = New-Object psobject Add-Member -InputObject $obj -MemberType NoteProperty -Name GUID -Value $_.pschildname Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayName -Value $_.GetValue("DisplayName") Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayVersion -Value $_.GetValue("DisplayVersion") if ($Wow6432Node) { Add-Member -InputObject $obj -MemberType NoteProperty -Name Wow6432Node? -Value "No" } $results += $obj #$output += $results }# End ForEach-Object if ($Wow6432Node) { Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall | ` ForEach-Object { $obj = New-Object psobject Add-Member -InputObject $obj -MemberType NoteProperty -Name GUID -Value $_.pschildname Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayName -Value $_.GetValue("DisplayName") Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayVersion -Value $_.GetValue("DisplayVersion") Add-Member -InputObject $obj -MemberType NoteProperty -Name Wow6432Node? -Value "Yes" $results += $obj } } $matched = $results | Sort-Object DisplayName | Where-Object { $_.DisplayName -match $item } if ($matched) { $output += $matched } else { $obj = New-Object psobject Add-Member -InputObject $obj -MemberType NoteProperty -Name GUID -Value "Missing: $item" Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayName -Value "Missing: $item" Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayVersion -Value "Missing: $item" Add-Member -InputObject $obj -MemberType NoteProperty -Name Wow6432Node? -Value "Missing: $item" $output += $obj } } # For Each return $output } # End Function #EndRegion '.\Private\SearchForRegUninstallKey.ps1' 56 #Region '.\Public\Find-ADHostApp.ps1' 0 function Find-ADHostApp { <# .SYNOPSIS Searches AD Computers uninstall registry nodes for input Strings for installed app finding. .DESCRIPTION Searches computers using Invoke-Command passing functions to remote hosts. It will search the registry for strings matching a provided array of app names. .NOTES The function defaults to Searching all remotable host in a domain found to have a login within the last 90 days. It does not take into account the state of the product if a service is involved. This would require a manual check on the specific service. .LINK Specify a URI to a help page, this will show when Get-Help -Online is used. .EXAMPLE Find-ADHostApp -AppNames "Adobe","Microsoft","Carbon" -ComputerNames "pdc-00","pdc-ha-00" -IncludeWow6432Node Output: GUID : Missing: Adobe DisplayName : Missing: Adobe DisplayVersion : Missing: Adobe Wow6432Node? : Missing: Adobe PSComputerName : pdc-00 RunspaceId : 47d370fb-f095-4bcf-a036-40997cb5af12 GUID : {F1BECD79-0887-4630-957B-108C894264AD} DisplayName : Microsoft Azure AD Connect Health agent for AD DS DisplayVersion : 3.1.77.0 Wow6432Node? : No PSComputerName : pdc-00 RunspaceId : 47d370fb-f095-4bcf-a036-40997cb5af12 #> [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter( Mandatory = $true, HelpMessage = 'Array of one or more strings to search for apps: "Spiceworks","Microsoft","Adobe"', Position = 0 )] [string[]]$AppNames, [Parameter( HelpMessage = 'How many days back to consider an AD Computer last sign in as active', ParameterSetName = 'Default', Position = 2 )] [Parameter( HelpMessage = 'How many days back to consider an AD Computer last sign in as active', ParameterSetName = 'SearchWorkstations', Position = 2 )] [Parameter( HelpMessage = 'How many days back to consider an AD Computer last sign in as active', ParameterSetName = 'SearchOSString', Position = 2 )] [Parameter( HelpMessage = 'How many days back to consider an AD Computer last sign in as active', ParameterSetName = 'SearchBase', Position = 2 )] [int]$DaystoConsiderAHostInactive = 90, [Parameter( Mandatory = $true, ParameterSetName = 'Default', HelpMessage = 'Enter one or more filenames', Position = 1 )] [switch]$SearchServers, [Parameter( Mandatory = $true, ParameterSetName = 'SearchWorkstations', HelpMessage = 'Search Windows 10 and 11 workstations', Position = 1 )] [switch]$SearchWorkstations, [Parameter( Mandatory = $true, ParameterSetName = 'SearchOSString', HelpMessage = 'Search using custom OS Search String', Position = 1 )] [string]$SearchOSString, [Parameter( Mandatory = $true, ParameterSetName = 'SearchComputers', HelpMessage = 'Search using specific hosts assumed to be online.', Position = 1 )] [string[]]$ComputerNames, [Parameter( Mandatory = $true, ParameterSetName = 'SearchBase', HelpMessage = 'Search a specific Organizational Unit: "OU=Infrastructure,OU=CorpComputers,DC=ad,DC=fabuloso,DC=com"', Position = 1 )] [string]$SearchBase, [Parameter( Mandatory = $true, ParameterSetName = 'SearchBase', HelpMessage = 'Use a standard Filter: "Name -like "*PDC*"". Defaults to Wildcard', Position = 3 )] [string]$Filter = "*", [Parameter( HelpMessage = 'Enable this switch to output a CSV Report.' )] [switch]$Report, [Parameter( HelpMessage = 'Enter the working directory you wish the report to save to. Default creates C:\temp' )] [string]$DirPath = 'C:\Temp\', [Parameter( HelpMessage = 'Also Search Wow6432Node.' )] [switch]$IncludeWow6432Node ) begin { if ($Report) { # Create temp directory if [bool]$DirPathCheck = Test-Path -Path $DirPath If (!($DirPathCheck)) { Try { #If not present then create the dir New-Item -ItemType Directory $DirPath -Force } Catch { Write-Output "The Directory $DirPath was not created and does not exist. Evalate your permissions or modify the `$DirPath variable to suit." break } } } if ($ComputerNames) { $computers = $ComputerNames } # End if not $ComputerNames elseif (($SearchServers) -or ($SearchWorkstations) -or ($SearchOSString)) { $object = Get-ADHostManageability -DaysInactive $DaystoConsiderAHostInactive -Servers $SearchServers -Workstations $SearchWorkstations -OperatingSystemSearchString $SearchOSString $computers = ($object.GetEnumerator()).computerName $NonReachable = ($object.GetEnumerator()).NoRemoteAccess } elseif ($SearchBase) { $object = Get-ADHostManageability -DaysInactive $DaystoConsiderAHostInactive -SearchBase $SearchBase $computers = ($object.GetEnumerator()).computerName $NonReachable = ($object.GetEnumerator()).NoRemoteAccess } } # End Begin Block process { $results = Invoke-Command -ComputerName $computers -ScriptBlock ${Function:SearchForRegUninstallKey} -ArgumentList $AppNames, $IncludeWow6432Node } # End Process Block end { if ($Report) { if ($ComputerNames) { $results | Export-Csv "$DirPath\$((Get-Date).ToString('yyyy-MM-dd_hh.mm.ss'))_$($env:USERDNSDOMAIN)\ADAppInstallStatus.csv" -NoTypeInformation } # End If $ComputerNames else { $computers | Out-File "$DirPath\$((Get-Date).ToString('yyyy-MM-dd_hh.mm.ss'))_$($env:USERDNSDOMAIN)\ReachableHosts.txt" -NoTypeInformation $NonReachable | Out-File "$DirPath\$((Get-Date).ToString('yyyy-MM_dd_hh.mm.ss'))_$($env:USERDNSDOMAIN)\NonReachableHosts.txt" -NoTypeInformation $results | Export-Csv "$DirPath\$((Get-Date).ToString('yyyy-MM-dd_hh.mm.ss'))_$($env:USERDNSDOMAIN)\ADAppInstallStatus.csv" -NoTypeInformation } } # End If $Report return $results } #End Block } #EndRegion '.\Public\Find-ADHostApp.ps1' 162 |