test-connection that supports wildcard? workaround? - powershell

trying to see if anyone has a known workaround for using the test-connection cmdlet in powershell to ping wildcard entries in DNS.
I'm trying to clean out our DNS db and exported a list from our BIND server and am in the process of just pinging through the 600+ machines to see if anything responds. I made my own simple script but have also found one that works slightly better on this forum. The script works but the cmdlet help files state that the -computername parameter does not support wildcards and sure enough, when i run the script all CNAME records are reporting down/false when they actually should be responding. The code I'm using is below and is kind of messy but I just needed something quick and it works, but I've included it below for reference:
Get-Content -path C:\Work\testy.txt | ForEach-Object { Test-Connection -ComputerName $_ -Count 1 -AsJob } | Get-Job | Receive-Job -Wait | Select-Object #{Name='ComputerName';Expression={$_.Address}},#{Name='Reachable';Expression={if ($_.StatusCode -eq 0) { $true } else { $false }}} |out-file -FilePath c:\work\TEST.txt

As pointed out by briantist, any non-existing record name will do. You could generate a GUID to substitute the * in your record name:
"subdomain.domain.tld","*.domain.tld" |ForEach-Object {
Test-Connection -ComputerName $($_ -replace '\*',"$([guid]::NewGuid())")
}
Your expression for whether it's "Reachable" or not can be simplified as well:
#{Name='Reachable'; Expression={[bool]($_.StatusCode -eq 0)}}

Related

Get-ChildItem on Multiple Computers, Performance Issues

I'm wanting to improve on my script to be able to accomplish the following:
Scan servers based on get-adcomputer on specific OUs.
Scan each server based on whatever drive letter it has.
Scan each server for log4j.
Export all results to a CSV that identifies the folder path, name of file, and the server that the file was found on.
I have been using the following code to start with:
$Servers = Get-ADComputer -Filter * -SearchBase "OU=..." | Select -ExpandProperty Name
foreach ($server in $Servers){
Invoke-Command -ComputerName $Server -ScriptBlock {
$Drives = (Get-PSDrive -PSProvider FileSystem).Root
foreach ($drive in $Drives){
Get-ChildItem -Path $drive -Force -Filter *log4j* -ErrorAction SilentlyContinue | '
foreach{
$Item = $_
$Type = $_.Extension
$Path = $_.FullName
$Folder = $_.PSIsContainer
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}}, `
#{n="Created";e={$Age}},`
#{n="FilePath";e={$Path}},`
#{n="Extension";e={if($Folder){"Folder"}else{$Type}}}`
} | Export-Csv C:\Results.csv -NoType
}
}
I am having the following issues and would like to address them to learn.
How would I be able to get the CSV to appear the way I want, but have it collect the information and store it on my machine instead of having it on each local server?
I have noticed extreme performance issues on the remote hosts when running this. WinRM takes 100% of the processor while it is running. I have tried -Include first, then -Filter, but to no avail. How can this be improved so that at worst, it's solely my workstation that's eating the performance hit?
What exactly do the ` marks do?
I agree with #SantiagoSquarzon - that's going to be a performance hit.
Consider using writing a function to run Get-ChildItem recursively with the -MaxDepth parameter, including a Start-Sleep command to pause occasionally. Also, you may want to note this link
You'd also want to Export-CSV to a shared network drive to collect all the machines' results.
The backticks indicate a continuation of the line, like \ in bash.
Finally, consider using a Scheduled Task or start a powershell sub-process with a lowered process priority, maybe that will help?

Get Service from remote machines in a domain

Trying to get list of all machines in a Domain with a certain service
tried via all posts in here, helped per one machine, but if i use a text file with multiple machines, it failes
$computers = Get-Content c:\script\computers.txt
$service = "*crystal*"
foreach ($computer in $computers) {
$servicestatus = Get-Service -ComputerName $computer -Name $service
}
$Data = $servicestatus | Select-Object Name,Machinename | Format-Table -AutoSize
Write($Data) | Out-File c:\script\output.txt -Append
Expected list of machines with service in table, instead got error:
This operation might require other privileges
same script, but with a direct machine name, works like a charm.
Any clue what is wrong?
Why not use:
Invoke-Command -ComputerName $computers -ScriptBlock {Get-Service -Name *crystal*}
Eventually you may store the result from invoke into a variable and work with it.
The benefit of using Invoke-Command, insted of foreach is that Invoke works in parallel, while foreach is serial ...
Hope it helps!
Best regards,
Ivan

PowerShell check for service existance on remote machine - reliably?

I'm currently trying to check a bunch of remote machines for the existence of certain services. Currently that process is pretty slow (as in > 1 hour for a few hundred machines) which isn't much of a problem but also unreliable (which is a problem).
Some of those machines might be blocking access, turned off or might in some other way limit access to query for the services.
The current approach is to check with Get-Service and as it has proven to be unreliable to also check with Get-WmiObject as well as Get-CimInstance.
The problem is that they don't support timeouts so if a server is just unresponsive it will take quite a bit of time to get a response and because all three options might work or not depending on the machine there really isn't an option to only run one. As one of them might succeed where the others failed I need to check the results of all three calls.
Would anyone have an idea on how to do a sort of preflight check to figure out which command might work or if there is a different approach to checking for the existence of a service that might be more reliable?
The current approach looks akin to this (simplified):
$error.Clear()
$serviceExists = $false
$server = "localhost"
$serviceQuery = Get-Service -ErrorAction SilentlyContinue -Name WinRM -ComputerName $server
$wmiQuery = Get-WmiObject -ErrorAction SilentlyContinue -Class Win32_Service -Filter "Name = 'WinRM'" -ComputerName $server
$cimQuery = Get-CimInstance -ErrorAction SilentlyContinue -ClassName Win32_Service -Filter "Name = 'WinRM'" -ComputerName $server
if ($error.Count -ge 1) {
Write-Output "An error occurred while check for the service existence on $server"
}
if (
($serviceQuery | Where Name -eq 'WinRM') -ne $null -or
($wmiQuery | Where Name -eq 'WinRM') -ne $null -or
($cimQuery | Where Name -eq 'WinRM') -ne $null
) {
$serviceExists = $true
}
As far as I can tell it might be that Get-Service internally uses (the same approach as) Get-WmiObject and Get-CimInstance but I wasn't able to find documentation on that. The machine currently use PowerShell 5.x, PowerShell remoteing might or might not be configured on the servers that are being queried for the service existence.
Edit: Get-Service is unreliable because it might not indicate that there was an issue and just return $null without indicating that there actually was a problem. Also while it might fail one of the other options might still work.

Remote command output to text file (remote system)

I know I must be using these commands wrong but I can't seem to find a solution. I believe the issue is with my use of the invoke-command and out-file. I'm trying to check to see if a process is running on multiple remote machines and write their states back to a text file on the host system. Even if it wrote to the remote system I could work with that but I cant seem to get anything.
$MyDomain=’mydomain’
$MyClearTextUsername=’myusername’
$MyClearTextPassword=’mypassword’
$MyUsernameDomain=$MyDomain+’\’+$MyClearTextUsername
$SecurePassword=Convertto-SecureString –String $MyClearTextPassword
-AsPlainText –force
$MyCredentials=New-object System.Management.Automation.PSCredential
$MyUsernameDomain,$SecurePassword
$Servers = ( "server1","server2","server3")
$output = foreach ($Server in $Servers)
{
$Session = New-PSSession -ComputerName $Server -Credential $MyCredentials
Invoke-Command -Session $Session -ScriptBlock
{
Get-Service -Name service | select name, status, PSComputername, Runspaceid
} | Out-File -filepath 'c:\TEMP\check.txt'
}
Write-output $output | Out-File -filepath 'c:\TEMP\check.txt'
edit: I don't believe the last line is needed but I threw it in just to see if I could get anything out.
You are not capturing anything in $output because you are redirecting all of the output from your Invoke-Command to Out-File -filepath 'c:\TEMP\check.txt'. Get-Service doesn't return that much data, especially once's it's been deserialized when it returns from the remote session, so I wouldn't bother with the Select statement. Even if you do want to include the Select statement you are specifying PSComputerName which doesn't get added until the data comes back from the remote system, so you may want to move that Select to outside of the scriptblock and after the Invoke-Command in the pipeline. Plus, since you are outputting with Out-File your local file is being overwritten each time that call is made, so the first server's results are saved, then overwritten by the second server's results, then by the third server's results. After that, since $output has nothing (as all output was redirected to file), you are outputting an empty variable to the same file, effectively erasing the service state of the third server.
But this really all becomes a moot point if the script is run with the credentials that has access to the remote servers. You can specify one or more computer names to the Get-Service cmdlet, so this could become as simple as:
$Results = Get-Service Service -ComputerName 'Server1','Server2','Server3'
$Results | Select name, status, PSComputername, Runspaceid | Set-Content 'C:\TEMP\check.txt'
Just to make sure... you are looking for a service right? Not just a process? Because if it isn't a service you would need to use Get-Process instead of Get-Service.
If you want to output the data to the remote server you could do:
$output = foreach ($Server in $Servers)
{
$Session = New-PSSession -ComputerName $Server -Credential $MyCredentials
Invoke-Command -Session $Session -ScriptBlock
{
Get-Service -Name service | Tee-Object -FilePath C:\Temp\ServiceState.txt
} | select name, status, PSComputername, Runspaceid
}
$output | Out-File -filepath 'c:\TEMP\check.txt'
That should make a file in the C:\Temp folder on each server with the state of the service, as well as pass the information back to the local host, where it is passed to Select and stored in $output. At the end I output $output to file, just as you did.
I guess in the end you could just remove the Out-File call from within your loop, and it would probably do what you want it to.

If Else within an Invoke-Command Powershell

I am trying to run the below command to say if this registry key exists then Get-ItemProperty Else Do nothing or Display Text for Testing.
"SQL Server Product Name" = Invoke-Command -ComputerName $Computer -ScriptBlock {If (Test-Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names") { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object -FilterScript { (($_.Publisher -like "Microsoft*") -and ($_.DisplayName -like "Microsoft SQL Server*(*-bit)")) } | Select-Object -first 1 -ExpandProperty DisplayName } else {Write-Host "Blah"}}
The Else doesn't seem to do anything because right now if the reg key does not exist, it puts in {} to the results instead of Blah. I am not exactly sure if the If statement is working at all since I think it may just be running the Get-ItemProperty no matter what since if that path exists, I get the expected results.
The issue with your example is that the Else scriptblock uses Write-Host. When you execute this on a remote machine, the Host is a PowerShell session on that remote computer. You're writing the text to a session with no GUI on a remote machine.
To fix this, just remove the Write-Host cmdlet. The quoted text will be passed back to your local session along with anything else output by the scriptblock when it is executed on the remote session.