PowerShell script to connect all network printers on servers and then remove them - windows

I have a list of around 15 servers that for each one of them I want to connect to all network printers on a printer server, and then remove all connections.
This is the powershell script I wrote-
$servers = Get-Content -Path <path to csv>
foreach ($server in $servers){
$printers = Import-Csv -Path <path to csv>
foreach ($printer in $printers){
$printername = "\\printerserver\" + $printer
Add-Printer -ConnectionName $printername | Out-Null
}
Get-WmiObject Win32_Printer | where{$_.Network -eq ‘true‘} | foreach{$_.delete()}
}
The servers CSV contains a single column with FQDN'S of said servers.
The printers CSV contains plain names of network printers.
This is the error I am receiving:
Add-Printer : The specified server does not exist, or the server or printer name is invalid. Names may not contain ',' or '\' characters.
At line:9 char:1
+ Add-Printer -ConnectionName $printername | Out-Null
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (MSFT_Printer:ROOT/StandardCimv2/MSFT_Printer) [Add-Printer], CimException
+ FullyQualifiedErrorId : HRESULT 0x80070709,Add-Printer
What am I doing wrong here? Thanks.
Edit 1:
Content of servers csv:
servername
server01.domain
server02.domain
server03.domain
Content of printers csv:
printername
printer01
printer02
printer03

Related

Powershell Restart Script

I'm attempting to write a script that gives me a running list of computers whose name starts with SOU-C128*. I'm assigning the list to a variable and using it as input for the restart-computer cmdlet. However, I'm recieving the error provided:
restart-computer : Computer name #{Name=SOU-C127-04} cannot be resolved with the exception: One or more errors occurred..
At \\nas\user\IT\restart.ps1:2 char:1
+ restart-computer -computername $computers -force -wsmanauthentication ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (#{Name=SOU-C127-04}:String) [Restart-Computer], InvalidOperationException
+ FullyQualifiedErrorId : AddressResolutionException,Microsoft.PowerShell.Commands.RestartComputerCommand
This is the script
$computers=Get-ADComputer -Filter * | Where-Object {$_.Name -like "sou-c127*"} | Select -Property Name | Sort Name
restart-computer -computername $computers -force -wsmanauthentication Kerberos
Please assist!
Thanks.
Your immediate fix would be to use $computers.name or, expand the property in your Get-ADComputer by using Select-Objet -ExpandProperty 'Name'; foreach name would suffice too.
Wanted to suggest something though. In PowerShell when you're filtering, you always want to filter as far left as possible. This can become computationally expensive running a pipeline filter such as Where-Object on a large list. In your case: Get-ADComputer -Filter *, you're filtering for every computer, then filtering again. You can cut that out by filtering using the filter in your Get-ADComputer cmdlet to begin with:
$computers = Get-ADComputer -Filter "Name -like 'sou-c127*'" | Sort-Object -Property 'Name'
Restart-Computer -ComputerName $computers.Name

Folder audit Powershell script, nested loop

I am trying to get a script working to audit folder permissions on a Windows server, among other data, and export this data to a CSV file for analysis after a ransomware attack.
I ripped the script from a forum, but it did not run correctly as is. Below is a slightly modified version during my troubleshooting.
I am well versed in batch scripting, and have a decent understanding of loops and pipelining, but this Powershell script has me scratching my head.
It seems like the array is not making it to the nested loop.
I am testing in Windows 10 Pro 21H1, using Powershell version 5.1.19041.1320, build 10.0.19041.1320
##The script:
$ErrorActionPreference = "Continue"
$strComputer = $env:ComputerName
$colDrives = Get-PSDrive -PSProvider Filesystem
ForEach ($DriveLetter in $colDrives) {
$StartPath = "$DriveLetter`:\"
Get-ChildItem -LiteralPath $StartPath -Recurse | ?{ $_.PSIsContainer } |
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Item -
LiteralPath $FullPath}.Directoryinfo.GetAccessControl())}
Select #{N='Server Name';E={$strComputer}}
#{N='Full Path';E={$FullPath}}
#{N='Type';E={If($FullPath.PSIsContainer -eq $True) {'D'} Else {'F'}}}
#{N='Owner';E={$_.Owner}}
#{N='Trustee';E={$_.IdentityReference}}
#{N='Inherited';E={$_.IsInherited}}
#{N='Inheritance Flags';E={$_.InheritanceFlags}}
#{N='Ace Flags';E={$_.PropagationFlags}}
#{N='Ace Type';E={$_.AccessControlType}}
#{N='Access Masks';E={$_.FileSystemRights}}
Export-CSV -NoTypeInformation -Delimiter "|" -Path "$strComputer`_$DriveLetter.csv"
##The error I am getting:
You cannot call a method on a null-valued expression.
At C:\Users\user\Documents\fileaudit2.ps1:8 char:13
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $ ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidOperation: (:) [], RuntimeException
FullyQualifiedErrorId : InvokeMethodOnNull
##when I modify the nested loop as follows:
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Item -LiteralPath $FullPath}).Directoryinfo.GetAccessControl()}
##I get the error:
Get-Item : Cannot evaluate parameter 'LiteralPath' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without
input.
At C:\Users\user\Documents\fileaudit2.ps1:8 char:46
... Path = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Ite ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : MetadataError: (:) [Get-Item], ParameterBindingException
FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.GetItemCommand
##I'm just wholly struggling to understand what is not working in this loop.
You are mixing a lot of unneeded Get-Item calls in there.
I also would not use Get-PSDrive for this because I assume you don't want to get results for CD drives, USB devices etc in the report.
Try:
# this returns drives WITH a trailing backslash like C:\
$colDrives = ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' }).Name
# or use:
# this returns drives WITHOUT trailing backslash like C:
# $colDrives = (Get-CimInstance -ClassName win32_logicaldisk | Where-Object { $_.DriveType -eq 3 }).DeviceID
$result = foreach ($drive in $colDrives) {
Get-ChildItem -LiteralPath $drive -Directory -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
$path = $_.FullName
$acl = Get-Acl -Path $path
foreach ($access in $acl.Access) {
[PsCustomObject]#{
Server = $env:COMPUTERNAME
Drive = $drive[0] # just the first character of the drive
Directory = $path
Owner = $acl.Owner
Trustee = $access.IdentityReference
Inherited = $access.IsInherited
InheritanceFlags = $access.InheritanceFlags -join ', '
'Ace Flags' = $access.PropagationFlags -join ', '
'Ace Type' = $access.AccessControlType
'Access Masks' = $access.FileSystemRights -join ', '
}
}
}
}
# now you can save your result as CSV file for instance you can double-click to open in Excel:
$result | Export-Csv -Path 'X:\WhereEver\audit.csv' -NoTypeInformation -UseCulture
To do this on several remote machines, wrap it inside Invoke-Command
# set the credentials for admin access on the servers
$cred = Get-Credential 'Please enter your admin credentials'
# create an array of the servers you need to probe
$servers = 'Server01', 'Server02'
$result = Invoke-Command -ComputerName $servers -Credential $cred -ScriptBlock {
$colDrives = ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' }).Name
foreach ($drive in $colDrives) {
# code inside this loop unchanged as above
}
}
# remove the extra properties PowerShell added
$result = $result | Select-Object * -ExcludeProperty PS*, RunspaceId
# output to csv file
$result | Export-Csv -Path 'X:\WhereEver\audit.csv' -NoTypeInformation -UseCulture

PowerShell Script to Ping Computers Works but Give Errors

The script below works and produces output file but gives errors. I've looked at other questions and answers that addresses the same problem in a similar way but I'm still not able to figure out how to fix this problem. I've done this with a splat before but not quite sure how to make it work in this script. All help would be appreciated. Error is first and script is below that.
Error below
Test-Connection : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command
again.
At C:\Temp\PowerShellScripts\PingComputers\colorfulPingMultipleComputers.ps1:6 char:37
+ if (Test-Connection -ComputerName $name -Count 1 -ErrorAction Silen ...
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand
Script below
$Output= #()
$names = Get-content C:\Temp\Computers.txt
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
$Output+= "$name, Pinging"
Write-Host -ForegroundColor Cyan "$Name, Pinging"
}
else{
$Output+= "$name, Not Pinging"
Write-Host Write-Host -ForegroundColor Red "$Name, Not Pinging"
}
}
$Output | Out-file "C:\Temp\PingComputers.csv"
The script produces this output, computer names changed for privacy. So it does what I want it to do, but it still gives the errors as indicated above for each machine
AAA-L-CWCXRQ2, Pinging
BBB-L-DW2FQQ2, Pinging
CCC-L-JN3RM72, Pinging
DDD-L-34SQRQ2, Pinging
EEE-L-84F3ZM2, Pinging
Write-Host FFF-L-1B1CM72, Not Pinging
GGG-L-94XXRQ2, Pinging
Write-Host FFF-L-D5KXRQ2, Not Pinging
Write-Host GGG-L-75SX4Q2, Not Pinging
My guess is you have a blank line at the end of your text file. See this example.
$tempfile = New-TemporaryFile
#'
google.com
bing.com
'# | Set-Content $tempfile -Encoding UTF8
Get-Content $tempfile | foreach {
Test-Connection -ComputerName $_ -Count 1 -ErrorAction SilentlyContinue
}
I'd recommend just changing this line
$names = Get-content C:\Temp\Computers.txt
to
$names = Get-content C:\Temp\Computers.txt | Where-Object {$_}
which will filter out blank lines

New-ADUser unable to concatanate name and domainname

I'm trying to create userprincipalname as combination of username and hardcoded domainname
csv file:
name,displayname,givenname,surname
dhall,Don Hall,Don,Hall
Code:
Import-csv "c:\output.csv" | ForEach-Object {new-aduser -name $_.name -UserPrincipalName ("{0}#{1}" -f $_.name,"Dev.domain.Net") -DisplayName "$($_.givenname $_.surname)" -givenName $_.givenname -surname $_.surname -path "OU=Workspaces,DC=Dev,DC=domain,DC=Net" -AccountPassword (convertto-securestring passs -asplaintext -force) -Enabled [System.Convert]::toboolean($true)) -ChangePasswordAtLogon ([system.convert]::ToBoolean($true))}
And getting:
At line:1 char:159
+ ... -f $_.name,"Dev.domain.Net") -DisplayNa
+
Unexpected token '$_' in expression or stateme
+ CategoryInfo : ParserError: (:)
+ FullyQualifiedErrorId : UnexpectedToken
Tried also -UserPrincipalName ([string]::Concat($_.name,"#dev.domain.net"))
but same error
PS C:\> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 1944
Windows server 2016
Not sure what's causing that error (don't have an AD controller to test against at the moment so can't validate) but in cases where I'm going to need to reuse a property multiple times I tend to assign it to a temporary variable (makes it slightly easier to write the code and don't run into issues with the pipeline variable getting updated).
Import-CSV "c:\output.csv" | ForEach-Object {
$name = $_.name
$upn = "{0}#{1}" -f $name,"dev.domain.net"
New-ADUser -Name $name -UserPrincipalName $upn #...
}

Check If a program is installed on multiple computers using Powershell

I'm having issue with a script I've written and would love some help.
Please note I'm very new to powershell.
I've written a script that uses a txt file that contains remote computers on a domain, I appears to be working to some degree but in the event of a machine being offline I get errors which then loop the script.
$machines
$pcname
Name = 'Machine'
Expression = { $_.PsComputerName }
}
ForEach ($System in $Machines)
{
#Pings machine's found in text file
if (!(test-Connection -ComputerName $System -BufferSize 16 -Count 1 -ea 0 -Quiet))
{
Write-Output "$System Offline"
}
Else
{
#Providing the machine is reachable
#Checks installed programs for products that contain Kaspersky in the name
gwmi win32_product -Filter {Name like "%Kaspersky%"} -ComputerName $Machines | Select-Object -Property $pcname,Name,Version
}
}
At present this runs and output's like so:
Machine Name Version
UKTEST01 Kaspersky Security Center Network Agent 10.1.249
UKTEST02 Kaspersky Endpoint Security 10 for Windows 10.2.1.23
But in the event of a machine not being reachable the following error is given:
gwmi : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At C:\Scripts\Powershell\Kaspersky Endpoint Security 10\Script\New folder\Kaspersky Checker working v2.ps1:15 char:9
+ gwmi win32_product -Filter {Name like "%Kaspersky%"} -ComputerName $Mach ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
And then moves to the next machine in the list, and then repeats from the beginning again.
I'd like for this to simply show as:
UKTEST03 Offline
And stop once the last machine in the txt file is done.
Any help or advise would be greatly appreciated.
This is the perfect time to use a Try/Catch/Finally block. The flow is this : Try the block of code here, if you encounter an error, suppress the message and do what is in the Catch block instead.
I've modified your code a bit, so simply copy this whole code block and drop it in, replacing your Else {scriptblock} in your original code.
Else
{
#Providing the machine is reachable
#Checks installed programs for products that contain Kaspersky in the name
Try {Get-WMIObject -Class win32_product -Filter {Name like "%Kaspersky%"} `
-ComputerName $Machines -ErrorAction STOP |
Select-Object -Property $pcname,Name,Version }
Catch {#If an error, do this instead
Write-Output "$system Offline }
}
}
Your completed answer
I've folded in the change you requested, to keep your script from running on every machine in $machines instead of $system, as you likely intended.
ForEach ($System in $Machines){
#Pings machine's found in text file
if (!(test-Connection -ComputerName $System -BufferSize 16 -Count 1 -ea 0 -Quiet))
{
Write-Output "$System Offline"
}
Else
{
#Providing the machine is reachable
#Checks installed programs for products that contain Kaspersky in the name
Try {Get-WMIObject -Class win32_product -Filter {Name like "%Kaspersky%"} `
-ComputerName $System -ErrorAction STOP |
Select-Object -Property $pcname,Name,Version }
Catch {#If an error, do this instead
Write-Output "$system Offline "}
#EndofElse
}
#EndofForEach
}
You could try this:
$machines=... # your machines' names
foreach ($machine in $machines)
{
trap{"$machine`: not reachable or not running WsMan";continue}
if(test-wsman -ComputerName $machine -ea stop){
gcim -Class CIM_Product -Filter 'Name like "%Kaspersky%"' |
select pscomputername,name,version
}
}
I'm using gcim because gwmi is deprecated.
Correction: the correct name is Kaspersky; I corrected it.

Resources