Powershell script error processing CSV data - powershell

I'm writing a script to load computer names from a CSV file, then look up their IP addresses.
The script creates an error when using the name from the CSV.
If I run the script in ISE, the error shows up, but the result still comes though. If I run the script from powershell, it errors and the result is null.
If I substitute the $PCname = $_.System for $PCname = "Computer01" everything works fine.
If I write-host $_.System it displays "Computer01". How can I get this to work in powershell?
$file = "\\server.contoso.net\private$\Systems.csv";
$CSV = Import-CSV $file;
$CSV | %{
if ($_.Skip -eq 0)
{
$PCname = $_.System
# $PCname = "Computer01"
write-host $PCname
try
{
$ipaddress = [System.Net.Dns]::GetHostByName($PCname).AddressList[0].IpAddressToString
}
Catch [system.exception]
{
if(1)
{ $error[0].tostring() }
}
}
}
Error displayed is:
Exception calling "GetHostByName" with "1" argument(s): "The requested name is valid, but no data of the requested type was found"

Turns out that the values in the CSV at some point had whitespace added after them, which caused a name look up error. I'm not sure why ISE would still be able to look up the host, but removing the whitespace fixed the issue.
Thanks to sha, his recommendation helped me see the whitespace.

Related

Powershell command did not include error in output file

I have the code below, to be used in Powershell; it performed well, except that I need the output file to also include the error messages whenever the IPs did not resolve to names.
Get-Content inputfile.txt |
foreach-object { [System.Net.Dns]::GetHostEntry($_) } |
out-file -filepath outputfile.txt
At the moment, I'm able to see the red error messages displayed on Powershell window. But I want these to appear in the output file along with the results for each item listed in the input file.
Thanks in advance!
Since .GetHostEntry(..) doesn't give you a clear hint as to which IP failed to be resolved it's better if you create an object that associates the IP Address you're trying to resolve with the method call. This also allows you to have a better export type, instead of plain .txt file, you can export your objects as .csv with Export-Csv.
Below example uses .GetHostEntryAsync(..) which allow us to query multiple hosts in parallel!
using namespace System.Collections.Generic
using namespace System.Collections.Specialized
(Get-Content inputfile.txt).ForEach{
begin { $tasks = [List[OrderedDictionary]]::new() }
process {
$tasks.Add([ordered]#{
Input = $_
Hostname = [System.Net.Dns]::GetHostEntryAsync($_)
})
}
end {
do {
$id = [System.Threading.Tasks.Task]::WaitAny($tasks.Hostname, 200)
if($id -eq -1) { continue }
$thisTask = $tasks[$id]
$thisTask['Hostname'] = try {
$thisTask.Hostname.GetAwaiter().GetResult().HostName
}
catch { $_.Exception.Message }
$tasks.RemoveAt($id)
[pscustomobject] $thisTask
} while($tasks)
}
} | Export-Csv outputfile.csv -NoTypeInformation

Output of Get-content as variable?

I am attempting to run a foreach loop on a get-content and convertfrom-json cmd. Now im aware this potentially has issues being multiple value results in the variable, im wondering how i can continue to pass this info to the rest of the script.
$testconv = Get-device * |select ID
$testid = $testconv.id
$conv = foreach ($id in $testid)
{
get-content "\\HDC-PRTG-03\System Information Database\Services\Device$id.Services" | Convertfrom-json
}
$rpccheck =$conv.message
$snmpcheck = $conv.message
$svcname = $conv.data.displayname
$svcstate=$conv.data.properties.state
if($RPCon = $rpccheck |select-string -pattern RPC -AllMatches){
write-host RPC Not enabled
}else{
write-host No RPC Enabled - Moving to Services List
Now when i run that with out the $conv= making it a variable it returns
kind : Services
recievetime : 29-01-2018 14:43:32
error : 106
Message : SNMP Channels Not Available.
Which is what i expect. However when i define it a variable with $conv= it just starts to say it cannot find the file paths which i find an odd error to throw but hey ho.
Do any of you smart guys have any pointers for how i can keep these fromjson objects in memory so i can continue to run foreach loops against them. The ultiumate function of this script is to query a local .services file for what services are running on the device and then create sensors to monitor them within our PRTG installation. Therefore i need to be able to ref the deviceID and apply things to it.
I suspect i may be using too many foreach loops in the whole script but frankly i am 100% out of my depth
any guidance hugely hugely appreciated
Sam
If i understand correctly you should have json files for all device ID's. If a file with the name of a particular device is missing you will get the 'File not found' error.
As for the code, you can try this:
$testconv = Get-Device * | select ID
$testid = $testconv.id
$oldErrorAction = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
foreach ($id in $testid) {
try {
$conv = Get-Content -Path "\\HDC-PRTG-03\System Information Database\Services\Device$id.Services" -Raw | ConvertFrom-Json
$rpccheck = $conv.message # These look the same to me...
$snmpcheck = $conv.message # These look the same to me...
$svcname = $conv.data.displayname
$svcstate = $conv.data.properties.state
$Matches = ($rpccheck | Select-String -Pattern "RPC*" -AllMatches)
if ($Matches.Matches.Count) {
Write-Host "RPC Not enabled"
}
else {
Write-Host "No RPC Enabled - Moving to Services List "
}
}
catch {
Write-Warning $_.Exception.Message
}
}
$ErrorActionPreference = $oldErrorAction
Instead of the try{}..catch{} you could also first test if a file with that name is present using Test-Path directly before doing the Get-Content.

DNS name from IP address

I need to create a list of IP addresses and DNS names. I
am trying to get DNS names from IP addresses. I have tried two ways:
try/catch but it ends afterwards.
Without and it just outputs DNS names that I can't relate to the IP addresses.
Here's what I have so far:
#try {
Get-Content C:\Users\pintose\Documents\IP_Address.txt | ForEach-Object
{([system.net.dns]::GetHostByAddress($_)).hostname >> C:\Users\user\Documents\hostname.txt}
# }
# catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt }
# }
Try this solution:
$outFile = "C:\Users\user\Documents\hostname.txt"
Get-Content C:\Users\pintose\Documents\IP_Address.txt | ForEach-Object {
$hash = #{ IPAddress = $_
hostname = "n/a"
}
$hash.hostname = ([system.net.dns]::GetHostByAddress($_)).hostname
$object = New-Object psobject -Property $hash
Export-CSV -InputObject $object -Path $outFile -Append -NoTypeInformation
}
We create a objects, that have the IPaddress in it and a hostname n/a if it cannot be resolved. Then, the object gets exported into the file. You'll get something like:
192.0.0.1; Server1
This uses a workflow so it can do parallel foreach
Workflow Get-DNSNames([string[]]$IPAddresses){
foreach -parallel ($IP in $IPAddresses){
try{
#{$IP = $(([system.net.dns]::GetHostByAddress($IP)).hostname)}
}catch{
#{$IP = "N/A"}
}
}
}
$List = Get-DNSNames -IPAddresses $(Get-Content "C:\IPAddresses.txt").Split("[\r\n]")
$List | Out-File "C:\IPAddresses_Complete.txt"
You might want to try the other solutions offered here, but here are some things you might want to think about.
First, I'd recommend not putting the try{}catch{} around the whole of the first command. If you are looping through data and just one of them causes an exception, you risk not completing the task. Put the try{}catch{} around just the "risky" line of code:
Get-Content C:\Users\pintose\Documents\IP_Address.txt | Foreach-Object {
try {
([system.net.dns]::GetHostByAddress($_)).hostname >> C:\Users\user\Documents\hostname.txt
}
catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt
}
}
}
When you catch the exception, you only write to the text file in the case that "the requested name is valid" (do you mean invalid?). You never write anything to the file otherwise. Thus, going back to your original code:
IF there is an exception caused by ANY of the IP addresses
AND the exception is NOT "the requested name is valid" (which I think might be a typo?)
THEN no error gets written to the file and the script ends without necessarily completing all the IP addresses.
Other things:
You use two methods to write to the file: >> and Out-File. Probably better to use the PowerShell cmdlet but with the -Append switch to ensure you append to the end of the file:
([system.net.dns]::GetHostByAddress($_)).hostname | Out-File C:\Users\user\Documents\hostname.txt -Append
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt -Append
#restless1987 has suggested a way to ensure you write both the IP address and the hostname (if determined) to the output file. I'd have a look at that to work out what is going on.
My final tip would be to be wary of reading in from .txt files with Get-Content. They often have trailing (blank) lines and you might want to try to ignore such blanks. Probably not a big issue in this case as it will just mean a failed DNS attempt, but I have seen such things wreak havoc on every mailbox in a (very) large company when used with other commands.
Another way...
$ip_list = Get-Content ./IP_Address.txt
foreach ($ip in $ip_list) {
try {
([system.net.dns]::GetHostByAddress($ip)).hostname |
Out-File -FilePath ./hostname.txt -Append
}
catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File -FilePath './hostname.txt' -Append }
}
}
There are many tools that can accomplish this, but if you need a quick and dirty solution that you can run just about anywhere this will get the job done.
Using the eternally useful ps tools such as psloggedon /accepteula \\computername or ip address you can get who is currently logged in to check if this is the correct machine. For example:
c:\pstools>psloggedon /accepteula \\10.0.0.10
loggedon v1.33 - See who's logged on
Copyright ⌐ 2000-2006 Mark Russinovich
Sysinternals - www.sysinternals.com
Users logged on locally:
Error: could not retrieve logon time
NT AUTHORITY\LOCAL SERVICE
Error: could not retrieve logon time
NT AUTHORITY\NETWORK SERVICE
1/12/2015 8:06:51 AM DOMAIN\user
Error: could not retrieve logon time
NT AUTHORITY\SYSTEM
Users logged on via resource shares:
1/17/2015 2:26:43 PM DOMAIN\user
Now that you have confirmed that this IP address is the correct one for this user. We just need to lookup the IP address using nslookup.
C:\>nslookup 10.0.0.10
Server: server.domain.com
Address: 10.10.0.1
Name: Workstation07.server.domain.com
Address: 10.0.0.10
Now we know that the computer name for that computer is Workstation07.

Catching error in Powershell and rewriting output

I'm trying to craft a script to resolve a long list of domain names to IP addresses. Some of these aren't defined and I need to catch the error and just return a "blank value." In the script below, I tried doing this using a basic If/Then, but I still get a wordy error (at the bottom) rather than just a blank value. Any ideas how to get around this? I really appreciate it!
----- SCRIPT -----
$names = Get-Content C:\temp\names.txt
ForEach ($name in $names) {
$ipAddress = [System.Net.Dns]::GetHostAddresses("$name")[0].IPAddressToString;
if ($ipAddress) {
Write-Host $name"-"$ipAddress
}
else {
Write-Host $name"-"
}
}
---- OUTPUT/ERROR ----
mydomain.com-1.2.3.4
yourdomain.com-4.3.2.1
Exception calling "GetHostAddresses" with "1" argument(s): "The requested name is valid, but no data of the requested type was found"
anotherdomain.com-5.5.5.5
---- What I'd Like to See -----
mydomain.com-1.2.3.4
yourdomain.com-4.3.2.1
NOTDEFINEDDOMAIN.tld-
anotherdomain.com-5.5.5.5
---- HERE'S THE SOLUTION THAT WORKED - THANK YOU!----
$names = Get-Content C:\temp\names.txt
ForEach ($name in $names) {
Try {
$ipAddress = [System.Net.Dns]::GetHostAddresses("$name")[0].IPAddressToString;
Write-Host $name"-"$ipAddress
}
Catch {
Write-Host $name"-"
}
}
Update of answer:
Catching error in Powershell and rewriting output
I need to catch the error and just return a "blank value
Use try/catch:
$names = Get-Content C:\temp\names.txt
ForEach ($name in $names)
{
try
{
$ipAddress = [System.Net.Dns]::GetHostAddresses("$name")[0].IPAddressToString;
Write-Host $name"-"$ipAddress
}
catch
{
Write-Host $name"-"
$_.Exception.Message # <- Check this to read and rewrite exception message
}
}
---- What I'd Like to See -----
If you want - you can manipulate of exception message like as string - this is line to get message in catch block:
$_.Exception.Message
Other way to get info about errors is $Error variable (it's the array/list of errors)...
More information:
http://vwiki.co.uk/Exceptions_and_Error_Handling_(PowerShell)
PowerShell Tutorial – Try Catch Finally and error handling in PowerShell
Using PowerShell $Error variable
about_Try_Catch_Finally
Update 2:
I forgot about one thing - try/catch working only with terminating errors.
I'm not sure about type of error in your case (because can't reproduce it), however sometimes you may want to add to your command:
-Error Stop

powershell Import-csv pop up

so i am working with a bit of code that after setting a static location for the CSV i am using for import. When i run the code it seems to run until i get the Windows can't open this File pop. you know the one with what do you want to do options, like user the web services to find the correct program. i am copying the code so hopefully someone can point on where i made this fubar. just for note before i made the CSV static the Script asked you to type the location in every time so maybe i missed a setting there
if ($args[0] -eq $null)
{
$userNameFile = D:\lync_creation\userlog.csv
$userNameFile = $usernamefile -replace '"',""}
else
{$usernamefile = $args[0]}
if ($userNameFile -ne "")
{$csv=import-csv $userNameFile}
else
{"Could not find a valid .csv with the user information."
exit}
foreach($c in $csv)
# enable for lync
{
"Enabling " + $c.Identity + " for Lync 2010"
Enable-csuser -identity $c.Identity -registrarpool pool01.west.com –sipaddresstype EmailAddress
}
write-host "Press any key to continue..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,AllowCtrlC")
You are seriously over complicating things if you're just going to use a static location for your CSV file.
$csv = Import-CSV D:\lync_creation\userlog.csv
foreach($c in $csv){
"Enabling $($c.Identity) for Lync 2010"
Enable-csuser -identity $c.Identity -registrarpool pool01.west.com –sipaddresstype EmailAddress
}
write-host "Press any key to continue..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,AllowCtrlC")
The first line imports the CSV file into a variable.
The next 4 loops through all entries in that variable, write the host who it's enabling and then enables the person.
The last 2 lines give a Press any key to continue message and then waits for a key press before continuing.