Struggling with foreach - powershell

I'm rather new and, well, awful at this whole scripting thing: so any help would be appreciated.
Basically I am trying to create a PowerShell script that installs an undefined number of printers on an undefined number of computers. The computer names and printer names will come from local text files.
This is what I have so far:
$credentials = Get-Credential
$printerlist = Get-Content c:\setup\printers.txt
get-content c:\setup\names.txt | foreach-object {foreach($printer in $printerlist){rundll32 printui.dll PrintUIEntry /ge /c $_ /n $printer}}
EDIT: I am getting the error, unable to enumerate per machine printer connections operation could not be completed (error 0x0000007b) I have tried modifying the script anyway i can come up with, which is probably fewer ways than it should be.

I don't think you have an issue with your foreach loop here.
I think it's just the usage of rundll32 printui.dll PrintUIEntry
Install printer:
rundll32 printui.dll,PrintUIEntry /in /c "\\COMPUTER_NAME" /n "\\PRINT_SERVER_NAME\PRINTER_NAME"
Sets default printer:
rundll32 printui.dll,PrintUIEntry /y /c "\\COMPUTER_NAME" /n "\\PRINT_SERVER_NAME\PRINTER_NAME"
Try installing with the /in individually for one computer from powershell console without your script to see if you still get the same error, could be a permission but I don't think so.

Related

WMIC If / Else Statement for Software Version

I'm trying to use PowerShell to move install files, uninstall any previous version of the software, remove the install directories, and execute the BAT file.
My domain has finally updated and allowed WinRM to run on our machines, making patching much easier to facilitate remotely. I'm working my first script to do this involving updating Java. What I want to do is use PowerShell Studio to deploy a script, this script will kill all the tasks Java is attached to, use wmic to query the installed Java version and call for uninstall, and then Start-Process a BAT file which will do the install, and then clean itself up. What's happening is when I run into a machine with NO Java on it, I get "No Instance(s) Available".
Googling and looking around here, I can't seem to get my If / Else statement right and was looking for some help.
taskkill /F /IM iexplorer.exe
taskkill /F /IM chrome.exe
taskkill /F /IM outlook.exe
wmic product where "name like 'Java%%'" call uninstall /nointeractive
Start-Process -FilePath 'C:\Suppot\Java\java.bat' -Verb runas -Wait
RD /S /Q "C:\support\java"
What I would like to happen is to watch the machine update and install Java quietly in the background, refreshing Control Panel to verify in testing, that it works.
What happened is there was an error in the code, and the uninstall worked and it failed after that. On the next run, it now fails when it can't find a version of Java to remove.
The way your script is written is not very PoSh. Essentially you're just running batch code in PowerShell.
For enumerating/killing processes use Get-Process:
Get-Process -Name 'chrome', 'iexplore', 'outlook' | ForEach-Object { $_.Kill() }
For querying WMI you'd use Get-WmiObject or Get-CimInstance (the latter is essentially a modernized version of the former) unless you're really pressed for performance. Then, and only then, you'd resort to wmic.
However, for your particular task one wouldn't use WMI in the first place, because querying the Win32_Product class is considered harmful. Look up the uninstall string in the registry instead, split the string, and run it via Start-Process. Add the argument /qn to the parameter string for an unattended removal.
$path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
Get-ChildItem $path | ForEach-Object {
Get-ItemProperty -Path $_.PSPath | Where-Object {
$_.DisplayName -like '*java*'
} | ForEach-Object {
$cmd, $params = $_.UninstallString -split ' ', 2
Start-Process $cmd -ArgumentList "${params} /qn" -Wait
}
}
Files and folders can be removed with Remove-Item:
Remove-Item 'C:\support\java' -Recurse -Force

Powershell printer script

I will translate my question in english so that everyone can read it!
Am not really good in powershell i work on it for 3 days.
I need to create a powershell script to show what is the default printer selected in the computer ( example :PrinterA) before the execute a line to select another printer as default printer ( PrinterB) .
After that i need to reset the old default printer ( PrinterA).
I execute line:
Get-WmiObject -query " SELECT * FROM Win32_Printer WHERE Default=$true"
to show default printer but i dont know how to memorize it.
To select the (printeB ), I do:
RUNDLL32 PRINTUI.DLL,PrintUIEntry /y /n "PrinteB"
Can you help me please ?
I'd use
$OldDefaultPrinter = (Get-WmiObject win32_printer | Where-Object Default -eq $True).Name
To store the current default printer in a variable.
To restore with your method
RUNDLL32 PRINTUI.DLL,PrintUIEntry /y /n "$OldDefaultPrinter"
you did not mention the specific version of Powershell that you are using. [grin] presuming you are running ps5.1 on win10, you can use the print management cmdlets to do what you need. take a look at this ...
PrintManagement
— https://learn.microsoft.com/en-us/powershell/module/printmanagement/?view=win10-ps
the Get-Printer cmdlet will get info about the available printers. the Set-Printer cmdlet will let you set the default printer.

Run exe with arguments on remote machine

This one is ruining me. Something that I'm pretty sure should be simple just isn't working and it's probably just a single quotation/character out of place.
There's a lync/Skype tool called sefautil.exe that does all kinds of marvelous things the webGUI doesn't. A typical command would be:
C:\Program Files\Skype for Business Server 2015\ResKit>sefautil.exe /server:sfbpool01.domain.local sip:user1#domain.local /setfwddestination:user2#domain.local /enablefwdimmediate
This works fine when remoted onto any of the machines, but I'm really struggling to run it via remote PowerShell.
Whatever commands I try via invoke-command either give me a standard /? response or nothing at all. I've passed the args via -ArgumentList, as a variable, as anything I can think of and it's just not working.
What makes the thing even more tasking as if you run without admin rights, you won't ever get any results. The command has to be ran as an admin. Now I can quite easily put an admin mode checker into my script, but if it's just as easy to send the command as admin I'll take it.
Any help would be massively appreciated.
#qbanet359
I've gone about it a different way which feels a little cheap, but it does work so can't complain.
I've created a scheduled task on the server hosting sefautil.exe to run a batch file under elevated permissions - I've called it sefautil.
I also copied sefaUTIL.exe to C:\TEMP on the server.
Then in my PowerShell script I'm using:
$SERVER = "\\computer1\c$\temp"
"cd \" | Out-File "$SERVER\sefautiltest.bat" -Encoding unicode
"cmd /c C:\Temp\sefautil.exe /server:sfbpool01.ad.leics.gov.uk sip:dols.team#leics.gov.uk" | Out-File "$SERVER\sefautiltest.bat" -Append
gc $SERVER\sefautiltest.bat | out-file $server\sefautil.bat -encoding ascii
Invoke-Command -Credential $CREDS -ComputerName computer 01-ScriptBlock { schtasks /Run /TN sefautil }
It's almost certainly a long winded way of doing this, but it does work.
Thanks for giving me a fresh perspective on things.
A Little late to the party, but I'm sure a lot of other SfB-Admins will struggle here as well.
This is how I made it work:
Invoke-Command -ComputerName $Global:SefaUtilServer -ScriptBlock {&'C:\Program Files\Skype for Business Server 2015\ResKit\SEFAUtil.exe' '/server:epsachhst-lfe11.epsa.swisscom-mcc.local' $args[0] "/enablefwdnoanswer" "/setfwddestination:$($args[1])" "/callanswerwaittime:$($args[2])"} -Credential $Global:LyncSchedTask_Cred -Authentication Credssp -ArgumentList #($UserSip.replace("sip:",""),$Destination,$Delay)
Hint: make sure, that you don't run in the "Powershell Double Hop issue". SefaUtil will make a Connection to a Frontend Server (I assume it's the one with the CMS located) to make the actual changes in the DB. (See my answer on TechNet: https://social.technet.microsoft.com/Forums/office/en-US/5d4c4f90-1b40-4742-ae4b-c2e1a62a0adb/running-sefautil-remotely?forum=lyncdeploy#da6b82b9-cada-420b-a7a7-2110c0ed2280 (by cwa.cloud)
This is the solution I came up with below.
Basically create a batch file with powershell on the SfB server and then run it with psexec to use the System account (psexec had been copied to the server).
Make sure you insert your own server's name where it has "servername" and run the script with an account that has sufficient permissions. Then call the script with the correct parameters.
BTW, I've noticed it can return an error code of 1 if the forwarding is already in place.
param ([string]$FwdUser,[string]$DestUser)
#skype for business phone forwarding
#create a batch file to run the command, run it as system with psexec and remove the batch file afterwards. sefautil does not cooperate with remote execution
$sefautilcmd = "`"C:\Program Files\Skype for Business Server 2015\ResKit\SEFAUtil.exe`" /Server:servername.headoffice.novationleasing.com.au " + $FwdUser + " /setfwddestination:" + $DestUser + " /enablefwdimmediate"
New-Item \\servername\c$\temp\tempfwd.bat
Set-Content \\servername\c$\temp\tempfwd.bat $sefautilcmd
Invoke-Command -ComputerName servername -ScriptBlock {C:\temp\psexec.exe -s -accepteula c:\temp\tempfwd.bat}
Remove-Item \\servername\c$\temp\tempfwd.bat

Link a file extension to an app

I was wondering if it would be possible to create PowerShell script that would link a certain file extension to an application.
For example:
The .ntb extension has to be opened using the following application by default:
C:\Program Files\SMART Technologies\Education Software\Notebook.exe
Why would I need a script for this you ask?
It could indeed be done by using Run With and then ticking the Default box. However I need to perform this on about 150+ computers. So I'd think to run the script when booting once.
I am a newbie when it comes to PowerShell, so if anyone could give a "small" start, I would be grateful.
For a scripted solution I'd use the cmd built-ins assoc and ftype:
$prg = 'C:\Program Files\SMART Technologies\Education Software\Notebook.exe'
$ext = '.ntb'
& cmd /c "ftype SMART.Notebook=`"$prg`" %1"
& cmd /c "assoc $ext=SMART.Notebook"
The above can be run on remote hosts via the Invoke-Command cmdlet:
Invoke-Command -Computer HostA,HostB,... -ScriptBlock {
$prg = 'C:\Program Files\SMART Technologies\Education Software\Notebook.exe'
$ext = '.ntb'
& cmd /c "ftype SMART.Notebook=`"$prg`" %1"
& cmd /c "assoc $ext=SMART.Notebook"
}
Otherwise you'll have to edit the registry, in which case the deployment via group policy would be preferable, as others have already pointed out.

Powershell uninstall program with msiexec

I've run into a problem getting msiexec to remove java with Powershell. I've output my resultant command to the screen and pasted it into a batch file and it runs great. But when it's executed via Powershell it fails saying the "package cannot be found". Can anyone spot what I might be doing wrong? I've looked up and down google and tried a few different ways of executing the command w/o success and with the same result.
cls
$java = Get-WmiObject -Class win32_product | where { $_.Name -like "*Java*"}
$msiexec = "c:\windows\system32\msiexec.exe";
#$msiexecargs = '/x:"$app.LocalPackage" /qr'
$msiexecargs = '/uninstall "$app.IdentifyingNumber" /qr /norestart'
if ($java -ne $null)
{
foreach ($app in $java)
{
write-host $app.LocalPackage
write-host $app.IdentifyingNumber
#&cmd /c "msiexec /uninstall $app.IdentifyingNumber /passive"
#Start-Process -FilePath $msiexec -Arg $msiexecargs -Wait -Passthru
[Diagnostics.Process]::Start($msiexec, $msiexecargs);
}
}
else { Write-Host "nothing to see here..." }
Write-Host "check end"
The goal is to use the Windows 7 logon script to remove all versions of Java on end-user systems and then install the latest. I prefer to make it all Powershell, but if I can't get this working I'll just use a batch file hard coded with the uninstall GUID's
The write-host statements are all for the purpose of debugging, I'm just interested in the execution of msiexec in some variation of this format: msiexec /x {GUID} /passive /norestart
The error I get is:
"This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package."
I know it works on its own, just not in this script...so I'm thinking it's a syntax thing.
If you have any questions let me know.
First you have to know the difference between this:
"$app.IdentifyingNumber"
and this
"$($app.IdentifyingNumber)"
So I think you wanted to use the latter (the code is a little bit confusing because of the commented lines):
&cmd /c "msiexec /uninstall $($app.IdentifyingNumber) /passive"