getting two ipaddress from the same FQDN - powershell

I am trying to get a list of IP addresses from several servers defined in server.txt.
Each server has 2 IP addresses and 2 FQDN.
Example:
servername (Production lan):server1 IPaddress:147.111.111.16
servername (backup lan):server1-bck IPaddress:10.0.4.12
Here is the code I'm using:
$servers = Get-Content server.txt
$server2 = "$servers-bck"
$zai = ""
foreach ($server in $servers)
{
$zai = $zai + $server + "`t" +
([System.Net.Dns]::GetHostAddresses($server) | foreach {echo $_.IPAddressToString}) +
"`t" +
([System.Net.Dns]::GetHostAddresses($server2) | foreach {echo $_.IPAddressToString}) +
"`n"
}
$zai > IP-address.csv
Unfortunately only the IP for Production lan is correct. The IP for backup lan only shows the IP of the last server in server.txt. I assume the problem is in: "foreach { echo $._IPAddressToString". I don't know how to fix it.
Any idea or advice will be helpful.

You seem to assume that
$server2="$servers-bck"
would append -bck to the name of each element in the array $servers. That is not the case. Instead, the array is expanded by joining its elements using the output field separator, so that -bck ends up after the last array element:
PS C:\> $a = 'a', 'b', 'c'
PS C:\> $a
a
b
c
PS C:\> "$a-bck"
a b c-bck
To get the backup server for each server from your list you need to append -bck inside the loop:
...([System.Net.Dns]::GetHostAddresses("$server-bck"))...
As a side-note, you shouldn't build CSVs manually. Let PowerShell do that for you:
$servers | % {
New-Object -Type PSObject -Property #{
'Name' = $_
'Production' = ([Net.Dns]::GetHostAddresses($_) | select -Expand IPAddressToString) -join ' '
'Backup' = ([Net.Dns]::GetHostAddresses("$_-bck") | select -Expand IPAddressToString) -join ' '
}
} | Export-Csv 'IP-address.csv' -NoType -Delimiter "`t"

Related

How to compare a CSV Host_Name field to a Hashtable Host_Name field and then merge the data into an Out-File in text format

Need to take my $sw CSV file and use foreach to compare that against a hash translation table $swtranslation, Key field, then output matches including the hash table's values that match into a text file.
Problem I have is it runs the search for a few minutes and returns the sw_names.txt output file with nothing in it. It should have well over 1074+ matches. My guess is my syntax or something is not right.
See code for what I have going so far.
# This is the CSV file listing all the network switches I need to run against the translation table.
$sw = Import-Csv .\AllDeviceForExport.csv -Header Host_Name, IP_Address
# Compile the switch translation table for processing and convert to hash //
$swtranslation = #{};
Import-Csv .\sw_translation.csv -Header Host_Name, DataSpace_ID | % {
$swhash[$_.Host_Name] = $_.DataSpace_ID
}
# Run the Switch listing $sw against the translation table $swtranslation
# matching the DataSpace_ID and merging DataSpace_ID and Host name and
# all other switch fields together in output //
foreach ($key in $swhash.Keys) {
$sw | Select-Object #{n="Name";e={$outputhash[$swhash.Keys($_.Host_Name).Value]}},* |
Where-Object { $_.Name -ne $null } |
Foreach { $_ -replace '--' } |
Out-File ./sw_names.txt -Force
}
Expected results:
Host_Name DataSpace_ID
ABC-123-3750-SW1 1
DEF-234-2950-SW1 5
DEF-234-2950-SW2 5
GHI-567-4510-SW1 6
GHI-567-4510-SW2 6
It's unclear what you are after.
You have two csv files without headers,
.\AllDeviceForExport.csv -Header Host_Name, IP_Address
.\sw_translation.csv -Header Host_Name, DataSpace_ID
Usually one builds a hash table from one file and iterates the other to check if there are matching properties or not.
What your code tries to do is building the hash table, iterate the keys of it and then (very inefficiently) on each key search the whole other file thwarting the whole idea.
Not knowing which files Host_Name property should be checked I suggest a different approach:
Use Compare-Object
## Q:\Test\2019\08\15\SO_57515952.ps1
# simulate $swtrans = Import-Csv .\sw_translation.csv -Header Host_Name, DataSpace_ID
$swtrans = #"
ABC-123-3750-SW1,1
DEF-234-2950-SW1,5
DEF-234-2950-SW2,5
GHI-567-4510-SW1,6
GHI-567-4510-SW2,6
"# -split '\r?\n' | ConvertFrom-Csv -Header Host_Name, DataSpace_ID
# simulate $sw = Import-Csv .\AllDeviceForExport.csv -Header Host_Name, IP_Address
$sw = #"
DEF-234-2950-SW1,192.168.234.1
DEF-234-2950-SW2,192.168.234.2
GHI-567-4510-SW1,192.168.567.1
GHI-567-4510-SW2,192.168.567.2
GHI-567-4510-SW3,192.168.567.3
"# -split '\r?\n' | ConvertFrom-Csv -Header Host_Name, IP_Address
Compare-Object -Ref $swtrans -Diff $sw -Property Host_Name -PassThru -IncludeEqual
This yields:
> Q:\Test\2019\08\15\SO_57515952.ps1
Host_Name DataSpace_ID SideIndicator
--------- ------------ -------------
DEF-234-2950-SW1 5 ==
DEF-234-2950-SW2 5 ==
GHI-567-4510-SW1 6 ==
GHI-567-4510-SW2 6 ==
GHI-567-4510-SW3 =>
ABC-123-3750-SW1 1 <=
The SideIndicator Property can be used to specify which lines to output and itself suppressed.

Retrieve data from last line in vmware.log file?

I currently have a script that retrieves the last modified date of the .vmx in a VM's datastore in vCenter. I need to make changes to instead use and display the last date in the vmware.log file (located in the same datastore as the .vmx)
I'm not sure how to grab that line and convert it to a XX/XX/XXXX format. In the log file, it shows it as Dec 23 10 for example. If this is not possible, no worries. I just need to pull the last line in the log file and export it to a .csv file. Below is my current code:
add-pssnapin VMware.VimAutomation.Core
# ---------- Only modify the fields in this area -------------
$vCenter = 'qlab-copsmgr' #name of the vCenter
$dataCenter = 'Fly-away Kit' #name of the DataCenter
$outputFile = $vCenter + '-LastDateUsed.csv' #desired output file name
# ---------- No modification is needed in the below code. Do not edit -------------
$columnName = "Name,DataStore,Date Last Used" | Out-File .\$OutputFile -Encoding ascii
Connect-VIServer $vCenter -WarningAction SilentlyContinue
$vmList = Get-VM | where { $_.PowerState -eq “PoweredOff”} | select Name
$vmList = $vmList -replace 'Name : ', '' -replace '#{Name=', '' -replace '}', ''
ForEach ($VM in $vmList)
{
# Get configuration and path to vmx file
$VMconfig = Get-VM $VM | Get-View | select config
$VMXpath = $VMconfig.config.files.VMpathName
# Remove and/or replace unwanted strings
$VMXpath = $VMXpath -replace '\[','' -replace '\] ','\' -replace '#{Filename=','/' -replace '}','' -replace '/','\'
# List the vmx file in the datastore
$VMXinfo = ls vmstores:\$VCenter#443\$DataCenter\$VMXpath | Where {$_.LastWriteTime} | select -first 1 | select FolderPath, LastWriteTime
# Remove and/or replace unwanted strings
$VMXinfo = $VMXinfo -replace 'DatastoreFullPath=', '' -replace '#{', '' -replace '}', '' -replace ';', ',' -replace 'LastWriteTime=', ''
# Output vmx information to .csv file
$output = $VM + ', ' + $VMXinfo
$output
echo $output >> $OutputFile
}
I also needed to pull the last event from the vmware.log file in order to backtrack the power off time for VMs where there is no vCenter event history. I looked at file timestamps but found that some VM processes and possibly backup solutions can make them useless.
I tried reading the file in place but ran into issues with the PSDrive type not supporting Get-Content in place. So for better or worse for my solution I started with one of LucD's scripts - the 'Retrieve the logs' script from http://www.lucd.info/2011/02/27/virtual-machine-logging/ which pulls a VMs vmware.log file and copies it to local storage. I then modified it to copy the vmware.log file to a local temp folder, read the last line from the file before deleting the file and return the last line of the log as a PS object.
Note, this is slow and I'm sure my hacks to LucD's script are not elegant, but it does work and I hope if helps someone.
Note: This converts the time value from the log to a PS date object by simple piping the string timestamp from the file into Get-Date. I've read that this does not work as expected for non-US date formatting. For those outside of the US you might want to look into this or just pass the raw timestamp string from the log instead of converting it.
#Examples:
#$lastEventTime = (Get-VM -Name "SomeVM" | Get-VMLogLastEvent).EventTime
#$lastEventTime = Get-VMLogLastEvent -VM "SomeVM" -Path "C:\alternatetemp\"
function Get-VMLogLastEvent{
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)][PSObject[]]$VM,
[string]$Path=$env:TEMP
)
process{
$report = #()
foreach($obj in $VM){
if($obj.GetType().Name -eq "string"){
$obj = Get-VM -Name $obj
}
$logpath = ($obj.ExtensionData.LayoutEx.File | ?{$_.Name -like "*/vmware.log"}).Name
$dsName = $logPath.Split(']')[0].Trim('[')
$vmPath = $logPath.Split(']')[1].Trim(' ')
$ds = Get-Datastore -Name $dsName
$drvName = "MyDS" + (Get-Random)
$localLog = $Path + "\" + $obj.Name + ".vmware.log"
New-PSDrive -Location $ds -Name $drvName -PSProvider VimDatastore -Root '\' | Out-Null
Copy-DatastoreItem -Item ($drvName + ":" + $vmPath) -Destination $localLog -Force:$true
Remove-PSDrive -Name $drvName -Confirm:$false
$lastEvent = Get-Content -Path $localLog -Tail 1
Remove-Item -Path $localLog -Confirm:$false
$row = "" | Select VM, EventType, Event, EventTime
$row.VM = $obj.Name
($row.EventTime, $row.EventType, $row.Event) = $lastEvent.Split("|")
$row.EventTime = $row.EventTime | Get-Date
$report += $row
}
$report
}
}
That should cover your request, but to expound further on why I needed the detail, which reading between the lines may also benefit you, I'll continue.
I inherited hundreds of legacy VMs that have been powered off from various past acquisitions and divestitures and many of which have been moved between vCenter instances losing all event log detail. When I started my cleanup effort in just one datacenter I had over 60TB of powered off VMs. With the legacy nature of these there was also no detail available on who owned or had any knowledge of these old VMs.
For this I hacked another script I found, also from LucD here: https://communities.vmware.com/thread/540397.
This will take in all the powered off VMs, attempt to determine the time powered off via vCenter event history. I modified it to fall back to the above Get-VMLogLastEvent function to get the final poweroff time of the VM if event log detail is not available.
Error catching could be improved - this will error on VMs where for one reason or another there is no vmware.log file. But quick and dirty I've found this to work and provides the detail on what I need for over 90%.
Again this relies on the above function and for me at least the errors just fail through passing through null values. One could probably remove the errors by adding a check for vmware.log existance before attempting to copy it though this would add a touch more latency in execution due to the slow PSDrive interface to datastores.
$Report = #()
$VMs = Get-VM | Where {$_.PowerState -eq "PoweredOff"}
$Datastores = Get-Datastore | Select Name, Id
$PowerOffEvents = Get-VIEvent -Entity $VMs -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} | Group-Object -Property {$_.Vm.Name}
foreach ($VM in $VMs) {
$lastPO = ($PowerOffEvents | Where { $_.Group[0].Vm.Vm -eq $VM.Id }).Group | Sort-Object -Property CreatedTime -Descending | Select -First 1
$lastLogTime = "";
# If no event log detail, revert to vmware.log last entry which takes more time...
if (($lastPO.PoweredOffTime -eq "") -or ($lastPO.PoweredOffTime -eq $null)){
$lastLogTime = (Get-VMLogLastEvent -VM $VM).EventTime
}
$row = "" | select VMName,Powerstate,OS,Host,Cluster,Datastore,NumCPU,MemMb,DiskGb,PoweredOffTime,PoweredOffBy,LastLogTime
$row.VMName = $vm.Name
$row.Powerstate = $vm.Powerstate
$row.OS = $vm.Guest.OSFullName
$row.Host = $vm.VMHost.name
$row.Cluster = $vm.VMHost.Parent.Name
$row.Datastore = $Datastores | Where{$_.Id -eq ($vm.DatastoreIdList | select -First 1)} | Select -ExpandProperty Name
$row.NumCPU = $vm.NumCPU
$row.MemMb = $vm.MemoryMB
$row.DiskGb = Get-HardDisk -VM $vm | Measure-Object -Property CapacityGB -Sum | select -ExpandProperty Sum
$row.PoweredOffTime = $lastPO.CreatedTime
$row.PoweredOffBy = $lastPO.UserName
$row.LastLogTime = $lastLogTime
$report += $row
}
# Output to screen
$report | Sort Cluster, Host, VMName | Select VMName, Cluster, Host, NumCPU, MemMb, #{N='DiskGb';E={[math]::Round($_.DiskGb,2)}}, PoweredOffTime, PoweredOffBy | ft -a
# Output to CSV - change path/filename as appropriate
$report | Sort Cluster, Host, VMName | Export-Csv -Path "output\Powered_Off_VMs_Report.csv" -NoTypeInformation -UseCulture
Cheers!
I pray this pays back some of the karma I've used.
Meyeaard
I have made a script that checks line by line and if string is found changes it to desired format
#example input you can use get-content PATH to txt or any file and assign it to $lines variable
$lines = #"
ernfoewnfnsf
ernfoewnfnsf
Dec 23 10 sgdsgdfgsdadasd
"# -split "\r\n"
#checks line by line and if find anything that maches start of the line, one Big letter two small, space, two digits, space, two digits, space
$lines | ForEach-Object{
if ($_ -match "^[A-Z][a-z]{2}\s\d{2}\s\d{2}\s")
{
$match = [convert]::ToDateTime($matches[0])
$_ -replace $matches[0], "$($match.ToShortDateString()) " | out-file { PATH } -APPEND
}
else
{
$_ | out-file { PATH } -APPEND
}
}
just change {PATH} with a filenamePAth and this should work for you

Parsing multiple valus in multiple variables

I am trying to find a way to execute a command in powershell and put each line of the result in a different variable to use them later in my script. For example if i execute the below command:
C:\> Get-VMHost -Name hq-esxi-prod-01a.nsx.gss | Get-VM | select Name
I will get the below:
Name
----
HQ-LinServ-01a
HQ-Win2012-01a
HQ-Web-02a
I want to have a script that will add each line in a different variable in a script (excluding the first which is name).
how can i do that.
Thank you for your help.
You can use Set-Variable in a loop to put each value in a separate variable:
$i = 0
... | Get-Vm | Select-Objet -Expand Name | ForEach-Object {
Set-Variable -Name "vm$i" -Value $_
$i++
}
However, that usually isn't good advice. It's more common to put all names in one (array) variable:
$vmList = ...| Get-Vm | Select-Object -Expand Name
so you can access individual names via $vmList[<index>], or (if you need access by some kind of name) in a hashtable:
$i = 0
$vmList = #{}
... | Get-Vm | Select-Objet -Expand Name | ForEach-Object {
$vmList["vm$i"] = $_
$i++
}
Best practice would depend on the particular scenario you need this for, though.
Thank you for your reply,
I have tried you answer but it seems that i am using PowerCLI for VMware it does not include Select-Object -Expand (not sure i had an exception), However your answer have mad me reach to a suitable answer for this.
I have used the below and it worked fine using foreach and adding the values in an array and then reading them as below:
$p1vmname = Get-VMHost -Name hq-esxi-prod-01a.nsx.gss | Get-VM | select Name
$p1vmlist = #()
foreach ($p1line in $p1vmname)
{
$p1vmlist += $p1line
}
$p1 = 0
do {
$x = $p1+1
Write-Host -BackgroundColor:Black -ForegroundColor:Yellow "vm number $x is "$p1vmlist[$p1]"."
$p1++
}
until ($p1 -eq $p1vmc)
}
However when using this the names was not totally correct as they had some additional characters as below:
vm number 1 is #{Name=HQ-Web-01a}
vm number 2 is #{Name=HQ-LinServ-01a}
vm number 3 is #{Name=HQ-Win2012-01a}
so i used split and trim to get rid of these as below and worked fine.
$p1vmname = Get-VMHost -Name hq-esxi-prod-01a.nsx.gss | Get-VM | select Name
$p1vmlist = #()
foreach ($p1line in $p1vmname)
{
$p1vmlist += $p1line
}
$p1 = 0
do {
$x = $p1+1
$p1vmlist[$p1] = ($p1vmlist[$p1]) -split("=") | Select-Object -Last 1
$p1vmlist[$p1] = $p1vmlist[$p1].trimend("}")
Write-Host -BackgroundColor:Black -ForegroundColor:Yellow "vm number $x is "$p1vmlist[$p1]"."
$p1++
}
until ($p1 -eq $p1vmc)
}
Output:
vm number 1 is HQ-Web-01a .
vm number 2 is HQ-LinServ-01a .
vm number 3 is HQ-Win2012-01a .
Thank you so much for your answer that helped me a lot.
I am really enjoying scripting now.

Resolve-DNSName for Windows 2008

I have a script working for Windows 2012 (PowerShell v4) but it has to work also for Windows 2008 (PowerShell v2), what is the equivalent of the cmdlet "Resolve-DNSName" for Windows 2008?
Resolve-DnsName -Name client01 -Server server01
I know it exists the same for nslookup and this is what I would like as a cmdlet (one-liner, with no input required from my part)
nslookup
server server01
client01
The following works for DNS resolution but is missing the -server parameter :
[Net.DNS]::GetHostEntry("MachineName")
Thanks
Unfortunately there isn't a way to do this natively in powershell prior to Version 4 in Windows 8.1 or Server 2012. There are .NET methods however:
https://stackoverflow.com/a/8227917/4292988
The simplest solution in powershell is to call nslookup, and cleanup the output
&nslookup.exe client01 server01
I removed select-string from the original sample, it left less to work with
The function you posted following mine doesnt work very well, and will never work in PowershellV2, [PSCustomObject] wasn't supported until v3. Furthermore if you send a dns query that would normally return a single address, it returns nothing. For queries with aliases, it returns the aliases where the ipaddress should be. Test Resolve-DnsName2008 -name www.stackoverflow.com -server 8.8.8.8.
The Following is a function that should do what your asking, at least for ipv4addresses:
function Resolve-DnsName2008
{
Param
(
[Parameter(Mandatory=$true)]
[string]$Name,
[string]$Server = '127.0.0.1'
)
Try
{
$nslookup = &nslookup.exe $Name $Server
$regexipv4 = "^(?:(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)\.){3}(?:0?0?\d|0?[1-9]\d|1\d\d|2[0-5][0-5]|2[0-4]\d)$"
$name = #($nslookup | Where-Object { ( $_ -match "^(?:Name:*)") }).replace('Name:','').trim()
$deladdresstext = $nslookup -replace "^(?:^Address:|^Addresses:)",""
$Addresses = $deladdresstext.trim() | Where-Object { ( $_ -match "$regexipv4" ) }
$total = $Addresses.count
$AddressList = #()
for($i=1;$i -lt $total;$i++)
{
$AddressList += $Addresses[$i].trim()
}
$AddressList | %{
new-object -typename psobject -Property #{
Name = $name
IPAddress = $_
}
}
}
catch
{ }
}
I use this code to input FQDNs one per line and output respective IPs.
$Server = Get-Content servers.txt
$OutArray = #()
$output = foreach ($Server in $Server) {
$IP = [System.Net.Dns]::GetHostAddresses($Server)
$OutArray += $Server + " " + $IP.IPAddressToString
}
$OutArray | Out-File IPs.txt
The problem is that if I use :
&nslookup.exe client01 server01 | select-string "Name", "Addresses"
It will only display the first record, in my case I had 5 records found and only one displayed.
The solution I found works very well :
function Resolve-DNSName2008
{
Param
(
[string]$Name,
[string]$Server
)
$nslookup = &nslookup.exe $Name $Server
$name = [string]($nslookup | Select-String "Name")
$nameClean = ([regex]::match($name,'(?<=:)(.*\n?)').value).Trim()
$addresses = (([regex]::match($nslookup,'(?<=Addresses:)(.*\n?)').value).Trim()).Split(' ')
$addressesClean = $addresses.Split('',[System.StringSplitOptions]::RemoveEmptyEntries) | Sort-Object
$addressesClean | %{
[PSCustomObject]#{
Name = $nameClean
IPAddress = $_
}
}
}
Usage:
Resolve-DNSName2008 -Name server.domain.com -Server 10.0.0.0
Output:
Name IPAddress
---- ---------
server.domain.com 10.0.0.1
server.domain.com 10.0.0.2
server.domain.com 10.0.0.3
server.domain.com 10.0.0.4
server.domain.com 10.0.0.5

Issue in Power shell script while using Ping command

I am trying to read the host file and trying to ping the each host name and after that capturing the IP address in the response and trying to match with the IP address mentioned in the host file.
I have three scenarios:-
1) Its pinging the host and getting the reply back with the correct IP
Result :-Resolved and Replied
2) It's Not pinging at all and not resolving the IP
Result :-Not Resolved and Not Replied
3) It's Pinging but not resolving the IP correctly mentioned to the IP in the host file
Result :-Not Resolved and Replied
I am trying to achieve that scenario with the below script but not fully achieved as different expression need to be used.
Can someone help me to finish it
$lines = Get-Content myfile.txt | Where {$_ -notmatch "((^#)|(^\s+$))"}
# get all test pairs
$tests=$lines |
ForEach-Object{
$fields = $_ -split '\s+'
echo " Fields are $fields"
for ($i = 1; $i -lt $fields.Length; $i++){
New-Object PsObject -Property #{IP=$fields[0];Host=$fields[$i]}
}
}
$tests |
ForEach-Object{
$props=#{
IPAddress=$_.ip
Hostname=$_.Host
Resolve =' '
Reply = ' '
}
$PingResult = ping -n 1 -w 10 $_.host
#echo "$PingResult"
foreach ($line in $PingResult)
{
if ($line.StartsWith("Pinging") -eq $true)
{
$_.ip= $line -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
echo "IP is $IP"
if ($matches[0])
{
$props.Resolve ='Y'
$props.Reply='Y'
}
else
{
$props.Resolve ='Y'
$props.Reply='N'
}
}
}New-Object PsCustomObject -Property $props
}|
Format-Table -AutoSize | Out-String | Out-File D:\CEP\testlogging.txt
Note:- Cannot use Test-Connection because it throws exception when server wont reply back or doesnot exist and it takes more time to ping.
Thanks.
Suppose it's too late to be of much help but this should resolve the issues you are reporting with Test-Connection.
Test-Connection -ComputerName $IP -Count 1 -ErrorAction SilentlyContinue
That will limit the ping count to 1 and error will be ignored. Then you can work with the object it produces instead of having to parse lines.