Got a .ps where im getting alarmgroups from several files. Im trying to add them to an Object but the Problem is every new file hes adding another header into the Object. Is there a possibility, adding the header only 1 time. Append my data to the Object and when hes finished sorting the hole Object?
My Code.
$rootPath = $PSScriptRoot
if ($rootPath -eq "") {
$rootPath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
}
$alarmPath = "$rootPath\Alarmgroups"
$mdi_alarms_template = "$rootPath\tmpl\mdi-alarms.tmpl.html"
$mdi_alarms = "$rootPath\mdi-alarms.html"
$fileNames = Get-ChildItem -Path $alarmPath -Filter *.algrp
$AlarmgroupIndexString = $LanguageIDString = $tmpString = $ID_string = $html_output= ""
$MachineCode = "Out_2.Alarm.Current"
$BitNo = $Element = $Format2Element = $Format3BitID =0
$BitID = $Format3TextID = 1
$list = New-Object System.Collections.ArrayList
Clear-Content "$rootPath\test.txt"
Clear-Content "$rootPath\list.txt"
Clear-Content "$rootPath\output.txt"
# Parse each alarm group file in the project
foreach ($file in $fileNames) {
$Content = [xml](Get-Content -Path $file.FullName)
$ns = New-Object System.Xml.XmlNamespaceManager($Content.NameTable)
$ns=#{asdf="http://asdf-automation.co.at/AS/VC/Project"}
$AlarmgroupIndex = Select-Xml -Xml $Content -XPath "//asdf[contains(#Name,'Index')]" -namespace $ns | select -ExpandProperty node
$AlarmgroupIndexString = $AlarmgroupIndex.Value
$AlarmgroupLanguageText = Select-Xml -Xml $Content -XPath "//asdf:TextLayer" -namespace $ns | select -ExpandProperty node
$AlarmgroupIndexMap = Select-Xml -Xml $Content -XPath "//asdf:Index" -namespace $ns | select -ExpandProperty node
$LUT =#{}
$AlarmgroupIndexMap | foreach{
$LUT.($_.ID) = $_.Value
}
$tmpArray =#()
$list = $AlarmgroupLanguageText | foreach{
$LanguageIDString = $_.LanguageId
$AlarmgroupTextLayer = Select-Xml -Xml $Content -XPath "//asdf:TextLayer[#LanguageId='$LanguageIDString']/asdf:Text" -namespace $ns | select -ExpandProperty node
$AlarmgroupTextLayer | foreach{
if($LUT.ContainsKey($_.ID))
{
$ID_string = $LUT[$_.ID]
}
[pscustomobject]#{
Language = $LanguageIDString
GroupID = $AlarmgroupIndexString
TextID = $ID_string #-as [int]
Text = $_.Value
}
$ID_string =""
}
$LanguageIDString=""
}
$list = $list |Sort-Object -Property Language, GroupID, {$_.TextID -as [int]}
# $list = $list |Sort-Object -Property #{Expression={$_.Language}}, #{Expression={$_.TextId}} , #{Expression={$_.TextID -as [int]}}
$list | Out-File "$rootPath\list.txt" -Append -Encoding utf8
Output:
GroupID Language TextID Text
------- -------- ------ ----
24 aa Group
24 aa 0
24 aa 1
24 aa 2
24 aa 3
24 aa 4
24 aa 5
24 aa 6
24 aa 7
24 aa 8
24 aa 9
24 aa 10
GroupID Language TextID Text
------- -------- ------ ----
24 ar Group
24 ar 0
24 ar 1
24 ar 2
24 ar 3
24 ar 4
24 ar 5
24 ar 6
24 ar 7
So i have several headers in my outputfile. Is it possible to erase them or add elements to the Object without the header. Tried several solution nothing worked.
If i understand it correctly im generating an Object and add it to an object with all values incl. header.
[pscustomobject]#{
Language = $LanguageIDString
GroupID = $AlarmgroupIndexString
TextID = $ID_string #-as [int]
Text = $_.Value
You can assign all the output from the foreach loop to a single variable and then move the file-write logic to the end of the script where you can output it all at once:
# Assign results of entire `foreach(){}` statement to `$combinedLists`
$combinedLists = foreach ($file in $fileNames) {
# XML navigation + object creation + assignment to `$list` still goes here
# We sort and then instead of assigning the output to a variable directly,
# we just let it "bubble up" from here to the `$combinedList` assignment
$list |Sort-Object -Property Language, GroupID, {$_.TextID -as [int]}
}
# Now we can write everything to file at once (overwrites existing contents)
$combinedLists |Out-File "$rootPath\list.txt" -Force -Encoding utf8
Related
I am trying to get a list of files and a count of the number of rows in each file displayed in a table consisting of two columns, Name and Lines.
I have tried using format table but I don't think the problem is with the format of the table and more to do with my results being separate results. See below
#Get a list of files in the filepath location
$files = Get-ChildItem $filepath
$files | ForEach-Object { $_ ; $_ | Get-Content | Measure-Object -Line} | Format-Table Name,Lines
Expected results
Name Lines
File A
9
File B
89
Actual Results
Name Lines
File A
9
File B
89
Another approach how to make a custom object like this: Using PowerShell's Calculated Properties:
$files | Select-Object -Property #{ N = 'Name' ; E = { $_.Name} },
#{ N = 'Lines'; E = { ($_ | Get-Content | Measure-Object -Line).Lines } }
Name Lines
---- -----
dotNetEnumClass.ps1 232
DotNetVersions.ps1 9
dotNETversionTable.ps1 64
Typically you would make a custom object like this, instead of outputting two different kinds of objects.
$files | ForEach-Object {
$lines = $_ | Get-Content | Measure-Object -Line
[pscustomobject]#{name = $_.name
lines = $lines.lines}
}
name lines
---- -----
rof.ps1 11
rof.ps1~ 7
wai.ps1 2
wai.ps1~ 1
I have data in this shape:
externalName,day,workingHours,hoursAndMinutes
PRJF,1,11,11:00
PRJF,2,11,11:00
PRJF,3,0,0:00
PRJF,4,0,0:00
CFAW,1,11,11:00
CFAW,2,11,11:00
CFAW,3,11,11:00
CFAW,4,11,11:00
CFAW,5,0,0:00
CFAW,6,0,0:00
and so far code is
$gdata = Import-csv $filepath\$filename | Group-Object -Property Externalname;
$test = #()
$test += foreach($rostername in $gdata) {
$rostername.Group | Select -Unique externalName,
#{Name = 'AllDays';Expression = {(($rostername.Group) | measure -Property day).count}},
}
$test;
What I can't work out is how to do a conditional count of the lines where day is non-zero.
The aim is to produce two lines:
PRJF, 4, 2, 11
CFAW, 6, 4, 11
i.e. Roster name, roster length, days on, average hours worked per day on.
You need a where-object to filter for non zero workinghours
I'd use a [PSCustomObject] to generate a new table
EDIT a bit more efficient with only one Measure-Object
## Q:\Test\2018\08\06\SO_51700660.ps1
$filepath = 'Q:\Test\2018\08\06'
$filename = 'SO_S1700660.csv'
$gdata = Import-Csv (Join-Path $filepath $filename) | Group-Object -Property Externalname
$test = ForEach($Roster in $gdata) {
$WH = ($Roster.Group.Workinghours|Where-Object {$_ -ne 0}|Measure-Object -Ave -Sum)
[PSCustomObject]#{
RosterName = $Roster.Name
RosterLength = $Roster.Count
DaysOn = $WH.count
AvgHours = $WH.Average
TotalHours = $WH.Sum
}
}
$test | Format-Table
Sample output:
> .\SO_51700660.ps1
RosterName RosterLength DaysOn AvgHours TotalHours
---------- ------------ ------ -------- ----------
PRJF 4 2 11 22
CFAW 6 4 11 44
Our users sometimes gives us misspelled names/usernames and I would like to be able to search active directory for a near match, sorting by closest (any algorithm would be fine).
For example, if I try
Get-Aduser -Filter {GivenName -like "Jack"}
I can find the user Jack, but not if I use "Jacck" or "ack"
Is there a simple way to do this?
You can calculate the Levenshtein distance between the two strings and make sure it's under a certain threshold (probably 1 or 2). There is a powershell example here:
Levenshtein distance in powershell
Examples:
Jack and Jacck have an LD of 1.
Jack and ack have an LD of 1.
Palle and Havnefoged have an LD of 8.
Interesting question and answers. But a possible simpler solution is to search by more than one attribute as I would hope most people would spell one of their names properly :)
Get-ADUser -Filter {GivenName -like "FirstName" -or SurName -Like "SecondName"}
The Soundex algorithm is designed for just this situation. Here is some PowerShell code that might help:
Get-Soundex.ps1
OK, based on the great answers that I got (thanks #boxdog and #Palle Due) I am posting a more complete one.
Major source: https://github.com/gravejester/Communary.PASM - PowerShell Approximate String Matching. Great Module for this topic.
1) FuzzyMatchScore function
source: https://github.com/gravejester/Communary.PASM/tree/master/Functions
# download functions to the temp folder
$urls =
"https://raw.githubusercontent.com/gravejester/Communary.PASM/master/Functions/Get-CommonPrefix.ps1" ,
"https://raw.githubusercontent.com/gravejester/Communary.PASM/master/Functions/Get-LevenshteinDistance.ps1" ,
"https://raw.githubusercontent.com/gravejester/Communary.PASM/master/Functions/Get-LongestCommonSubstring.ps1" ,
"https://raw.githubusercontent.com/gravejester/Communary.PASM/master/Functions/Get-FuzzyMatchScore.ps1"
$paths = $urls | %{$_.split("\/")|select -last 1| %{"$env:TEMP\$_"}}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
for($i=0;$i -lt $urls.count;$i++){
Invoke-WebRequest -Uri $urls[$i] -OutFile $paths[$i]
}
# concatenating the functions so we don't have to deal with source permissions
foreach($path in $paths){
cat $path | Add-Content "$env:TEMP\Fuzzy_score_functions.ps1"
}
# to save for later, open the temp folder with: Invoke-Item $env:TEMP
# then copy "Fuzzy_score_functions.ps1" somewhere else
# source Fuzzy_score_functions.ps1
. "$env:TEMP\Fuzzy_score_functions.ps1"
Simple test:
Get-FuzzyMatchScore "a" "abc" # 98
Create a score function:
## start function
function get_score{
param($searchQuery,$searchData,$nlist,[switch]$levd)
if($nlist -eq $null){$nlist = 10}
$scores = foreach($string in $searchData){
Try{
if($levd){
$score = Get-LevenshteinDistance $searchQuery $string }
else{
$score = Get-FuzzyMatchScore -Search $searchQuery -String $string }
Write-Output (,([PSCustomObject][Ordered] #{
Score = $score
Result = $string
}))
$I = $searchData.indexof($string)/$searchData.count*100
$I = [math]::Round($I)
Write-Progress -Activity "Search in Progress" -Status "$I% Complete:" -PercentComplete $I
}Catch{Continue}
}
if($levd) { $scores | Sort-Object Score,Result |select -First $nlist }
else {$scores | Sort-Object Score,Result -Descending |select -First $nlist }
} ## end function
Examples
get_score "Karolin" #("Kathrin","Jane","John","Cameron")
# check the difference between Fuzzy and LevenshteinDistance mode
$names = "Ferris","Cameron","Sloane","Jeanie","Edward","Tom","Katie","Grace"
"Fuzzy"; get_score "Cam" $names
"Levenshtein"; get_score "Cam" $names -levd
Test the performance on a big dataset
## donload baby-names
$url = "https://github.com/hadley/data-baby-names/raw/master/baby-names.csv"
$output = "$env:TEMP\baby-names.csv"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri $url -OutFile $output
$babynames = import-csv "$env:TEMP\baby-names.csv"
$babynames.count # 258000 lines
$babynames[0..3] # year, name, percent, sex
$searchdata = $babynames.name[0..499]
$query = "Waren" # missing letter
"Fuzzy"; get_score $query $searchdata
"Levenshtein"; get_score $query $searchdata -levd
$query = "Jon" # missing letter
"Fuzzy"; get_score $query $searchdata
"Levenshtein"; get_score $query $searchdata -levd
$query = "Howie" # lookalike
"Fuzzy"; get_score $query $searchdata;
"Levenshtein"; get_score $query $searchdata -levd
Test
$query = "John"
$res = for($i=1;$i -le 10;$i++){
$searchdata = $babynames.name[0..($i*100-1)]
$meas = measure-command{$res = get_score $query $searchdata}
write-host $i
Write-Output (,([PSCustomObject][Ordered] #{
N = $i*100
MS = $meas.Milliseconds
MS_per_line = [math]::Round($meas.Milliseconds/$searchdata.Count,2)
}))
}
$res
+------+-----+-------------+
| N | MS | MS_per_line |
| - | -- | ----------- |
| 100 | 696 | 6.96 |
| 200 | 544 | 2.72 |
| 300 | 336 | 1.12 |
| 400 | 6 | 0.02 |
| 500 | 718 | 1.44 |
| 600 | 452 | 0.75 |
| 700 | 224 | 0.32 |
| 800 | 912 | 1.14 |
| 900 | 718 | 0.8 |
| 1000 | 417 | 0.42 |
+------+-----+-------------+
These times are quite crazy, if anyone understand why please comment on it.
2) Generate a table of Names from Active Directory
The best way to do this depends on the organization of the AD. Here we have many OUs, but common users will be in Users and DisabledUsers. Also Domain and DC will be different (I'm changing ours here to <domain> and <DC>).
# One way to get a List of OUs
Get-ADOrganizationalUnit -Filter * -Properties CanonicalName |
Select-Object -Property CanonicalName
then you can use Where-Object -FilterScript {} to filter per OU
# example, saving on the temp folder
Get-ADUser -f * |
Where-Object -FilterScript {
($_.DistinguishedName -match "CN=\w*,OU=DisabledUsers,DC=<domain>,DC=<DC>" -or
$_.DistinguishedName -match "CN=\w*,OU=Users,DC=<domain>,DC=<DC>") -and
$_.GivenName -ne $null #remove users without givenname, like test users
} |
select #{n="Fullname";e={$_.GivenName+" "+$_.Surname}},
GivenName,Surname,SamAccountName |
Export-CSV -Path "$env:TEMP\all_Users.csv" -NoTypeInformation
# you can open the file to inspect
Invoke-Item "$env:TEMP\all_Users.csv"
# import
$allusers = Import-Csv "$env:TEMP\all_Users.csv"
$allusers.Count # number of lines
Usage:
get_score "Jane Done" $allusers.fullname 15 # return the 15 first
get_score "jdoe" $allusers.samaccountname 15
Barcode1 Plate # 12/29/2017 07:35:56 EST
A 1 4 5 6
A 1 4 5 6
A 1 4 5 6
A 1 4 5 6
A 1 4 5 6
A 1 4 5 6
A 1 4 5 6
Above is an example of a tab delimited text file. I need to get the data from the column with no header; namely, the columns at the end and I don't know how to identify it. I am trying to swap columns and output a text file. The source data file format is the same every time.
This is part of what I have:
$swapColumns = #{
column1 = #{
name = "date-header"
instance = 1
}
column2 = #{
name = "Blank"
instance = 1
}
}
$formats = #(
'XR-{0:yyyyMMdd}-01.txt'
)
$date = [datetime]::now
$ErrorActionPreference = 'Stop'
function Get-HeaderIndex {
param(
[System.Collections.Generic.List[string]]$Source,
[string]$Header,
[uint16]$Instance
)
$index = 0;
for ($i = 0; $i -lt $Instance; $i++) {
$index = $Source.IndexOf($Header, $index, ($Source.Count - $index))
if (($index -eq -1) -or (($i + 1) -eq $Instance)) {
break
}
$index = $index + 1
}
if ($index -eq -1) { throw "index not found" }
return $index
}
#grabs the first item in folder matching UCX-*.txt
$fileDetails = Get-ChildItem $PSScriptRoot\UCX-*.txt | select -First 1
#gets the file contents
$file = Get-Content $fileDetails
#break up script in sections that look like '======section======'
#and store the section name and line number it starts on
$sections = #()
for ($i = 0; $i -lt $file.Count; $i++) {
if ($file[$i] -match '^=+(\w+)=+$') {
$section = $Matches[1]
$sections += [pscustomobject]#{line = $i; header = $section}
}
}
#get the data section
$dataSection = $sections | ? {$_.header -eq 'data'}
#get the section following data
$nextSection = $sections | ? {$_.line -gt $dataSection.line} | sort
-Property line | select -First 1
#get data column headers
$dataHeaders = New-Object System.Collections.Generic.List[string]
$file[$dataSection.line + 1].split("`t") | % {
[datetime]$headerDateValue = [datetime]::MinValue
$headerIsDate = [datetime]::TryParse($_.Replace('EST','').Trim(),
[ref] $headerDateValue)
if ($headerIsDate) {
$dataHeaders.Add('date-header')
}
else {
$dataHeaders.Add($_)
}
}
#get index of columns defined in $swapColumns
$column1 = Get-HeaderIndex -Source $dataHeaders -Header
$swapColumns.column1.name -Instance $swapColumns.column1.instance
$column2 = Get-HeaderIndex -Source $dataHeaders -Header
swapColumns.column2.name -Instance $swapColumns.column2.instance
#iterate over each row in data section, swap data from column1/column2
for ($i = $dataSection.line + 2; $i -lt $nextSection.line - 1; $i++) {
$line = $file[$i]
$parts = $line.split("`t")
$tmp1 = $parts[$column1]
$parts[$column1] = $parts[$column2]
$parts[$column2] = $tmp1
$file[$i] = $parts -join "`t"
}
#write new file contents to files with names defined in $formats
$formats | % {
$file | Out-File ($_ -f $date) -Force
}
If you know what your file format is going to be then forget whatever the current header is and assume when we convert the file to a CSV object.
It looks like you need to parse the date of out the header which should be trivial. Grab it from $fileheader however you would like.
$wholeFile = Get-Content C:\temp\test.txt
$fileHeader = $wholeFile[0] -split "`t"
$newHeader = "Barcode1", "Plate #", "Date", "Plumbus", "Dinglebop"
$wholeFile |Select-Object -Skip 1 | ConvertFrom-Csv -Delimiter "`t" -Header $newHeader
If the columns length is always the same, there's another option, specify manually the width of the columns, See example:
$content = Get-Content C:\temp.tsv
$columns = 13, 24, 35 | Sort -Descending
$Delimiter = ','
$Results = $content | % {
$line = $_
$columns | % {
$line = $line.Insert($_, $Delimiter)
}
$line
} |
ConvertFrom-Csv -Delimiter $Delimiter
Results:
Barcode1 Plate # H1 12/29/2017 07:35:56 EST
--------- ----------- -- -----------------------
A 1 4 5
A 1 4 5
A 1 4 5
A 1 4 5
A 1 4 5
A 1 4 5
A 1 4 5
Then you can easily get the data you need:
$Results[0].H1
4
[This answer doesn't solve the OP's problem after clarifying the exact requirements, but may be of general interest to some, given the question's generic title.]
If the file is really tab-delimited, you can use Import-Csv -Delimiter "`t" to read it, in which case PowerShell will autogenerate header names as H<n> if they're missing, where <n> is a sequence number starting with 1.
Caveat: This doesn't work if the unnamed column is the last one, because - inexplicably - Import-Csv then ignores the entire column (more generally, any run of trailing delimiters).
Import-Csv -Delimiter "`t" file.tsv | Select-Object -ExpandProperty H1
What could be the best way to resolve a computer name apart from using:
[System.Net.DNS]::GetHostByName('MachineName').HostName
I dont want to import any specific DNS Modules.
You can try the GetHostEntry method:
[Net.DNS]::GetHostEntry("MachineName")
Another way would be to ping it using Test-Connection cmdlet, see this tip
I was in a case where I had to query a specific DNS server, which is not possible directly with .net / powershell. So I ended up with using the good old nslookup :
$client="10.110.10.10"
$ns="10.20.1.10"
(nslookup $client $ns |sls name).toString().split(":")[1].trim()
The following 2 ways to resolve IP's to DNS addresses are the only ones.
It's how you use it that counts.
[Net.DNS]::GetHostEntry("MachineName")
[System.Net.DNS]::GetHostByName('MachineName').HostName
Like I said, It's how you use them.
I have written a script for doing just this.
It takes a list of IP addresses and resolves there DNS.
Later in the script it converts the output to an excel sheet, showing you the results.
Based upon filters you can set the layout.
Now I know not all IP's will be resolved with these methods, that's why I included a function in my script that filters out unresolved IP's and places them to the bottom of the excel sheet.
(Giving every IP a direct link to who.is/whois/ipadress
Here's the script, íf you are interested.
#Get current date
$Date = date -format yyyy-MM-dd
$Company = "Company"
$Company2 = "Company2"
########################
#Define all Paths.
$Path = "C:\inetpub\wwwroot\BlockedIP" #This is where your file's will be saved.
md "$Path\HTML\$Date" -Force |Out-Null
$path2 = "$Path\HTML\$Date"
$PathWeb = "/ResolvedIp/HTML/$Date"
########################
#Define File's used or created in this script.
$File = "$Path\IP-$Date.txt"
$FileHtml = "$Path2\IP-$Date.htm"
$FileXML = "$Path\IP-$Date.xlsx"
$FileHTMLWeb = "$PathWeb\IP-$date.htm"
######################################
#Define error actions.
$erroractionpreference = "SilentlyContinue"
###########################################
#Since the script used COM objects it will need the following 2 folders:
#(32Bit)
MD "C:\Windows\System32\config\systemprofile\Dektop" -force
MD "C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\Temporary Internet" -force
#(64Bit)
MD "C:\Windows\SysWOW64\config\systemprofile\Desktop" -force
MD "C:\Windows\SysWOW64\config\systemprofile\AppData\Local\Microsoft\Windows\Temporary Internet" -force
#Once successfull the script will run without a problem if scheduled.
cls
(gc $File) | ? {$_.trim() -ne "" } | set-content $File
$IPCount = (gc $File)
$IPCount = $IPCount.count
write "$IPCount unique IP addresses detected."
#Define error actions.
$erroractionpreference = "SilentlyContinue"
#Get content from given IP list.
$colComputers = #(gc $File | sort |Select -unique)
$SourceCount = $colComputers.Count
write "$SourceCount IP's detected."
Function Set-KnownIPs{
Param([Object]$DNSLookupObject)
Switch($DNSLookupObject){
{$_.Source -Match "(108.162.254|141.101.(?:104|105)|199.27.128|173.245(?:53|52|51))"}{$_.HostName = "CloudFlare, Inc."}
{$_.Source -Match "(64.18.[0-18])"}{$_.HostName = "Google, Inc."}
{$_.Source -Match "(192.168|127.0.0)"}{$_.HostName = "Internal Infrastructure"}
}
$DNSLookupObject
}
#Get DNS Results
$Progress=1
$DNSResults = $colComputers | %{
Write-Progress -Activity "Creating a usable 'Blocked IP' list ($Progress/$sourcecount)" -PercentComplete ($Progress/$sourceCount*100) -Status "Please stand by"
try {
($dnsresult = [System.Net.DNS]::GetHostEntry($_))
}
catch {
$dnsresult = "Fail"
}
Set-KnownIPs -DNSLookupObject ([PSCustomObject][Ordered]#{
Source=$_.ToUpper()
HostName=$(if(!([string]::IsNullOrEmpty($dnsresult.HostName))){$dnsresult.HostName})
IPAddress=$(if(!([string]::IsNullOrEmpty($dnsresult.AddressList))){$dnsresult.AddressList[0].ToString()})
})
$Progress++
}
$Keywords = #("Google","Cloudflare","Cloud","Ping",
"Easy-Voyage","McAfee","Pingdom","Panopta","Scoot","Uniglobe",
"Internal")
$Filter = "($(($Keywords|%{[RegEx]::Escape($_)}) -join "|"))"
$DNSLookupFailed = $DNSResults |
?{[string]::IsNullOrEmpty($_.HostName) -and !($_ -match $filter)}
$DNSWithKeyword = $DNSResults |
?{$_ -match $Filter}
$DNSNoKeyword = $DNSResults |
?{!($_.HostName -match $Filter) -and !([string]::IsNullOrEmpty($_.IPAddress))}
#$count = ($DNSResults|?{$_ -match $filter}).count
$count = $SourceCount
#####################
#start Excel.
$a = New-Object -comobject Excel.Application
# set interactive to false so nothing from excel is shown.
$a.DisplayAlerts = $False
$a.ScreenUpdating = $True
$a.Visible = $True
$a.UserControl = $True
$a.Interactive = $True
###########################
#Create sheets in Excel.
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Activate() | Out-Null
#Create a Title for the first worksheet and adjust the font
$c.Cells.Item(1,1)= "Blocked IP's $Date"
$c.Cells.Item(1,1).Font.ColorIndex = 55
$c.Cells.Item(1,1).Font.Color = 8210719
$c.Cells.Item((3+$DNSWithKeyword.Count+1),1) = "IP's not in whitelist"
$c.Cells.Item((3+$DNSWithKeyword.Count+1),1).Font.ColorIndex = 55
$c.Cells.Item((3+$DNSWithKeyword.Count+1),1).Font.Color = 8210719
$c.Cells.Item((3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+3),1)= "IP's without DNS return"
$c.Cells.Item((3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+3),1).Font.ColorIndex = 55
$c.Cells.Item((3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+3),1).Font.Color = 8210719
#######################################
$range = $c.Range("a1","e1")
$range.Style = 'Title'
$range.Select()
$range.MergeCells = $true
$range.VerticalAlignment = -4108
################################
$Linkedin = "https://www.linkedin.com/profile/view?id=96981180" #Look me up! :D
#Define row to be used for linkedin link.
$CounterRow = $Count+5
######################
#Define subjects.
$c.Name = "Blocked IP's ($Date)"
$c.Cells.Item(2,1) = "Given IP"
$c.Cells.Item(2,2) = "Resolved DNS"
$c.Cells.Item(2,3) = "Returned IP"
$c.Cells.Item(2,5) = "$Company"
$c.Cells.Item((3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+$DNSLookupFailed.Count+5),1) = "Created by"
########################################
$link = "http://www.$Company"
$link2 = "$Linkedin"
$r = $c.Range("E2")
[void]$c.Hyperlinks.Add($r, $link)
$r = $c.Range("A$(3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+$DNSLookupFailed.Count+5)")
[void]$c.Hyperlinks.Add($r, $link2)
###################################
#Define cell formatting from subjects.
$c.Range("A2:E2").Interior.ColorIndex = 6
$c.Range("A2:E2").font.size = 13
$c.Range("A2:E2").Font.ColorIndex = 1
$c.Range("A2:E2").Font.Bold = $True
###################################
#Define the usedrange, excluding header and footer rows
$KeyRange = $c.Range("A3:c$(3+$DNSWithKeyword.Count)")
$NoKeyRange = $c.Range("A$(3+$DNSWithKeyword.Count+2):c$(3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+2)")
$NoDNSRange = $c.Range("A$(3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+4):c$(3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+$DNSLookupFailed.Count+4)")
$SheetRange = $c.Range("A3:e$(4+$DNSWithKeyword.Count+$DNSNoKeyword.Count+$DNSLookupFailed.Count+4)")
$Investigate = $c.Range("c$(3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+4):c$(3+$DNSWithKeyword.Count+$DNSNoKeyword.Count+$DNSLookupFailed.Count+4)")
################################
#Set background color for the IP list.
$SheetRange.interior.colorindex = 6
$KeyRange.interior.colorindex = 4
$NoKeyRange.interior.colorindex = 15
$NoDNSRange.interior.colorindex = 8
####################################
#Populate data into spreadsheet
$DNSWithKeyword | Select Source, HostName, IPAddress | Sort HostName -Descending |
ConvertTo-Csv -Delimiter "`t" -NoTypeInformation |
Select -Skip 1 | Clip
$c.Paste($KeyRange,$false)
$DNSNoKeyword | Select Source, HostName, IPAddress | Sort HostName -Descending |
ConvertTo-Csv -Delimiter "`t" -NoTypeInformation |
Select -Skip 1 | Clip
$c.Paste($NoKeyRange,$false)
$DNSLookupFailed | Select Source, HostName, IPAddress | sort Source -Descending|
ConvertTo-Csv -Delimiter "`t" -NoTypeInformation |
Select -Skip 1 | Clip
$c.Paste($NoDNSRange,$false)
############################
ForEach($Cell in $Investigate){
If([String]::IsNullOrWhitespace($Cell.value2)){
$Cell.Item($_) = "N/A"
[void]$cell.Hyperlinks.Add($Cell)
}
}
###########################################################################
#Define borders here.
$xlOpenXMLWorkbook = 51
$xlAutomatic=-4105
$xlBottom = -4107
$xlCenter = -4108
$xlRight = -4152
$xlContext = -5002
$xlContinuous=1
$xlDiagonalDown=5
$xlDiagonalUp=6
$xlEdgeBottom=9
$xlEdgeLeft=7
$xlEdgeRight=10
$xlEdgeTop=8
$xlInsideHorizontal=12
$xlInsideVertical=11
$xlNone=-4142
$xlThin=2
#########
$selection = $c.range("A2:C$(1+$DNSResults.Count-9)")
$selection.select() |out-null
$selection.HorizontalAlignment = $xlRight
$selection.VerticalAlignment = $xlBottom
$selection.WrapText = $false
$selection.Orientation = 0
$selection.AddIndent = $false
$selection.IndentLevel = 0
$selection.ShrinkToFit = $false
$selection.ReadingOrder = $xlContext
$selection.MergeCells = $false
$selection.Borders.Item($xlInsideHorizontal).Weight = $xlThin
#############################################################
#Define the usedrange for autofitting.
$d = $c.UsedRange
#################
#Make everything fit in it's cell.
$d.EntireColumn.AutoFit() | Out-Null
####################################
$c.usedrange | Where{$_.Value2 -match "(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)"} |
ForEach{$IPLink = "http://who.is/whois-ip/ip-address/$($Matches[1])";[void]$c.Hyperlinks.Add($_, $IPLink)}
#Define html code for Excel save to .htm.
$xlExcelHTML = 44
#################
#Save final result as an .xlsx file.
$b.SaveAs("$FileXML")
#####################
#Save final result as a .htm file
$b.SaveAs("$FileHTML",$xlExcelHTML)
###################################
In powershell 5.1 you can use
Resolve-DnsName