Compare-object output in variable - powershell

I'm building a script that will compare the last octed of in-use IPv4 addreses, with all the available octeds (2 till 254).
I am already this far that I do get a result by comparing array's, but my end-result is also an array, and I cannot seem to get only the number.
My script:
$guestIP = #("192.168.31.200","192.168.31.31","192.168.31.90","192.168.31.25","192.168.31.100")
$AllLastOcted = $("2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70","71","72","73","74","75","76","77","78","79","80","81","82","83","84","85","86","87","88","89","90","91","92","93","94","95","96","97","98","99","100","101","102","103","104","105","106","107","108","109","110","111","112","113","114","115","116","117","118","119","120","121","122","123","124","125","126","127","128","129","130","131","132","133","134","135","136","137","138","139","140","141","142","143","144","145","146","147","148","149","150","151","152","153","154","155","156","157","158","159","160","161","162","163","164","165","166","167","168","169","170","171","172","173","174","175","176","177","178","179","180","181","182","183","184","185","186","187","188","189","190","191","192","193","194","195","196","197","198","199","200","201","202","203","204","205","206","207","208","209","210","211","212","213","214","215","216","217","218","219","220","221","222","223","224","225","226","227","228","229","230","231","232","233","234","235","236","237","238","239","240","241","242","243","244","245","246","247","248","249","250","251","252","253","254")
$guestIP = $guestIP | sort -Property {$_-replace '[\d]'},{$_-replace '[a-zA-Z\p{P}]'-as [int]}
$AllLastOcted = $AllLastOcted | sort -Property {$_-replace '[\d]'},{$_-replace '[a-zA-Z\p{P}]'-as [int]}
$guestIP = $guestIP -replace('192.168.31.','')
Compare-Object -ReferenceObject ($AllLastOcted ) -DifferenceObject ($guestIP) | select -Property InputObject -First 1 | set-Variable AvailableOcted -PassThru
$AvailableOcted
My goal is, is that I have as result the first-available octed that I can use.
like:
write-host "IP that can be used is 192.168.31.$AvailableOcted"
PS > IP that can used is 192.168.31.2

You can simplify this a lot.
Instead of defining all numbers from 2 to 254 you can use the range operator to create the array. You also don't need the [int] casts. Instead of using the Compare-Object cmdlet to filter the octeds, you can use the Where-Object cmdlet:
$guestIP = #("192.168.31.200","192.168.31.31","192.168.31.90","192.168.31.25","192.168.31.100")
$AllLastOcted = 2 .. 254
$usedOcted = $guestIP -replace '.*\.'
$nextAvailableOcted = $AllLastOcted | Where { $_ -NotIn $usedOcted } | select -first 1
write-host "IP that can be used is 192.168.31.$nextAvailableOcted"
Output:
IP that can be used is 192.168.31.2

Well, as simple as:
$AvailableOcted.InputObject
would return only the value.
So it would look like this:
write-host ("IP that can be used is 192.168.31." + $AvailableOcted.InputObject)

Related

Select all unique third octet in a list of IP addresses with PowerShell

I have a list of IP addresses. They all start with 10.10. I want all the unique values of the third octet. This way I can count how many of that unique value there are.
10.10.26.251
10.10.27.221
10.10.26.55
10.10.31.12
10.10.12.31
10.10.31.11
10.10.27.15
10.10.26.5
When I am done I want to know that I have 3 .26 network devices, 2 27, and so on so forth. Other than breaking down the octet with a split and looping through each one, I can't think of any single liners. Any suggestions?
here's a small variant. [grin] i already had this before noticing the other answers - and it is a tad different.
what it does ...
creates a collection of IPv4 address objects to work with
groups them by a calculated property [the 3rd octet]
creates a [PSCustomObject] for each resulting group
sends it to the $Octet3_Report variable
shows it on screen
output to a CSV file would be easy at that point. here's the code ...
$IP_List = #(
[ipaddress]'10.10.26.251'
[ipaddress]'10.10.27.221'
[ipaddress]'10.10.26.55'
[ipaddress]'10.10.31.12'
[ipaddress]'10.10.12.31'
[ipaddress]'10.10.31.11'
[ipaddress]'10.10.27.15'
[ipaddress]'10.10.26.5'
)
$Octet3_Report = $IP_List |
Group-Object -Property {$_.ToString().Split('.')[2]} |
ForEach-Object {
[PSCustomObject]#{
Octet_3 = $_.Name
Count = $_.Count
}
}
$Octet3_Report
on screen output ...
Octet_3 Count
------- -----
26 3
27 2
31 2
12 1
It's like me to figure it out after the fact.
The Return contains the dns records. The IP address are stored inside recorddata. I pull the end of the IP address off. Then loop through grabbing only the range and count with a foreach loop to make it cleaner.
$DNSRecordCounts = #()
$Ranges = ($Return | where-object {$_.recorddata -like "10.10.*"}).recorddata -replace "\.\d{1,3}$" | select -Unique
foreach ($range in $Ranges) {
$DNSRecordCounts += [pscustomobject][ordered]#{
IPRange = $range
Count = ($Return | Where-Object {$_.recorddata -like "$($range).*"}).Count
}
}
Based on your question and what I can infer from your own answer, if you are looking for something a little more like "idiomatic" PowerShell you want the following:
$Return `
| Select-Object -ExpandProperty recorddata `
| ForEach-Object {
$_ -match "\d+\.\d+\.(?<octet>\d+)\.\d+" | Out-Null
$Matches.octet
} `
| Group-Object `
| ForEach-Object {
[PSCustomObject]#{
Octet = $_.Name
Count = $_.Count
}
}

How do I join two lines in PowerShell

I'm trying to get Information about our VMs in Hyper-V via PowerShell.
This is what I got so far:
$Path = 'c:/VM.csv'
"Name,CPUs,Dynamischer Arbeitsspeicher,RAM Maximum (in MB),RAM Minimum (in MB), Size" > $Path
$line1 = Get-VM | %{($_.Name, $_.ProcessorCount, $_.DynamicMemoryEnabled, ($_.MemoryMaximum/1MB), ($_.MemoryMinimum/1MB)) -join ","}
$line2 = Get-VM –VMName * | Select-Object VMId | Get-VHD | %{$_.FileSize/1GB -join ","}
$out = $line1+","+ $line2
Write-Output $out | Out-File $Path -Append
Import-Csv -Path $Path | Out-GridView
The Problem is that the second object ($line2) should be in the same column as $line1. As you can see, currently the information about the size of the VMs ($line2) is written in rows under the output of $line1. Also the order is wrong.
Any idea what is wrong with my code?
Thanks in advance.
I think you messed it up a little,
Export-CSV will do the job without the need to manually define the csv structure.
Anyway, regarding your code I think you can improve it a little, (I don't have hyper-v to test this, but I think it should work)
What I've done is create a results array to hold the final data, then using foreach loop i'm iterating the Get-VM Results and creating a row for each VM, at the end of each iteration I'm adding the row to the final results array, so:
$Results = #()
foreach ($VM in (Get-VM))
{
$Row = "" | Select Name,CPUs,'Dynamischer Arbeitsspeicher','RAM Maximum (inMB)','RAM Minimum (in MB)',Size
$Row.Name = $VM.Name
$Row.CPUs = $VM.ProcessorCount
$Row.'Dynamischer Arbeitsspeicher' = $VM.DynamicMemoryEnabled
$Row.'RAM Maximum (inMB)' = $VM.MemoryMaximum/1MB
$Row.'RAM Minimum (in MB)' = $VM.MemoryMinimum/1MB
$Total=0; ($VM.VMId | Get-VHD | %{$Total += ($_.FileSize/1GB)})
$Row.Size = [math]::Round($Total)
$Results += $Row
}
$Results | Export-Csv c:\vm.csv -NoTypeInformation
This is definitely not the proper way to build an object list, instead you should do something like this:
Get-VM | Select Name,
#{Name = "CPUs"; Expression = {$_.ProcessorCount},
#{Name = "Dynamischer Arbeitsspeicher"; Expression = {$_.DynamicMemoryEnabled},
#{Name = "RAM Maximum (in MB)"; Expression = {$_.MemoryMaximum/1MB},
#{Name = "RAM Minimum (in MB)"; Expression = {$_.MemoryMinimum/1MB},
#{Name = "Size"; Expression = {$_.VMId | Get-VHD | %{$_.FileSize/1GB -join ","}} |
Export-Csv c:\vm.csv -NoTypeInformation

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.

Sort by ascending number order

$var_1text = $var_2text = $var_17text = $null
Get-Variable -Name var_*
I get the following output : 1-17-2
var_1text
var_17text
var_2text
But I want the following output : 1-2-17
var_1text
var_2text
var_17text
Use the Sort-Object cmdlet:
Get-Variable -Name var_* |Sort-Object { ($_.Name -replace "[^\d]","") -as [int] }
Along the same lines
Get-Variable -Name var_* | Select-Object *,#{L="NameIndex";E={[void]($_.Name -match '\d+');[int]$Matches[0]}} | Sort-Object NameIndex
You can create a calculated property that only contains the number portion. Cast it to [int] and sort on that property. This would be especially useful if you need to refer to this more than once in the code as supposed to recalling the regex.

PowerShell - filtering for unique values

I have an input CSV file with a column containing information similar to the sample below:
805265
995874
805674
984654
332574
339852
I'd like to extract unique values into a array based on the leading two characters, so using the above sample my result would be:
80, 99, 98, 33
How might I achieve this using PowerShell?
Use Select-Object and parameter -unique:
$values =
'805265',
'995874',
'805674',
'984654',
'332574',
'339852'
$values |
Foreach-Object { $_.Substring(0,2) } |
Select-Object -unique
If conversion to int is needed, then just cast it to [int]:
$ints =
$values |
Foreach-Object { [int]$_.Substring(0,2) } |
Select-Object -unique
I'd use the Group-Object cmdlet (alias group) for this:
Import-Csv foo.csv | group {$_.ColumnName.Substring(0, 2)}
Count Name Group
----- ---- -----
2 80 {805265, 805674}
1 99 {995874}
1 98 {984654}
2 33 {332574, 339852}
You might use a hash table:
$values = #(805265, 995874, 805674, 984654, 332574, 339852)
$ht = #{}
$values | foreach {$ht[$_ -replace '^(..).+','$1']++}
$ht.keys
99
98
33
80
You could make a new array with items containing the first two characters and then use Select-Object to give you the unique items like this:
$newArray = #()
$csv = Import-Csv -Path C:\your.csv
$csv | % {
$newArray += $_.YourColumn.Substring(0, 2)
}
$newArray | Select-Object -Unique
Just another option instead of using Select-Object -unique would be to use the Get-Unique cmdlet (or its alias gu; see the detailed description here) as demonstrated below:
$values = #(805265, 995874, 805674, 984654, 332574, 339852)
$values | % { $_.ToString().Substring(0,2) } | Get-Unique
# Or the same using the alias
$values | % { $_.ToString().Substring(0,2) } | gu