Get default printer remotely - powershell

Windows 7:
cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\prnmngr.vbs -g
Windows XP:
cscript C:\windows\system32\prnmngr.vbs -g
These will get the default printer of the current system. I was wondering if there is a way to run this on my computer to get the default printer of a remote computer by computer name?
I tried running:
psexec \\c78572 -i -d cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\prnmngr.vbs -g
And it appears to run.. but I only see the results in a quick popup cmd line window on the remote computer and not on mine. All I see on my end is:
cscript started with process ID 568.
In powershell gwmi win32_printer -computername c78572 works.. but I don't know how to sort it to show me the default printer.
EDIT 12/20/13 I am trying to combine it with a show all printers and the default but I can't get it to work:
while (1) {
$tag1 = Read-Host 'Enter tag # or Q to quit'
if ($tag1 -eq "Q") {
break;
}
cls
sc.exe \\$tag1 start RemoteRegistry;
cls
start-sleep -seconds 2
cls
$OSInfo = get-wmiobject -class win32_operatingsystem -computername $tag1;
$OSInfo | Format-Table -Property #{Name="OS Name";Expression={$_.Caption}},#{Name="System Boot Time";Expression={$_.ConvertToDateTime($_.LastBootUpTime)}} -AutoSize;
gwmi win32_printer -computername $tag1 | ft -Property #{Name="Printer Name";Expression={$_.Name}} -AutoSize;
$Computer = $tag1
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('currentuser', $Computer)
$RegKey= $Reg.OpenSubKey('Software\Microsoft\Windows NT\CurrentVersion\Windows')
$DefaultPrinter = $RegKey.GetValue("Device")
$DefaultPrinter | ConvertFrom-Csv -Header Name, Provider, Order| Select Name
# Alt method: Get-WmiObject win32_printer -computername c60311
}

You can use wmi32_printer to get the default. Here is the code:
$AllPrinters = gwmi win32_printer -computername c78572
$DefaultPrinter = $AllPrinters | where {$_.Default -eq $true}
This will return all locally attached printers. If you want to get a list of network attached printers (as Aaron commented below), you run into a little bit of an issue. The above script doesn't work because WMI operates on the local machine, and not on the user level. After much research, one way of getting this information is to have a log on script that runs, because there is essentially no other way of remotely using WMI to get the logged in user's information.
How to really do it if we can't use WMI? Use the back door. All the pertinent information is stored in the registry. The output may not be pretty, but it will give you all the information that we need. We are only concerned about 3 key locations:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers
This contains all Locally Installed printers. Forget about it, use the gwmi win32_printer command to get this list.
HKEY_CURRENT_USER\Printers\Settings
This contains all the Currently logged in User Installed printers. It does not have the default printer information.
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows\Device
This is where to get the Currently logged in User Installed Default printer. i.e. This is what Aaron is specifically looking for.
So, we can use PowerShell to connect to the remote registry, and read the currently logged in user's default printer with the following script:
$Computer = "c78572"
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('currentuser', $Computer)
$RegKey= $Reg.OpenSubKey('Software\Microsoft\Windows NT\CurrentVersion\Windows')
$DefaultPrinter = $RegKey.GetValue("Device")
$DefaultPrinter | ConvertFrom-Csv -Header Name, Provider, Order| Select Name
----EDIT - to get a list of all printers----
To list all printers on the remote computer:
$Computer = "c78572"
#Get Local Printers:
$Printers = #(Get-WmiObject win32_printer -computername $Computer | Select Name)
#Get List of Network Printers:
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('currentuser', $Computer)
$RegKey= $Reg.OpenSubKey('Printers\Settings')
$Printers += #($RegKey.GetValueNames())
#Output List of Printers
Write-Output $Printers | ft -Property #{Name="Printer Name";Expression={$_.Name}} -AutoSize
#Get Default Printer
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('currentuser', $Computer)
$RegKey= $Reg.OpenSubKey('Software\Microsoft\Windows NT\CurrentVersion\Windows')
$DefaultPrinter = $RegKey.GetValue("Device")
#Output the Default Printer
Write-Output $DefaultPrinter | ConvertFrom-Csv -Header Name, Provider, Order| Select Name | ft -Property #{Name="Default Printer Name";Expression={$_.Name}} -AutoSize

This script will return the specified computer's currently-logged-in user's default printer (read from the Registry).
We're trying to clean up some network printer connections, and a script like this that shows the shared printers that a user is connected to is something we really need.
My primary challenge was figuring out a way to get at the "current user" information (as opposed to the "computer" information). The shared printer connections are stored in the user area, so that's where I needed to be.
I pieced together information from several sources to do it this way:
Determine the logged-in user (account)
Get the SID for that user
Use the SID to navigate to the user's HKEY_USERS Registry hive
Output the value in SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\Device. Before coding I determined that this Registry value is updated immediately upon a default printer change. BONUS: You can also change/set the user's default printer by updating this Registry value.
# ---------------------------------------------------------------------------
#
# This script requires a computer name. It will return the computer's
# currently logged-in user's default printer.
#
# ---------------------------------------------------------------------------
# Set the variable below to choose your computer
$Computer = "computer_name"
# get the logged-in user of the specified computer
$user = Get-WmiObject –ComputerName $computer –Class Win32_ComputerSystem | Select-Object UserName
# get that user's AD object
$AdObj = New-Object System.Security.Principal.NTAccount($user.UserName)
# get the SID for the user's AD Object
$strSID = $AdObj.Translate([System.Security.Principal.SecurityIdentifier])
# get a handle to the "USERS" hive on the computer
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("Users", $Computer)
# get a handle to the current user's USERS Registry key where the default printer value lives
$regKey = $reg.OpenSubKey("$strSID\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows")
# read and show the new value from the Registry for verification
$regValue = $regKey.GetValue("Device")
write-output $regValue
write-output " "
write-output " "
[void](Read-Host 'Press Enter to continue…')

Related

Get List of Printer Drivers from list of Computers PowerShell

I've tried a variety of iterations of this and gotten a range of errors. I'm trying to get a a list of installed drivers off from a list of computers. None of the ways I've tried in PowerShell have piped the information into a csv. Here's the current iteration of the script.
#Load Active Directory
Import-Module activedirectory
#Load list of computers
$results = #()
$Computer = Get-Content -path 'C:\ScriptResources\computers.txt'
#Check each computer in the list
foreach($ComputerName in $Computer)
{
$results += Get-ADComputer -Filter " Name -Like '*$ComputerName*' " | Get-PrinterDriver; Start-Sleep -milliseconds 500
}
#Export to CSV file
$results | export-csv 'C:\ScriptResults\InstalledPrinters.csv'
I've also used it with just the Get-Printer command and got the following error.
Get-Printer : No MSFT_Printer objects found with property 'Name' equal to 'Redacted'. Verify the value of the
property and retry.
Depending what I've fed the $Computer file I'll get different errors. I've also gotten the RPC server is unavailable and Error Spooler Service Not Running. I have domain wide privileges and I checked the print spooler service and it is running.
The reason I think this is odd is that I have .bat tool that I use that gets printer info from a singular host and I don't run into any issues. The reason I'm trying to put this in PowerShell is because 1) I want to do the whole domain and 2) PowerShell formats its outputs in a more useable fashion.
wmic /node:%ComputerIP% path win32_printer get deviceid, drivername, portname
Additionally, I've also tried the following in the $results function of the script
$results += Get-WmiObject -class Win32_printer -ComputerName name, systemName, shareName
This didn't give errors. What it did instead is that for each computer in the list of computers it checked the computer I was running the script from for its printers and output on each line which printers were installed on my computer.
I'm at a loss and any help would be appreciated. Thanks!
Just so this is closed out. Vivek's answer ended up working.
$results += Get-WmiObject -class Win32_printer -ComputerName $Computer | Select name, systemName, shareName
The RPC issue I was getting was that the list of computers were all turned off for some reason (remote site + different time zone + doing the testing during second shift). Normally, everything remains on though. So that was just an anomaly.
Thanks for the help!

Why am i receiving RPC server is unavailable error when looping?

I have a Powershell script to find specific servers and their corresponding service accounts. If I modify the script to use a single server and a single service account, the results are what I expect. If I loop thru the servers and accounts, I receive the following error:
#################################################################
# Find Service Account(s) used to start Services on a Server(s) #
#################################################################
$accounts = (Get-Content C:\Users\location\Scripts\Service_Accounts.txt)
Remove-Item -path C:\Users\location\Scripts\ServiceAccountFnd.txt -force -erroraction silentlycontinue
Import-Module ActiveDirectory # Imports the Active Directory PowerShell module #
## Retrieves servers in the domain based on the search criteria ##
$servers=Get-ADComputer -Filter {Name -Like "namehere*"} -property *
## For Each Server, find the services running under the user specified in $account ##
ForEach ($server in $servers) {
Write-Host $server
ForEach ($account in $accounts) {
Write-Host $account
Get-WmiObject Win32_Service -ComputerName $server | Where-Object {$_.StartName -like "*$account*"} | Format-Table -HideTableHeaders -property #{n='ServerName';e={$_.__SERVER}}, StartName, Name -AutoSize | Out-File -FilePath C:\Users\location\Scripts\ServiceAccountFnd.txt -append -Width 150
}
}
Your $server variable does not only contain the hostname, but also all attributes of the AD computer object.
Try to change the ComputerName value to $server.name.
If that doesn't help: Can you confirm, that you used the very same computer in the loop as without the loop, as you described? I'd assume that you try to access another computer, which is not configured as expected.
Besided that, I'd recommend you to use Get-CimInstance rather than Get-WmiObject, as it doesn't use RPC, but WinRM by default. WinRM is more firewall friendly, secure and faster.

SCCM Device Collection Query: Find all clients in intranet

I'm trying to create a WMI query for a device collection to find all computers that are currently outside our intranet. I can do this in Powershell by executing:
(Get-WmiObject -namespace root\ccm -query "select InInternet from ClientInfo").InInternet
But I cannot find the appropriate query in SCCM.
In configuration manager on the client you are able to see the "Connection Type" and whether or not it's currently Intranet or Internet.
Does anyone know if this is possible in an SCCM query?
AFAIK SCCM doesn't collect Connection type, probably because it changes too often (or at least can do). The only server-side query I can think of is to check if the last MP was one of the internet-enabled MPs. Ex:
SELECT * FROM SMS_R_System WHERE ResourceID IN ( SELECT ResourceID FROM SMS_G_System_CH_ClientSummary WHERE LastMPServerName IN ('InternetEnabledMP.DMZ.contoso.local','MySecondInternetEnabledMP.DMZ.contoso.local'))"
if (Get-WmiObject -namespace root\ccm -query "select InInternet from ClientInfo").InInternet can return the correct data, you should still be able to get all result from clients one by one by running command on a remote machine using -computername property:
Import-Module 'C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
cd cts:
$devices = (Get-CMDevice -CollectionName "All Desktop and Server Clients").name
Foreach ($device in $devices)
{
if(Test-Connection -ComputerName $device -Count 1 -Quiet)
{
$InInternet = (Get-WmiObject -ComputerName $device -Namespace root\ccm -Query 'select InInternet from ClientInfo').InInternet
$properties = #{'name' = $device; 'IsInternet' = $InInternet}
$Object = New-Object -TypeName PSObject -Property $properties
Write-Output $Object
}else{
Write-Warning "Try connection to $device failed!"
}
}
The script is not a complete script because it did not catch exceptions when trying to connect to target machine to get property. But it should be able to tell what I mean here and should be able to work. You may need to run script under admin permission

Working with Windows Event Logs in PowerShell

I have a script to read the last 30 entries for the Application and System event logs, currently the scripts only works on my machine and only outputs a partial message (see below for example).
411905 May 05 15:05 Information Microsoft-Windows... 1501 The Group Policy settings for the user were processed successfully. There were no changes detected since the last succ...
Can anyone tell me how the following can be done
use this for remote computers - I have tried entering the computer name in the format of \domain\computername but is doesn't work
How I can display the full message and not just a section
How I can save the file with the computer name as part of the file name e.g. "mycomputer application log.txt"
My script so far is like this
Get-EventLog -LogName Application -Newest 30 -ComputerName MYCOMPUTER | Out-File -FilePath "D:\Test\Application Event Logs.txt"
Get-EventLog -LogName System -Newest 30 -ComputerName MYCOMPUTER | Out-File -FilePath "D:\Test\System Event Logs.txt"
I am new to scripting and thought this could be a useful script to have but I can't get it to work.
Remote Computers
If you mean computers in another domain, then I don't think you can using the cmdlet alone.
Instead, you can use PowerShell remoting to run any powershell commands that exist on the remote computer. But you have to set up remotin gon the remote machine, and use SSL or trusted hosts, with explicit credentials to ensure the connection will be allowed:
$credential = Get-Credential # enter credentials for remote machine
$session = New-PSSession -ComputerName REMOTECOMPUTER -Credential $credential
Invoke-Command -Session $session -ScriptBlock {
Get-EventLog # parameters
}
The Full Text
It's important to note that what is returned by Get-WinEvent is a complex object. What you see when it's displayed on the screen is just a view. Writing it out to a file directly will also be just a view. Instead, explicitly figure out what you want, build a string, and then write it to a file.
Start by assigning the result of the cmdlet to a variable so that you can inspect it:
$events = Get-WinEvent #params
Now you can look at the results:
$events | Get-Member # see what properties are available
So then you can see that Message is a property.
To get just the message, you can use Select-Object and since you want it as a string and not a property, you -ExpandProperty:
$events | Select-Object -ExpandProperty Message | Out-File #etc
That would write out all the messages (but no other info).
In practice, you might want to operate on each log entry returned and build your string to write to the file:
$events | ForEach-Object {
# $_ represents the current object
$msg = $_.Message
$id = $_.Id
$timeCreated = $_.TimeCreated
"A log entry with ID $id was created at $timeCreated, and it says:`r`n`r`n$msg`r`n---------`r`n"
} | Out-File #params
Using the Computer Name
Assuming you know the computer name you're checking in advance, put it in a variable, then embed it in the file name:
$computer = 'MYCOMPUTER'
Get-WinEvent -ComputerName $computer | ForEach-Object {
# do stuff like above
} | Out-File -Path "D:\Whatever\$computer Application Log"

Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x80070 6BA

I have what should be a simple script that will connect to all the servers in a domain and build a table of all the services running on each server. However, when I try to automate the script to grab all the servers in a foreach loop I get an RPC error. If the $name variable is replaced with the server DNS name everything works as expected. I've checked the firewall and DCOM services on my system (win7) and the servers (2000 - 2008R2) and these are all enabled or disabled appropriately. So, I'm thinking something in the script is broke. I'm still learning powershell, so any tips are appreciated.
Here is the script so far.
$servernames = get-adobject -Filter 'ObjectClass -eq "Computer" ' -Searchbase "OU=Servers,DC=E,DC=BENEFIS,DC=ORG"
foreach ($name in $servernames) {
Get-WMIObject win32_service -computername $name -Property SystemName,Name,StartName,StartMode |
Format-table SystemName, Name, Startname >c:\serverservices.txt }
Each object you get back have a name property so you need to pass its value to the ComputerName parameter. In addition, to get computer object use the Get-ADComputer cmdlet, you also need to specify the Append switch when you export to the file otherwise content will be overwritten and what you'll see finally is the output of the last computer only.
$servernames = Get-ADComputer -SearchBase "OU=Servers,DC=E,DC=BENEFIS,DC=ORG" -Filter *
foreach ($name in $servernames)
{
Get-WMIObject win32_service -computername $name.Name -Property SystemName,Name,StartName,StartMode |
Format-table SystemName, Name, Startname | Out-File c:\serverservices.txt -Append
}