Powershell Script- Pipe to export CSV for Reverse DNS fails. - powershell

I'm trying to write a script that will take input from a .txt file of IP address. Do a reverse DNS lookup to get the hostname of the IP address and then export that data into .csv file.
Here is what I have so far.
# getting the IP's from the file
$IPADDR = Get-Content "C:\Users\douglasfrancis\Desktop\IP_Test.txt"
ForEach ($IPADDR in $IPADDR)
{
[System.Net.DNS]::GetHostbyAddress($IPADDR) | Add-Member -Name IP -Value $IPADDR - MemberType NoteProperty -PassThru | select IP, HostName | sort -property Hostname | export- csv "C:\Users\douglasfrancis\Desktop\ReverseLookup.csv"
}
How it is now the created CSV file will have the column heads that I assigned and the last IP address in the list with its hostname. So somehow its dropping everything else.
If I run it with the export-csv commented out then all the IP's are displayed in the console but are not sorted by hostname.
I've used this same basic pipe before with no issues so I'm kinda at a loss for what is going on here. Any help would be awesome.
Thanks,

You should put the result in an array before exporting it to CSV :
# getting the IP's from the file
$IPADDRS = Get-Content "C:\Users\douglasfrancis\Desktop\IP_Test.txt"
$result = #()
ForEach ($IPADDR in $IPADDRS)
{
$result += [System.Net.DNS]::GetHostbyAddress($IPADDR) | Add-Member -Name IP -Value $IPADDR -MemberType NoteProperty -PassThru | select IP, HostName
}
$result | sort -property Hostname | export-csv "C:\Users\douglasfrancis\Desktop\ReverseLookup.csv" -NoTypeInformation
#David Braban is true, this was not your trouble but I use $IPADDRS for the collection and $IPADDR for each value, this way it's much correct (en readable)/

In your code, I see:
ForEach ($IPADDR in $IPADDR)
That's very wrong: you use the same variable both as your collection and as the variable to iterate.
Use:
$ipaddr | %{ [System.Net.DNS]::GetHostbyAddress($_) } | ...

Related

PowerShell: Two A records are shown for each SRV record - when there should only be one

Have an odd situation where the PowerShell script below is showing two A records for each SRV record - when there should only be one A record per SRV record. Sample output:
If you're running Active Directory in your environment, simply run the PowerShell script below and you should get the same results. The output file will be on your Desktop. I've re-written this script multiple times and after many trials and errors, still getting the same results. Any idea why the A record is listed twice, and how to get it to show only once?
# Show Active Directory SRV records by Site
$logpath = "$ENV:UserProfile\Desktop\"
####################################################
#find date and convert to string
$date=((Get-Date).ToString('yyyy-MM-dd-HH-mm-ss'))
#defines the path where to find the log file of this script
$Logfile = $logpath+$date+".log"
#convert date and time to string
$date=((Get-Date).ToString('yyyy-MM-dd-HH-mm-ss'))
#get addomain
$domain = ((Get-WmiObject Win32_ComputerSystem).Domain)
#get all sites
$sites = (([System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites).Name)
Function Write-Log
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
Write-Log "Domainname: $domain"
Write-Log "-------------"
foreach ($site in $sites){
$srv = (Resolve-DnsName -Type SRV _ldap._tcp.$site._sites.$domain | ft Name,Type,NameTarget,Port | Out-String)
Write-Log "Sitename: $site"
Write-Log "-------------"
Write-Log "$srv"
}
There might be some differences between the two A-Records, but you're missing them due to this lines here.
$srv = (Resolve-DnsName -Type SRV _ldap._tcp.$site._sites.$domain |
ft Name,Type,NameTarget,Port | Out-String)
The ft command here is an alias for Format-Table, and the default parameter is a list of which Columns to select. In that selection, you're only keeping these fields:
Name
Type
NameTarget
Port
Now, there might be a lot of other fields we're not seeing, and I expect one of them contains some unique different value. So I would suggest you run this to see how it's different. (Maybe this device has two network interfaces and they both have different IPs, and thus both get their own set of DNS settings back from DHCP)
Resolve-DnsName -Type SRV _ldap._tcp.$site._sites.$domain | format-list
That will show you all the fields, and you can figure out why you have two records.
After some trial and error, this gives me exactly what I need. Change:
$srv = (Resolve-DnsName -Type SRV _ldap._tcp.$site._sites.$domain | ft Name,Type,NameTarget,Port | Out-String)
to:
$srv = (Resolve-DnsName -Type SRV _ldap._tcp.$site._sites.$domain | select-object -First 2 | ft Name,Type,NameTarget,Port | Out-String)
Explanantion: The use of select-object allows one to trim off rows from a result object array.

Power shell For Loop not Looping

So the output works fine but I'm having an issue with it only outputing the last line it runs. Is there anyway to check for loops to test in the future?
but i have a list of ip address and im trying to check if the firewall in windows is enabled or disabled.
They are on one LARGE (300+ workgroup). Any help in getting this to loop properly would be appreciated. Security and other things are not a concern cause i have other scripts that run fine. And i dont get any errors. just the single output.
ive already tried moving the array and that didn't help. im thinking it could be the PSCustomObject part as i'm just starting to learn these. Or could it be my input and output formats are different and that's causing issues??
clear
$ComputerList = get-content C:\Users\Administrator\Desktop\DavidsScripts\TurnOffFirewall\input.txt
$Status = #(
foreach ($Computer in $ComputerList) {
netsh -r $Computer advfirewall show currentprofile state})[3] -replace 'State' -replace '\s'
$Object = [PSCustomObject]#{
Computer = $Computer
Firewall = $Status
}
Write-Output $Object
$Object | Export-Csv -Path "C:\FirewallStatus.csv" -Append -NoTypeInformation
Your previous code was not escaping the loop and was only adding the last computer in the loop to the object.
The best way I have found, is to make a temp object and add it to an array list then export that. Much nicer.
$ComputerList = get-content C:\Users\Administrator\Desktop\DavidsScripts\TurnOffFirewall\input.txt
$collectionVariable = New-Object System.Collections.ArrayList
ForEach ($Computer in $ComputerList) {
# Create temp object
$temp = New-Object System.Object
# Add members to temp object
$temp | Add-Member -MemberType NoteProperty -Name "Computer" -Value $Computer
$temp | Add-Member -MemberType NoteProperty -Name "Firewall" -Value $((netsh -r $Computer advfirewall show currentprofile state)[3] -replace 'State' -replace '\s')
# Add the temp object to ArrayList
$collectionVariable.Add($temp)
}
Write-Output $collectionVariable
$collectionVariable | Export-Csv -Path "C:\FirewallStatus.csv" -Append -NoTypeInformation
Here's a streamlined, functional version of your code, using a single pipeline:
Get-Content C:\Users\Administrator\Desktop\DavidsScripts\TurnOffFirewall\input.txt |
ForEach-Object {
[pscustomobject] #{
Computer = $_
Firewall = (-split ((netsh -r $_ advfirewall show currentprofile state) -match '^State'))[-1]
}
} | Export-Csv -Path C:\FirewallStatus.csv -NoTypeInformation
Note:
No intermediate variables are needed; each computer name read from the input file is processed one by one, and each custom object constructed based on it is sent to the output CSV file.
The command for extracting the firewall status from netsh's output was made more robust in order to extract the state information based on the line content (regex ^State, i.e., a line starting with State) rather than a line index ([3]); the unary form of -split splits the line of interest into tokens by whitespace, and index [-1] extracts the last token, which is the state value.
As for what you tried:
Your foreach loop ended before $Object was constructed, so you ended up constructing just 1 object to send to the output file with Export-Csv.
If you had formatted your code properly, that fact would have been more obvious; try using Visual Studio Code with the PowerShell extension, which offers automatic formatting via the >Format Document (Shift+Alt+F) command.

Powershell script to return search results from a list of keywords

I have a project name called 'SFO104' and I have a list of serial numbers i.e 5011849, 5011850 etc and I have to search a long list of 500+ serial numbers to see if they exist in any other documents not relating to the project name SFO104 or the PO number 114786.
I was thinking of outputting the search results to a csv for each serial number searched but the below isnt working.
$searchWords = gc C:\Users\david.craven\Documents\list.txt
$results = #()
Foreach ($sw in $searchWords)
{
$files = gci -path C:\Users\david.craven\Dropbox\ -filter "*$sw*" -recurse | select FullName
foreach ($file in $files)
{
$object = New-Object System.Object
$object | Add-Member -Type NoteProperty –Name SearchWord –Value $sw
$object | Add-Member -Type NoteProperty –Name FoundFile –Value $file
$results += $object
}
}
$results | Export-Csv C:\Users\david.craven\Documents\results.csv -NoTypeInformation
The image below shows my search of the serial number 5011849 and the results returned correspond to project SFO104 which is as expected.
Your code works, the file is getting populated. However, what you have specified does not have the headers defined as in your screen shot. Also, what does that list.txt look like. My searchlist.txt is a single column file:
Hello
client
Using your code as is, only changing the file path and name, and a slight modification to where the filename is accessed, gives these results...
$searchWords = gc 'D:\Scripts\searchlist.txt'
$results = #()
Foreach ($sw in $searchWords)
{
$files = gci -path d:\temp -filter "*$sw*" -recurse
foreach ($file in $files)
{
$object = New-Object System.Object
$object | Add-Member -Type NoteProperty –Name SearchWord –Value $sw
$object | Add-Member -Type NoteProperty –Name FoundFile –Value $file.FullName
$results += $object
}
}
$results | Export-Csv d:\temp\searchresults.csv -NoTypeInformation
# Results
# psEdit -filenames 'd:\temp\searchresults.csv'
SearchWord FoundFile
---------- ---------
Hello D:\temp\Duplicates\PowerShellOutput.txt
Hello D:\temp\Duplicates\BeforeRename1\PowerShellOutput.txt
Hello D:\temp\Duplicates\PoSH\PowerShellOutput.txt
Hello D:\temp\Duplicates\Text\PowerShellOutput.txt
client D:\temp\Client.txt
client D:\temp\Duplicates\CertLabClients_v1.ps1
client D:\temp\Duplicates\Check Logon Server for Client.ps1
client D:\temp\Duplicates\Create Wireless Hosted Networks in Windows Clients.ps1
...
Update for OP
Since you are using a comma separate list. You need to break that into separate items. I changed my file to this
Hello,client
You cannot match on that layout unless you are trying to match the whole consecutive string. So, if I break the above this way ...
$searchWords = (gc 'D:\Scripts\searchlist.txt') -split ','
… thus the results are as shown before.
Update for the OP
Example, test with this (a different rough approach)...
Foreach ($sw in $searchWords)
{
Get-Childitem -Path "d:\temp" -Recurse -include "*.txt","*.csv" |
Select-String -Pattern "$sw" |
Select Path,LineNumber,#{n='SearchWord';e={$sw}}
}
The LineNumber was sonly added so show where the string was located. Also, note, your code, and what I provide here, will only work for text, csv files.
If you plan to hit these, doc, docx, xls, xlsx, that means way more code as you have to use the default apps Word, Excel, to open and read these files.
This means using the COM Object model for each of those file types in your code. As discussed and shown here:
How do I make powershell search a Word document for wildcards and return the word it found?
You'd need to do a similar thing for Excel or PowerPoint, and if you have PDF, that requires and addon.
Update for OP
Like I said, I put this together quickly so it is a bit rough (no error handling, etc...) by I did test it using my input file and target folder tree and it does work.
# This is what my input looks like
Hello,client
595959, 464646
LIC
Running the code should have given you the results below, using only .txt,.csv files. Using any other file type will error by design as per my comment above regarding, you cannot use this approach for non text-based files without using the native app for the non text file type.
$searchWords = ((gc 'D:\Scripts\searchlist.txt') -split ',').Trim()
Foreach ($sw in $searchWords)
{
Get-Childitem -Path "d:\temp" -Recurse -include "*.txt","*.csv" |
Select-String -Pattern "$sw" |
Select Path,LineNumber,#{n='SearchWord';e={$sw}}
}
Path LineNumber SearchWord
---- ---------- ----------
D:\temp\Duplicates\BeforeRename1\PsGet.txt 157 Hello
...
D:\temp\Duplicates\PoSH\PsGet.txt 157 Hello
...
D:\temp\Duplicates\BeforeRename1\PoSH-Get-Mo... 108 client
D:\temp\Duplicates\BeforeRename1\Powershell ... 12 client
D:\temp\Duplicates\BeforeRename1\Powershell ... 15 client
D:\temp\Duplicates\BeforeRename1\PsGet.txt 454 client
...
D:\temp\newfile.txt 4 client
D:\temp\MyFile.txt 5 595959
D:\temp\ProcessNames.csv 4 595959
D:\temp\Duplicates\Text\JSON-CSS.txt 30 464646
D:\temp\Duplicates\JSON-CSS.txt 30 464646
D:\temp\MyFile.txt 5 464646
D:\temp\ProcessNames.csv 4 464646
D:\temp\Duplicates\BeforeRename1\GetSetScree... 7 LIC

How to Get The Real RecordData from DnsServerResourceRecord Using Powershell?

I'm using PowerShell to extract information from an Active Directory DNS server and I'm having trouble getting to the data I want.
Specifically, I'm trying to get the names of hosts that belong to a particular subnet, 10.104.128.x.
When I use the following commands:
Get-DnsServerResourceRecord -ComputerName AD_DNS_SERVER -ZoneName 104.10.in-addr.arpa -RRType Ptr | Where-Object {$_.HostName -like '*.128'}`
I get output that looks like this:
HostName RecordType Timestamp TimeToLive RecordData
-------- ---------- --------- ---------- ----------
104.128 PTR 10/19/2015 3:00:0... 00:15:00 adl5c260a86ba79.XYZ.net.
11.128 PTR 12/29/2015 6:00:0... 00:15:00 adl3c970e8d7166.XYZ.net.
110.128 PTR 1/29/2012 11:00:0... 00:15:00 nroxitow7tst.ABC.com.
114.128 PTR 1/20/2012 7:00:00 AM 00:15:00 adl5c260a86c29e.ABC.com
What I really want are the first column, (HostName), which has the last two octets of the IP; and the fifth column, (RecordData), which has the name of the host the IP is assigned to.
The hostname is the data I really want/need. And I see it right there!
So I used the select command to pare down the output in the pipe train. New command looks like this:
Get-DnsServerResourceRecord -ComputerName AD_DNS_SERVER -ZoneName 104.10.in-addr.arpa -RRType Ptr | Where-Object {$_.HostName -like '*.128'} | select HostName, RecordData
But the output looks like this:
HostName RecordData
-------- ----------
104.128 DnsServerResourceRecordPtr
11.128 DnsServerResourceRecordPtr
110.128 DnsServerResourceRecordPtr
114.128 DnsServerResourceRecordPtr
Dosen't get me the hostname though. Just the type of object the RecordData is but not the data that the object contains, perhaps?
I also tried piping the output to CSV and got the same result.
Then I tried looking at the DnsServerResourceRecord object properties with Get-Member. That showed me the object had a property called PSComputerName. I thought maybe that would have the name of the host but that came up blank when I tried to select it.
I then Googled around a bit and found a few pages that recommended a few ways to use RecordData.ipv4address to coax the data out of the DnsServerResourceRecordPtr object but I haven't gotten any of them to work yet. Output still prints blanks.
So my question is: does a reliable method exist for getting the actual hostname from a PTR record?
To select the PtrDomainName property from the DnsServerResourceRecordPtr object, use a calculated property:
... |Select-Object HostName, #{Name='RecordData';Expression={$_.RecordData.PtrDomainName}}
Yes it's really weird that you can't just call ToString on the DNS record data, it's all formatted using the PowerShell formatters which you can only access with Format-List or Format-Table, rather than just calling $resourceRecord.RecordData.ToString().
I've added more data types than Krzysztof Madej by just hacking out the PowerShell formatters from the XML file, the details are here.
http://david-homer.blogspot.com/2020/10/getting-text-representation-of.html
$dnsserver = "yourowndnsserver"
$dnszones = Get-DnsServerZone -ComputerName $dnsserver | Select-Object ZoneName
ForEach ($zone in $dnszones) {
$data = New-Object System.Object
$ZoneName = $zone.ZoneName
$data = Get-DnsServerResourceRecord $ZoneName -ComputerName $dnsserver
foreach ($registros in $data) {
$data = $ZoneName
$data += ","
$data += $registros.hostname;
$data += ","
$data += $RecordType = $registros.recordType;
$data += ","
if ($RecordType -like "PTR") {
$data += $registros.RecordData.PtrDomainName
}
elseif ($RecordType -like "A") {
$data += $([system.version]($registros.RecordData.ipv4address.IPAddressToString));
}
elseif ($RecordType -like "CNAME") {
$data += $registros.RecordData.HostNameAlias;
}
elseif ($RecordType -like "NS") {
$data += $registros.RecordData.nameserver;
}
elseif ($RecordType -like "MX") {
$data += $registros.RecordData.MailExchange;
}
elseif ($RecordType -like "SOA") {
$data += $registros.RecordData.PrimaryServer;
}
elseif ($RecordType -like "SRV") {
$data += $registros.RecordData.DomainName;
}
$data | out-file -FilePath $env:TEMP\$(Get-Date -Format dd_MM_yyyy)_DNSRecords.csv -Append
}
}

How do you export objects with a varying amount of properties?

Warning - I've asked a similar question in the past but this is slightly different.
tl;dr; I want to export objects which have a varying number of properties. eg; object 1 may have 3 IP address and 2 NICs but object 2 has 7 IP addresses and 4 NICs (but not limited to this amount - it could be N properties).
I can happily capture and build objects that contain all the information I require. If I simply output my array to the console each object is shown with all its properties. If I want to out-file or export-csv I start hitting a problem surrounding the headings.
Previously JPBlanc recommended sorting the objects based on the amount of properties - ie, the object with the most properties would come first and hence the headings for the most amount of properties would be output.
Say I have built an object of servers which has varying properties based on IP addresses and NIC cards. For example;
ServerName: Mordor
IP1: 10.0.0.1
IP2: 10.0.0.2
NIC1: VMXNET
NIC2: Broadcom
ServerName: Rivendell
IP1: 10.1.1.1
IP2: 10.1.1.2
IP3: 10.1.1.3
IP4: 10.1.1.4
NIC1: VMXNET
Initially, if you were to export-csv an array of these objects the headers would be built upon the first object (aka, you would only get ServerName, IP1, IP2, NIC1 and NIC2) meaning for the second object you would lose any subsequent IPs (eg IP3 and IP4). To correct this, before an export I sort based on the number of IP properties - tada - the first object now has the most IPs in the array and hence none of the subsequent objects IPs are lost.
The downside is when you then have a second varying property - eg NICs. Once my sort is complete based on IP we then have the headings ServerName, IP1 - IP4 and NIC1. This means the subsequent object property of NIC2 is lost.
Is there a scalable way to ensure that you aren't losing data when exporting objects like this?
Try:
$o1 = New-Object psobject -Property #{
ServerName="Mordor"
IP1="10.0.0.1"
IP2="10.0.0.2"
NIC1="VMXNET"
NIC2="Broadcom"
}
$o2 = New-Object psobject -Property #{
ServerName="Rivendell"
IP1="10.1.1.1"
IP2="10.1.1.2"
IP3="10.1.1.3"
IP4="10.1.1.4"
NIC1="VMXNET"
}
$arr = #()
$arr += $o1
$arr += $o2
#Creating output
$prop = $arr | % { Get-Member -InputObject $_ -MemberType NoteProperty | Select -ExpandProperty Name } | Select -Unique | Sort-Object
$headers = #("ServerName")
$headers += $prop -notlike "ServerName"
$arr | ft -Property $headers
Output:
ServerName IP1 IP2 IP3 IP4 NIC1 NIC2
---------- --- --- --- --- ---- ----
Mordor 10.0.0.1 10.0.0.2 VMXNET Broadcom
Rivendell 10.1.1.1 10.1.1.2 10.1.1.3 10.1.1.4 VMXNET
If you know the types(NICS, IPS..), but not the count(ex. how many NICS) you could try:
#Creating output
$headers = $arr | % { Get-Member -InputObject $_ -MemberType NoteProperty | Select -ExpandProperty Name } | Select -Unique
$ipcount = ($headers -like "IP*").Count
$niccount = ($headers -like "NIC*").Count
$format = #("ServerName")
for ($i = 1; $i -le $ipcount; $i++) { $format += "IP$i" }
for ($i = 1; $i -le $niccount; $i++) { $format += "NIC$i" }
$arr | ft -Property $format
What about getting a list of all unique property headers and then doing a select on all the objects? When you do a select on an object for a nonexistent property it will create a blank one.
$allHeaders = $arrayOfObjects | % { Get-Member -inputobject $_ -membertype noteproperty | Select -expand Name } | Select -unique
$arrayOfObjects | Select $allHeaders
Granted you are looping through ever object to get the headers, so for a very large amount of objects it may take awhile.
Here's my attempt at a solution. I'm very tired now so hopefully it makes sense. Basically I'm calculating the largest amount of NIC and IP note properties, creating a place holder object that has those amounts of properties, adding it as the first item in a CSV, and then removing it from the CSV.
# Create example objects
$o1 = New-Object psobject -Property #{
ServerName="Mordor"
IP1="10.0.0.1"
IP2="10.0.0.2"
NIC1="VMXNET"
NIC2="Broadcom"
}
$o2 = New-Object psobject -Property #{
ServerName="Rivendell"
IP1="10.1.1.1"
IP2="10.1.1.2"
IP3="10.1.1.3"
IP4="10.1.1.4"
NIC1="VMXNET"
}
# Add to an array
$servers = #($o1, $o2)
# Calculate how many IP and NIC properties there are
$IPColSize = ($servers | Select IP* | %{($_ | gm -MemberType NoteProperty).Count} | Sort-Object -Descending)[0]
$NICColSize = ($servers | Select NIC* | %{($_ | gm -MemberType NoteProperty).Count} | Sort-Object -Descending)[0]
# Build a place holder object that will contain enough properties to cover all of the objects in the array.
$cmd = '$placeholder = "" | Select ServerName, {0}, {1}' -f (#(1..$IPColSize | %{"IP$_"}) -join ", "), (#(1..$NICColSize | %{"NIC$_"}) -join ", ")
Invoke-Expression $cmd
# Convert to CSV and remove the placeholder
$csv = $placeholder,$servers | %{$_ | Select *} | ConvertTo-Csv -NoTypeInformation
$csv | Select -First 1 -Last ($csv.Count-2) | ConvertFrom-Csv | Export-Csv Solution.csv -NoTypeInformation