Identify & Disable Locally attached print devices

Good Morning,

Basically, what I am trying to do is use a PowerShell script to identify, disable any print device connected to a laptop.(When connected to our corporate VPN.) Users in our environment run as standard users. I guess I want to get feedback on script structure and feedback on an easier way. Also, I am using “SubinACL” to adjust the ACE on the printer objects found. I know this is an old utility. I couldn’t figure out a way with PowerShell to do it.(any suggestion otherwise would be gladly accepted:)) The script works. I am just not sure if it is the best way?

one particular item I am struggling with is this line below. What I am trying to do is find all the printers with specific port names and that the printer is local. The idea the filter would return locally connected print devices. The reason I can’t just use “Local” is that it will return Windows print to file printer objects like: “Send To OneNote”, “Print as a PDF”, “Microsoft XPS Document Writer” you get the idea. The line below works, I guess I was trying to think of an easy way to manage the script in case we needed to add more port names. My thought was returning the port names and adding them to an Array to reference. The issue I am having is if multiple printers are connected via USB the system will uptick the numeric value of the port: USB001, USB002 etc. hince the use of the “*”.

Get-WmiObject win32_printer | Where-Object {($.Portname -ilike “USB*” -or $.portname -like “BTH*”) -and ($_.local -eq $true)}

So, the command is working, you just want a “cleaner” command? If so, always try to filter at the WMI level and only return what you want to the pipeline. Think of your command as “Get every printer and now find printer with criteria X” versus “Find only local printers and now find printer with criteria X”. Probably not a huge issue in this scenario, but if you ran this on say a print servers with hundreds of printers and were only looking for HP printers, it would come back much faster :

Get-WMIObject -Class Win32_Printer -Filter "Local=True And PortName Like '%USB%' Or PortName Like '%BTH%'"

Note that you use % wildcard because this is a WQL query, not Powershell that uses a * wildcard

Another possibility is to use a Regex match with the pipe operator which is OR

Get-WMIObject -Class Win32_Printer -Filter "Local=True" | Where{$_.PortName -match "usb|bth"}

So the only what that I can get the Array to populate the printers is with the following:

{Get-WmiObject win32_printer | where-object {($_.portname -match "BTH") -or ($_.portname -match "USB")}

I tried your suggestion(s) but it didn’t work?

One other issue is my foreach loop is not running through all of the printers.

 foreach($stg in $LPtr){
&$shSubinACL /printer $LPtr.name /deny=$localusr=p
}
ELSE{
&$shSubinACL /printer $LPtr.name /Grant=$localusr=f
}

   

I apologize, looking over it again I had some typo’s in the commands you had. They do work. Now though I am still having the issue with my “foreach” loop not executing on all the printers in the Array. It seems to be just running on one?

That code isn’t correct, it’s part for loop and part IF, I assume it’s just a mistype, but should look more like this:

$printers = Get-WMIObject -Class Win32_Printer -Filter "Local=True" | Where{$_.PortName -match "usb|bth"}
foreach($printer in $printers){
    if ($printer.SOMETHING -eq "X") {
        &$shSubinACL /printer $printer.name /deny=$localusr=p
    }
    ELSE{
        &$shSubinACL /printer $printer.name /Grant=$localusr=f
    }
}

I don’t know what your criteria would be for running one command or the other, but you would need to update $printer.Something logic. You might also have a problem running the command synchronously in your loop. You need to either run the command and wait for it to complete using Start-Process or System.Diagnostics.Process, see:

https://powershell.org/forums/topic/delete-files-after-processing/

or do the command asynchronously using CMD \C to spawn an outside instance to run each command in it’s own command prompt and then close, see:

https://powershell.org/forums/topic/invoking-registry-uninstallstring/

Ok, I think I got it! So I change an item of the script like:

 $Lptrs = Get-WmiObject win32_printer -Filter "Local=true" | where{$_.PortName -match "USB|BTH"}
$Lptrs = $Lptrs.name 

I also adjusted my mistake as you pointed out:

 ForEach($Lptr in $Lptrs){

    If (($CorpNetCheck.CorpNetworkOnlineStatus -eq 0) -or ($CorpVPNCheck.CorpVPNCheck -eq 1)){
    
        &$shSubinACL /printer $LPtr /deny=$Localusr=p
}

    ELSE{
   
        &$shSubinACL /printer $LPtr /Grant=$Localusr=f 

One issue I did run into initially was I was using “$LPtrs” in the Foreach loop and it still wasn’t working. I changed it to “$Lptr”(the placeholder variable) and it WORKED! I like that it works, but could you educated me as to why this is? I have my thoughts but…

In Powershell 3, they introduced implicit for loops. Basically, Powershell does the foreach for you if you reference an object property versus the second example where a manual foreach is done. This is important because if you write a script using implicit loops, it will not work on Powershell V2, the default Powershell version on Windows 7 machines.

PS C:\> $processes = Get-Process | Select -First 3

PS C:\> $processes.Name
agent
armsvc
audiodg

PS C:\> $processes | foreach{$_.Name}
agent
armsvc
audiodg

In your case $Lptrs is a collection of objects. The whole point of a for loop is that you want to do something for each object in that collection. You were looping through each object and then referencing the entire collection (e.g. $processes.Name) in your loop:

PS C:\> foreach[$process in $processes]{"shSubinACL {0}" -f $processes.Name}
shSubinACL agent
shSubinACL agent
shSubinACL agent

When you print the command, you can see that it’s just using the first indexed item of the collection, so if you reference the variable for the individual (e.g. $process.Name)

PS C:\> foreach[$process in $processes]{"shSubinACL {0}" -f $process.Name}
shSubinACL agent
shSubinACL armsvc
shSubinACL audiodg

Now you are referencing the individual indexed item and getting it’s properties versus the collection.

I appreciate the explanation thanks! And thanks for all of your help!!!