Need some help adjusting this code so that it will only provide IP addresses that are not pinging and will be exported to a CSV. Currently, it outputs both up and down IP's and I want to clean up some of the clutter.
$ping = (Test-Connection -ComputerName $comp.IPAddress -Count 1 -ErrorAction SilentlyContinue)
if($ping)
{
$status = "Up"
}
else
{
$status = "Down"
}
[pscustomobject]#{
Location = $comp.Location
IP = $comp.IPAddress
Status = $status
}
I have tried manipulating the $status variable but haven't had any luck. I am sure it's something very simple, I am just missing it.
# -Quiet means we just return a boolean true / false value
$ping = Test-Connection -ComputerName ($comp.IPAddress) -Count 1 -ErrorAction SilentlyContinue -Quiet
if(-not $ping)
{
# we only care about when it's down, so move the logic to return our object here
[pscustomobject]#{
Location = $comp.Location
IP = $comp.IPAddress
Status = 'Down'
}
}
Aramus, Welcome to SO!
Test-Connection returns a Null if the connection is down so...
$ping = (Test-Connection -ComputerName $($comp.IPAddress) -Count 1 -ErrorAction SilentlyContinue)
if($Null -ne $ping)
{
$status = "Up"
}
else
{
$status = "Down"
}
Also note the $() around $Comp.IPAddress, this is to insure the parser fetches the IPAddress before running the rest of the command.
Related
I'm looking for the easiest way to write the success or failure of a scheduled script to a csv file. I am not looking for a way to log the error or reason of failure just a simple "Succes/Failure". For this reason i'm not trying to fix/log non-terminating errors.
I thought the easiest way to put every script in a
try {
Write-Host "test"
}
catch{
Code to write to csv here
}
Block where I write to a csv file in the Catch part. Is there a better/easier way to do this or is this the right way to go?
Continuing from my comment. . .
Honestly, this really depends on the situation, i.e. what is you're trying to accomplish. So, let's make up a scenario of querying a computer for some basic info:
Function Get-SystemInfo {
Param (
[Parameter(Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerName = $env:COMPUTERNAME
)
Begin {
$ErrorActionPreference = 'SilentlyContinue'
}
Process {
foreach ($Computer in $ComputerName)
{
try {
# attempt cim session with -EA Stop to not allow it to go
# any further and we can calculate the results.
$CIMSession = New-CimSession -ComputerName $Computer -ErrorAction Stop
$Status = $true
# Perform the actual queries
$CS = Get-CimInstance -ClassName Win32_COmputerSystem -CimSession $CIMSession
$BS = Get-CimInstance -ClassName Win32_BIOS -CimSession $CIMSession
# evaluate against the returned objects
$UserName = if ($CS.UserName -eq $null) { 'No User Logged in' } else { $CS.UserName }
$Manufacturer = if ($CS.Manufacturer -eq $null) { 'N/a' } else { $CS.Manufacturer }
$Model = if ($CS.Model -eq $null) { 'N/a' } else { $CS.Model }
$SerialNumber = if ($BS.SerialNumber -eq $null) { 'N/a' } else { $BS.SerialNumber }
}
catch {
# Set the variables to $null
$Status = $false
$UserName = $null
$Manufacturer = $null
$Model = $null
$SerialNumber = $null
}
finally {
# Output the filled variables
[PSCustomObject] #{
ComputerName = $Computer
Connected = $Status
UserLoggedIn = $UserName
Manufacturer = $Manufacturer
Model = $Model
SerialNumber = $SerialNumber
}
}
}
}
End {
# cleanup
# some people argue this should be in the finally block
# to disconnect any machine, but looking at this from both
# sides, they both have pros/cons.
Get-CimSession | Remove-CimSession
}
}
... the biggest take-away from this quick function, is the -ErrorAction Stop while trying to create a CIM session. This is where looking at the bigger picture comes into play. If you are unable to connect to the computer, why bother continuing? This includes getting an echo reply from a quick ping since that doesn't dictate that you can connect to the remote PC just because you got a reply.
The rest is the if, and else statements that handle the light work evaluating against the returned objects for more control over the output.
Results would be:
PS C:\Users\Abraham> Get-SystemInfo
ComputerName : OER
Connected : True
UserLoggedIn : Abraham
Manufacturer : LENOVO
Model : 22251
SerialNumber : 55555555
PS C:\Users\Abraham> Get-SystemInfo -ComputerName BogusComputerName
ComputerName : BogusComputerName
Connected : False
UserLoggedIn :
Manufacturer :
Model :
SerialNumber :
I have the following PS script that repeats in the two blocks the same amount of registry keys. So I have two questions:
How can I avoid to repeat that long list of reg keys? I've tried adding them to a variable that is assigned #" "#, but when I use the variable instead of the reg keys it does not work
The $faultReport does not get the addition when into the Catch block, how to fix that?
$faultReport = #()
if (Test-Connection $2FQDN -Quiet -count 2) {
Try {
Invoke-Command -ComputerName $1FQDN -ScriptBlock {
Loads of registry keys
}
}
Catch {
$faultReport += $1FQDN}
}
elseif (Test-Connection $2FQDN -Quiet -count 2) {
Try {
Invoke-Command -ComputerName $2FQDN -ScriptBlock {
Loads of registry keys
}
}
Catch {
$faultReport += $2FQDN
}
}
It is possible to assign a scriptblock to a variable
$scriptBlock = { ... }
Also you have an error in your code: You check the connection for $2FQDN (instead of $1FQDN) in your first if.
Additionally, you could simplify your code further: both bodies of the if-else are identical.
My suggestion:
$scriptBlock = {
# Loads of registry keys...
}
$faultReport = #()
$computerName = $null
if (Test-Connection $1FQDN -Quiet -count 2) {
$computerName = $1FQDN
}
elseif (Test-Connection $2FQDN -Quiet -count 2) {
$computerName = $2FQDN
}
if ($computerName) {
try {
Invoke-Command -ComputerName $computerName -ScriptBlock $scriptBlock
}
Catch {
$faultReport += $computerName
}
}
Note: Always use proper indentation. I can never stress that enough. It makes reading and troubleshooting your own code so much easier.
So I am trying to Purge IPs from tool that we use. Before I can purge the assets we need to make sure the host is not pingable and not in DNS. I am new to PS and cant seem to wrap my head around on doing this. Any help is greatly appreciated. I have been doing this as a manual process by pinging the list of IPs and hostnames and doing a nslookup in cmd prompt before selecting the IPs that are needing to be removed. I have about 13k IPs left to do.
Update:
I want to implement this portion into it. Where In cell A it will have the IP
cell B will let me know if it is up or down. And Cell D will let me know if it is DNS aswell. Below is the script I got for pinging and checking if up or down and checking AD to see if the hostname is still in AD. I want it similar to this.. Please excuse my english
$path = ".\results.xls"
$objExcel = new-object -comobject excel.application
if (Test-Path $path)
{
$objWorkbook = $objExcel.WorkBooks.Open($path)
$objWorksheet = $objWorkbook.Worksheets.Item(1)
}
else {
$objWorkbook = $objExcel.Workbooks.Add()
$objWorksheet = $objWorkbook.Worksheets.Item(1)
}
$objExcel.Visible = $True
#########Add Header####
$objWorksheet.Cells.Item(1, 1) = "HostName"
$objWorksheet.Cells.Item(1, 2) = "Result"
$objWorksheet.Cells.Item(1, 3) = "MachineIP"
$objWorksheet.Cells.Item(1, 4) = "Active Directory"
$machines = Get-Content .\machinelist.txt
$row=2
$machines | foreach-object{
$ping = $null
$iname = $null
$machine = $_
$ping = Test-Connection $machine -Count 1 -ea silentlycontinue
$checkAD = try {$comp = Get-ADComputer -Identity $machine -ErrorAction
Stop
if ($comp){"Yes"}else{throw}
}
catch {"No"}
$objWorksheet.Cells.Item($row,4) = $checkAD
if($ping){
$objWorksheet.Cells.Item($row,1) = $machine
$objWorksheet.Cells.Item($row,2) = "UP"
$iname = $ping.IPV4Address.IPAddressToString
$objWorksheet.Cells.Item($row,3) = $iname
$row++}
else {
$objWorksheet.Cells.Item($row,1) = $machine
$objWorksheet.Cells.Item($row,2) = "DOWN"
$row++}
}
Here's a start, using Test-Connection and Resolve-DnsName, assuming you have a list of hosts' IP and name separated by a space, with a carriage return between each host:
$Assets = #"
127.0.0.1 localhost
255.255.255.255 fakehost
"#
$FailedPing = #()
$NoDNS = #()
$Assets -split "`r" | %{
$ipaddress = ($_.split(' ')[0]).trim()
$hostName = ($_.split(' ')[1]).trim()
Write-Host "IP: $ipaddress HOST: $hostName "
Try {
Test-Connection -ComputerName $ipaddress -Count 2 -ErrorAction Stop | Out-Null
}Catch {
$FailedPing += [pscustomobject]#{IP=$ipaddress; HostName = $hostName}
}
Try {
Resolve-DnsName -Name $hostName -ErrorAction Stop | Out-Null
}Catch {
$NoDNS += [pscustomobject]#{HostName = $hostName;Problem = $Error[0].Exception}
}
}
$FailedPing | ft
$NoDNS | ft
Right now the below script imports info from a CSV file then outputs in a CSV file the computer name, expectedIP, Status, GoodIP, dnsName. This is all through custom PS object.
I am currently trying to get an output known as actual IP. I am not quite sure how to pull the IP via the ping function that pings the host name. For instance if the hostname that is being pinged check the IP and returns true or false. How could it output what the actual IP of the server is instead of outputting true or false?
Here is the script:
$compinfo = import-csv .\compinfo.csv
$lookupData = foreach ($comp in $cominfo) {
$nslkup = [System.Net.DNS]::GetHostEntry($comp.hname)
$ping = (Test-Connection -ComputerName $comp.hname -Count 1 -ErrorAction SilentlyContinue)
if ($ping) {
$status = "up"
} else {
$status = "down"
}
if ($nslkup.AddressList.IPAddressToString -eq $comp.ip) {
$ipgood = $true
} else {
$ipgood = $false
}
[PSCustomObject]#{
computerName = $comp.hname
expectedIp = $comp.ip
status = $status
goodIp = $ipgood
dnsName = $nslkup.hostname
}
}
$lookupData | Export-Csv .\lookups.csv -NoTypeInformation
I appreciate everyone that commented on this. I figured it out. Here is the correct code for anyone else that has this issue in the future.
$compinfo = import-csv .\compinfo.csv
$lookupData = foreach($comp in $compinfo)
{
$nslkup = [System.Net.DNS]::GetHostEntry($comp.hname)
$ping = (Test-Connection -ComputerName $comp.hname -Count 1 -ErrorAction SilentlyContinue)
if($ping)
{
$status = "up"
}
else
{
$status = "down"
}
if($nslkup.AddressList.IPAddressToString -eq $comp.ip)
{
$ipgood = $true
}
else
{
$ipgood = $nslkup.AddressList.IPAddressToString
}
[pscustomobject]#{
computerName = $comp.hname
expectedIp = $comp.ip
status = $status
goodIp = $ipgood
dnsName = $nslkup.hostname
}
}
$lookupData | export-csv .\lookups.csv -NoTypeInformation
Notice I replaced $False with $nslkup.AddressList.IPAddressToString
Hello Guys im having trouble trying to figure out how to make this script to work, im very new on scripting but i do understand most of it but still figuring out some things.
try {
Test-Connection -Computername $_ -count 1 -ErrorAction Stop
} catch {
$_.Exception.ErrorCode -eq 0x800706ba
} `
{
$err = 'Unavailable (Host Offline or Firewall)'
}
try {
Test-UserCredentials -Username testuser -Password (Read-Host -AsSecureString)
} catch {
$_.CategoryInfo.Reason -eq 'UnauthorizedAccessException'
} `
{
$err = 'Access denied (Check User Permissions)'
}
Write-Warning "$computer- $err" | Out-File -FilePath c:\temp\Folder\Errors.txt -Append
What im looking for is for this script to test if the system responds or not. If True then next step would be to test credentials, and last would be to perform a get-wmiobject query. But if the system does not respond to ping then i want to catch the hostname that failed to respond ping, capture it and export it to a txt and do the same if the credential fails.
try..catch is for handling terminating errors. Don't abuse it for status checks by forcing a check to fail hard when it doesn't need to. If you just want to test the availability of a system run Test-Connection with the parameter -Quiet as an if condition:
if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
...
}
If you need to cascade multiple checks you could do so in a more readable manner by inverting the checks and returning with an appropriate message:
function Test-Multiple {
...
if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
return "Host $_ unavailable."
}
$pw = Read-Host -AsSecureString
if (-not (Test-UserCredentials -Username testuser -Password $pw)) {
return 'Login failed for user testuser.'
}
...
}
If you want the information about ping or login failures in log files you can just append it to the respective files:
function Test-Multiple {
...
if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
$_ | Add-Content 'C:\path\to\unavailable.log'
return
}
$pw = Read-Host -AsSecureString
if (-not (Test-UserCredentials -Username testuser -Password $pw)) {
$_ | Add-Content 'C:\path\to\login_failure.log'
return
}
...
}
Personally, I can't stand the behavior of Test-Connection. Throwing an exception when it doesn't successfully ping isn't the behavior I want. Like, ever. I understand why they did it that way, but it's not how I ever want a ping to work. Test-Path doesn't throw an exception when the path is invalid. It just returns false. Why is Test-Connection so unfriendly?
WMI allows you to capture the actual status code, and it also allows you to easily control the timeout so it will function much more quickly.
I tend to use this:
$Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000";
if ($Ping.StatusCode -eq 0) {
# Success
}
else {
# Failure
}
If I actually want to decode the ping status code:
$StatusCodes = #{
[uint32]0 = 'Success';
[uint32]11001 = 'Buffer Too Small';
[uint32]11002 = 'Destination Net Unreachable';
[uint32]11003 = 'Destination Host Unreachable';
[uint32]11004 = 'Destination Protocol Unreachable';
[uint32]11005 = 'Destination Port Unreachable';
[uint32]11006 = 'No Resources';
[uint32]11007 = 'Bad Option';
[uint32]11008 = 'Hardware Error';
[uint32]11009 = 'Packet Too Big';
[uint32]11010 = 'Request Timed Out';
[uint32]11011 = 'Bad Request';
[uint32]11012 = 'Bad Route';
[uint32]11013 = 'TimeToLive Expired Transit';
[uint32]11014 = 'TimeToLive Expired Reassembly';
[uint32]11015 = 'Parameter Problem';
[uint32]11016 = 'Source Quench';
[uint32]11017 = 'Option Too Big';
[uint32]11018 = 'Bad Destination';
[uint32]11032 = 'Negotiating IPSEC';
[uint32]11050 = 'General Failure'
};
$Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000"
$StatusCodes[$Ping.StatusCode];
You could do it like this:
if(Test-Connection -Computername $_ -Count 2 -ErrorAction 0 -Quiet) {
if(-not (Test-UserCredentials -Username testuser -Password (Read-Host -AsSecureString))) {
$err = "Access denied (Check User Permissions)"
}
} else {
$err = "Unavailable (Host Offline or Firewall)"
}
if($err) {
Write-Warning "$computer - $err" | Out-File -FilePath c:\temp\Folder\Errors.txt -Append
}
I believe Test-Connection and Test-Credentials are meant to return $true or $false rather than an exception (if properly used), so you don't really need try/catch here.