How to query and add (if not listed) a location to Windows 10 Search Index using PowerShell - windows

I have to re-mount removable drives (which require authentication) each time I boot the computer and Windows Indexing keeps removing the removable drives (perhaps because the removable drives are not available when the computer boots). In an ideal world Windows Indexing would keep these locations and just list them as 'Unavailable' (which it sometimes does). However because it doesn't I am interested in executing a script that queries the Windows Indexing locations and if it does not list the removable drives then add them. At the bottom of this thread I pasted the Batch script that I setup to run at boot (via Start Up folder) to search for a specific folder that is available thereafter mounting one of the removable drives.
I have found several examples of how to do this on Windows 7 (links pasted below) but I can't figure out how to do it in Windows 10. The links provided to the DLL (Microsoft.Search.Interop.dll) no longer resolve.
When searching for the latest Windows Search SDK for Windows 10 I was lead to the Windows SDK here:
https://learn.microsoft.com/en-us/windows/win32/search/-search-developers-guide-entry-page
I installed the C++ related portion of the Windows SDK then searched for Microsoft.Search.Interop.dll but I couldn't find it. Perhaps the DLL has changed?
From How to rebuild Windows Search Index by using PowerShell?
Load DLL containing classes & interfaces
Add-Type -path "C:\Temp\SearchIndexSdk\Microsoft.Search.Interop.dll"
#Provides methods for controlling the Search service. This
interface manages settings and objects that affect the search engine
across catalogs.
https://msdn.microsoft.com/en-us/library/bb231485(v=vs.85).aspx
$sm = New-Object Microsoft.Search.Interop.CSearchManagerClass
#Retrieves a catalog by name and creates a new ISearchCatalogManager
object for that catalog.
$catalog = $sm.GetCatalog("SystemIndex")
#Resets the underlying catalog by rebuilding the databases and performing a full indexing.
#https://msdn.microsoft.com/en-us/library/bb266414(v=vs.85).aspx
$catalog.Reset()
From How to add a location to windows 7/8 search index using batch or vbscript?
#Code copied from "Powershell Tackles Windows Desktop Search" http://powertoe.wordpress.com/2010/05/17/powershell-tackles-windows-desktop-search/
#Microsoft.Search.Interop.dll is needed, download from http://www.microsoft.com/en-us/download/details.aspx?id=7388
#Load the dll
Add-Type -path "D:\Unattend\UserFiles\Tools\Microsoft.Search.Interop.dll"
#Create an instance of CSearchManagerClass
$sm = New-Object Microsoft.Search.Interop.CSearchManagerClass
#Next we connect to the SystemIndex catalog
$catalog = $sm.GetCatalog("SystemIndex")
#Get the interface to the scope rule manager
$crawlman = $catalog.GetCrawlScopeManager()
#add scope
$crawlman.AddUserScopeRule("file:///D:*",$true,$false,$null)
$crawlman.SaveAll()
I would add a comment to the existing threads but I am not able to because I don't have reputation of 50 (dumb rule IMO).
Last... I found this site which lists the DLL along with some code but it hasn't been updated in a long time.
https://github.com/FileMeta/WindowsSearchSample
Thanks in advance!
Batch script that runs at boot:
#echo off
echo Windows Search is being restarted to recognize the Z drive
:while
if EXIST Z:\Watch (
I WANT TO CALL POWERSHELL SCRIPT TO ADD THE LOCATION TO THE INDEX IF NEEDED HERE
sc stop WMPNetworkSvc
ping 127.0.0.1 -n 5 > nul
sc stop WSearch
ping 127.0.0.1 -n 5 > nul
sc start WSearch
ping 127.0.0.1 -n 5 > nul
sc start WMPNetworkSvc
echo Exiting this script in 5 seconds
ping 127.0.0.1 -n 5 > nul
exit
) else (
echo Waiting 60 seconds to check if Z drive is available
ping 127.0.0.1 -n 60 > nul
goto :while
)
When I do a search for Searchdll in what I believe to be the folder where the Windows SDK installed to (C:\Program Files (x86)\Windows Kits\10) I find the following. If I had to guess which DLL is the Windows 10 equivalent of Windows 7's Microsoft.Search.Interop.dll I would guess that it's the 1st one i.e. interop.searchapi.dll.
Add-Type -Path "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\interop.searchapi.dll" does return without error... however $sm = New-Object Microsoft.Search.Interop.CSearchManagerClass returns with error that it cannot find the class in the assembly.
When I cd to "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64" and enter ([appdomain]::currentdomain.GetAssemblies() | Where-Object Location -Match 'interop.searchapi').gettypes() I get the following
When I enter (([appdomain]::currentdomain.GetAssemblies() | Where-Object location -match 'interop.searchapi.dll').gettypes() | Where-Object name -eq 'CSearchManagerClass').getmembers() | Format-Table name, membertype I get
From the list of commands in the previous threads I do see GetCatalog and I presume that the members GetCrawlScopeManager, AddUserScopeRule, Reset, and SaveAll exist.
I don't know how to find the fully qualified class name or I'm doing something else wrong (unknowingly).
When I enter ([appdomain]::currentdomain.GetAssemblies() | Where-Object Location -Match 'interop.searchapi').fullname I get the following
Interop.SearchAPI, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
But when I enter $sm = New-Object Interop.SearchAPI.CSearchManagerClass I get an error that it can't find the type Interop.SearchAPI.CSearchManagerClass.

Related

Powershell - Checking # of files in a folder across a domain

So I'm trying to count the number of font files (that have different extensions) inside the local font folder of every computer in my domain at work to verify which computers have an up to date font installation using powershell.
So far I have
Write-Host ( Get-ChildItem c:\MyFolder | Measure-Object ).Count;
as a means of counting the files, I'm just at a loss on how exactly to replicate this and get a output that indicates the file count for the path for every computer on my domain (the file path is all the same for each)
How should I best proceed?
You will have to run the command against every computer. Assuming you have some sort of domain admin privelege and can access admin shares on all computers, you can use the c$ share.
The code below takes a list of computers in a single column CSV with no headers and runs the command against the admin share on each
$computers = Import-Csv -Path C:\computers.csv -Header Computer;
foreach($c in $computers)
{
Write-Host (Get-ChildItem "\\$($c.Computer)\c$\MyFolder" | Measure-Object).Count;
};

Windows Defender ATP - Disable .LNK files from running on USB devices

Dear Stackoverflow Community!
I've got a question concerning Windows Defender, Attack Surface Reduction and Endpoint Device Manager.
To prevent LNK Worm Expoitation I want to block .LNK files on removable devices (USB drives). Example: User inserts USB Drive and doubleclicks on a file 'USB Drive.lnk' which, in fact, is malicious.
Unfortunately after a lot of research and testing I have yet not found a satisfying solution for my problem. Maybe some of you had a similar problem.
Many thanks in advance!
Best regards
Solved it myself.
I enabled logging in event viewer under the path "Application and Services Logs\Microsoft\Windows\DriverFrameworks-UserMode".
Then created an Task in Task Scheduler that gets activated when Event ID 2003 is found in above path.
This task then executes a scripts that searches for .lnk files on USB sticks that have the drive letter D:.
#
# ------------- Globals -------------
#
# drive letter of usb device
$USBDeviceDriveLocation = "D:\"
# .lnk extensio
$LNKExtentsion = ".lnk"
#
# ------------- Functions -------------
#
function SearchAndRemoveFilesWithExtension
{
<#
#>
# file extension as parameter
param
(
$extension
)
# iterate through files and delete them
$FoundFiles = Get-ChildItem -Recurse -Path D:\ -Include "*$extension"
# if none lnk files found then exit
if ($FoundFiles.Count -eq 0)
{
exit
}
# remove those files
foreach ($file in $FoundFiles)
{
Remove-Item -Path $file.FullName
}
}
function Main
{
<#
.SYNOPSIS
Main Method
#>
SearchAndRemoveFilesWithExtension $LNKExtentsion
}
#
#
#
Main

Powershell script to list all open Explorer windows

This question shows a Powershell script to generate a list of open File Explorer windows and their path.
My goal is to capture the currently open set of explorer windows, and write out a CMD file with commands like: C:\WINDOWS\explorer.exe /e, "C:\open\this\folder"
So I would like to have the full path and folder name in normal path notation. This is what is showing in titlebar of the Explorer Windows: "C:\open\this\Favorite folder"
The proposed code is:
function Get-WindowTitle($handle) {
Get-Process |
Where-Object { $_.MainWindowHandle -eq $handle } |
Select-Object -Expand MainWindowTitle
}
$app = New-Object -COM 'Shell.Application'
$app.Windows() |
Select-Object LocationURL, #{n='Title';e={Get-WindowTitle $_.HWND}}
As shown above, LocationURL provides a full path in an escaped-URL style:
file:///C:/open/this/Favorite%20%folder"
The #{n='Title';e={Get-WindowTitle $_.HWND}} component produces a column "Title" which is truncated to 5 characters:
C:\...
The full output for one explorer window looks like:
LocationURL Title
----------- -----
file:///C:/open/this/Favorite%20%folder C:...
I found I could avoid the truncation by padding the string 'Title' with many spaces. That string's width seems to determine the maximum width of the output.
Still, I observe that only about 60% of the open explorer windows list a path. The rest are just a blank line.
I tried "$app.Windows() | Select-Object LocationName", but the output only contains the Explorer folder name only, not the full path and folder that is displayed in the Explorer title.
Another mystery is why the script runs so slowly. If I have 10 explorer windows open, the script runs for 30 seconds, taking about 3 seconds per path.
For this script:
function Get-WindowTitle($handle) {
Get-Process |
Where-Object { $_.MainWindowHandle -eq $handle } |
Select-Object -Expand MainWindowTitle
}
$app = New-Object -COM 'Shell.Application'
$app.Windows() |
Select-Object LocationName,#{n=' ------------Title---------------- ';e={Get-WindowTitle $_.HWND}}
This is the output (with some redacting with *** for privacy)
PS C:\E***> .\OpenExplorer.ps1
LocationName ------------Title----------------
------------ ----------------------------------------------------------------------------------
2019-07
Ame****
2019 Priv...
2019-10-3... C:\E\Event Presentations\2019-10-31 Priv**********bcast
E C:\E
5G Brief ... C:\E\Tech************ing\5G Brief (2018)
36 Series...
2019 DE* ... C:\E\16*****N\2019 DE*******************
Newsletters C:\E\Newsletters
Reports C:\E\Tech************ing\Reports
2019-10-2... C:\E\16**********s\2019-10-29 *********************
2019-11 C:\Data\Docs\Stand*********24\2019-11
UB****
Financial... C:\E\Financ************
Expenses C:\E\Internal\Expenses
E C:\E
E***
I assume what you're really interested is the local filesystem paths of the open Explorer windows, not necessarily the window titles (which aren't guaranteed to reflect the full paths).
Somewhat obscurely, the window objects returned by the .Windows() method contain the local path representation in their .Document.Folder.Self.Path property.
(New-Object -ComObject 'Shell.Application').Windows() | ForEach-Object {
$localPath = $_.Document.Folder.Self.Path
"C:\WINDOWS\explorer.exe /e, `"$localPath`""
}
The above produces output such as:
C:\WINDOWS\explorer.exe /e, "C:\Users\jdoe"
C:\WINDOWS\explorer.exe /e, "C:\Program Files"
You can output this to a batch file file as needed, e.g. by appending | Set-Content file.cmd to the above command.
Note: The windows are listed in the order in which they were created, so you cannot infer which among them was most recently activated. See this answer for a solution that finds the topmost File Explorer window and determines the path shown in it.
I found I could avoid the truncation
The truncation is just a display artifact - the data is still there.
You can make the data visible in one of two ways:
pipe to Format-Table -AutoSize to make sure that column values aren't truncated, space permitting
pipe to Format-List, which will show each property on its own line (line-wrapping overly long values).

SCCM 2012 OSD Task Sequence - Rename computer with Service Tag

I am planning to deploy Windows 10 using SCCM 2012. It is working fine, and now I just want to rename the computer to be same as its DELL service tag, and make it as part of Task Sequence. I would ideally like to use Powershell script to do so, however happy to use VBS as well, in case it isn't easy enough with PS.
Following is the Powershell script that does the job, however I can't add it as part of Task Sequence!
$sTag = Get-WmiObject -Class win32_BIOS | Select SerialNumber
$cName = 'DESKTOP' + $sTag.SerialNumber
Rename-Computer -NewName $cName
Can someone please assist?
Thanks in advance.
I think you would be better off not renaming the computer after it is already present in sccm and ad but give it a proper name before it is joined (assuming you use unknown computer support for the osd here)
In this case you should set the SCCM Variable OSDCOmputerName already within the WinPE phase like this (you can find more detailed examples e.g. here):
$sTag = Get-WmiObject -Class win32_BIOS | Select SerialNumber
$OSDComputerName = 'DESKTOP' + $sTag.SerialNumber
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$TSEnv.Value("OSDComputerName") = $OSDComputerName
If you want to use powershell in PE you will have to modify your boot image (Right click --> Properties --> Optional Components) to include "Windows PowerShell"

PowerShell Script Running as a Service Behaves Strangely

PowerShell Script Running as a Service Behaves Strangely
The Project:
Create a background process that determines if the on board network card is connected. If it is connected, disable the wireless network card. When the onboard network card is not connected, re-enable the wireless card.
Why:
Users hot-dock all the time, getting funky routing tables OR get bound to the wrong DNS servers. When they attempt to access a local resource, say printers, they aren’t able to and then are in my cube (they would file a ticket, but that too would be a local resource). Trying to convince users to disable their own wireless (via switch on laptop) or not hot dock has met with limited success.
The Problem:
The PowerShell script below does run, and does work under my testing conditions. Likely under most testing conditions as the code and wmi queries are pretty generic. Running the script manually yields the expected results, HOWEVER running the script as a service via the only method I could find, srvany.exe, yielded unexpected results and “broke stuff”.
Details:
Running the script as a service, via srvany.exe, works ONCE. When the loop comes back around to test the network connection or tries the method to enable or disable it. The errors indicate that “get-wmiobject” is not a proper Cmdlet. Huh? It works, manually, it works once, but a second time after it disabled the wireless network card it does not. Worse yet MY shell , outside of the service, suddenly can’t do a get-wmiobject, until…. until you go into Device Manager and re-enable the wireless network card yourself.
Debugging attempts:
I rewrote the script and cleaned it up a little to allow for it to get the objects outside of the Do While loop. Nothing changed, but I left the script that way as it seems cleaner anyhow. I enabled “Interact with Desktop” in the service properties and sure enough you can see the script trying to work and getting the before mentioned errors.
Please help. Again the object here is to run a background process, one with enough privileges in Vista or 7 to disable and enable the wireless network card.
#***********************************************************************
# "switch-wifi-srv.ps1"
# This script attempts to identify if a wired network card is in use if
# one is, the Wireless network card is disabled, until the wired network
# card is no longer in use.
#
# Written by Aaron Wurthmann - aaron (AT) wurthmann (DOT) com
#
# 2010.02.10 ver 2 (Service Version)
# If you edit please keep my name or at the very least original author's.
# As of this writing I am unsure if script will work with all scenarios,
# however it has worked for me on Dell laptops running Windows 7 x64.
# -----------------------------------------------------------------------
# This script comes with ABSOLUTELY NO WARRANTY.
# You may redistribute copies of the script under
# the terms of the GNU General Public License.
# -----------------------------------------------------------------------
# Service Installation:
# Aquire and install the Windows 2003 Resource Kit OR the srvany.exe.
# Use sc.exe and srvany.exe to create a service....
# sc create SwitchWifiAuto binPath= "C:\Program Files (x86)\Windows Resource Kits\Tools\srvany.exe" DisplayName= "Switch Wifi Automatically"
# Edit registry entry for SwitchWifiAuto, add a key and a string value...
# HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\SwitchWifiAuto\Parameters]
# "Application"="C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy RemoteSigned -File C:\\SwitchWifiAuto\\switch-wifi-srv.ps1"
#************************************************************************
$state=""
$wireStatus=""
$wifiStatus=""
# Get Wired and Wireless Card Objects
$objWire=get-wmiobject -class win32_networkadapter -namespace root\CIMV2 | Where-Object {$_.Name -notmatch "Wireless" -and $_.Name -notmatch "Virtual" -and $_.PhysicalAdapter -eq "True"}
$objWifi=get-wmiobject -class win32_networkadapter -namespace root\CIMV2 | where-object {$_.Name -match "Wireless"}
# Get Name of Service to be Used in totally useless Do While Loop
$objService=get-service -display "Switch Wifi Automatically"
# Begin Do While Loop
Do {
# Get status of wired network card. If enabled and connected set $state to Disable (which will later Disable the Wifi network card)
[string]$wireStatus=$objWire | % {$_.NetEnabled}
if($wireStatus -eq "True") {
$state="Disable"
}
# Get Status of wireless card.
if($objWifi){
[string]$wifiStatus=$objWifi | % {$_.NetEnabled}
# If $state is not set to disable and if the wireless card is currently disabled, enable it.
if($state -ne "Disable") {
if($wifiStatus -eq "False") {
Out-Null -InputOject ($objWifi | % {$_.Enable()})
}
# If $state is set to Disable and if wireless card is currently enabled, disable it.
} else {
if($wifiStatus -eq "True") {
Out-Null -InputOject ($objWifi | % {$_.Disable()})
}
}
}
# Reset Checked Variables for good measure
$state=""
$wireStatus=""
$wifiStatus=""
# Sleep for 120 seconds (two minutes)
Start-Sleep -s 120
# Continuing looping (do while) until the service is not running.
# This is of course technically useless as when the service is not running neither is the script to check if the service is not running.
# I made it this way however because I don't like infinite loops and I thought it would be funny to it this way instead of while $var=0
} while ($objService.Status -eq "Running")
Try to remove any output. Service don't have stdout stream. And when the buffer is full strange thing happens. Just a guess ( I never used powershell ).
Debugging attempts: I rewrote the script and cleaned it up a little to
allow for it to get the objects outside of the Do While loop.
You need to include these within the loop or you will not get updated values and the loop will do nothing.

Resources