Comparing two VMHostVirtualSwitches that always show equal - powershell

I am comparing a predetermined object (VMHostVirtualSwitch Name) value with all object(VMHostVirtualSwitch Names) values within a collection of objects and want the status to be "FAIL" if the objects don't match
I have written the following code so far but it doesn't seem to be working. I know the objects don't match and I should get "FAIL" as an output
$VMHostVirtualSwitch = Get-VMHostNetwork -VMHost abc.com | Select-Object VirtualSwitch*
$Cluster = Get-Cluster -VMHost abc.com
$VMHosts = Get-Cluster $Cluster | Get-VMHost
[int]$Switchcount=0
foreach ($VMHost in $VMHosts){
$CurrentHostVirtualSwitch = Get-VMHostNetwork -VMHost $VMHost | Select-Object VirtualSwitch*
if ($CurrentHostVirtualSwitch -ne $VMHostVirtualSwitch) {
$Switchcount++
}
}
if($Switchcount -ge 1) {
Write-Output "FAIL"
}
$VMHostVirtualSwitch has the following value
VirtualSwitch
-------------
{vSwitch3}
When I expand the $VMHostVirtualSwitch , I get the following values
Name NumPorts Mtu Notes
---- -------- --- -----
vSwitch3 10562 2340

You problem is PowerShell does not know how to compare those objects. Even if they had the same data they are technically two different objects (a blog post touches on this subject). At the end of the day if you are just comparing the names then do your comparison on just those.
$VMHostVirtualSwitch = (Get-VMHostNetwork -VMHost abc.com).VirtualSwitch.Name
$Cluster = Get-Cluster -VMHost abc.com
$VMHosts = Get-Cluster $Cluster | Get-VMHost
[int]$Switchcount=0
foreach ($VMHost in $VMHosts){
$CurrentHostVirtualSwitch = (Get-VMHostNetwork -VMHost $VMHost).VirtualSwitch.Name
if ($CurrentHostVirtualSwitch -ne $VMHostVirtualSwitch) {
$Switchcount++
}
}
if($Switchcount -ge 1) {
Write-Output "FAIL"
}
Now you should just be comparing strings which will get you more predictable results. I have only change the variable expansion in the above example. You might have some error checking to do to account for.
Something like this might be shorter then your loop
$badHosts = $VMHosts | Where-Object{(Get-VMHostNetwork -VMHost $_).VirtualSwitch.Name -ne $VMHostVirtualSwitch}
if($badHosts.count -ge 1) {
Write-Output "FAIL"
}
Compare-Object would also be a way to go for this, especially if there was multiple properties you were comparing: example. Since we are boiling down to simple strings I think what I propose should suffice.

Related

powershell winform searchbox shows results incorrect [duplicate]

I'm using Powershell to set up IIS bindings on a web server, and having a problem with the following code:
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if ($serverIps.length -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
$primaryIp = $serverIps[0]
$secondaryIp = $serverIps[1]
If there's 2+ IPs on the server, fine - Powershell returns an array, and I can query the array length and extract the first and second addresses just fine.
Problem is - if there's only one IP, Powershell doesn't return a one-element array, it returns the IP address (as a string, like "192.168.0.100") - the string has a .length property, it's greater than 1, so the test passes, and I end up with the first two characters in the string, instead of the first two IP addresses in the collection.
How can I either force Powershell to return a one-element collection, or alternatively determine whether the returned "thing" is an object rather than a collection?
Define the variable as an array in one of two ways...
Wrap your piped commands in parentheses with an # at the beginning:
$serverIps = #(gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort)
Specify the data type of the variable as an array:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
Or, check the data type of the variable...
IF ($ServerIps -isnot [array])
{ <error message> }
ELSE
{ <proceed> }
Force the result to an Array so you could have a Count property. Single objects (scalar) do not have a Count property. Strings have a length property so you might get false results, use the Count property:
if (#($serverIps).Count -le 1)...
By the way, instead of using a wildcard that can also match strings, use the -as operator:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" | Select-Object -ExpandProperty IPAddress | Where-Object {($_ -as [ipaddress]).AddressFamily -eq 'InterNetwork'}
You can either add a comma(,) before return list like return ,$list or cast it [Array] or [YourType[]] at where you tend to use the list.
If you declare the variable as an array ahead of time, you can add elements to it - even if it is just one...
This should work...
$serverIps = #()
gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort | ForEach-Object{$serverIps += $_}
You can use Measure-Object to get the actual object count, without resorting to an object's Count property.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if (($serverIps | Measure).Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
Return as a referenced object, so it never converted while passing.
return #{ Value = #("single data") }
I had this problem passing an array to an Azure deployment template. If there was one object, PowerShell "converted" it to a string. In the example below, $a is returned from a function that gets VM objected according to the value of a tag. I pass the $a to the New-AzureRmResourceGroupDeployment cmdlet by wrapping it in #(). Like so:
$TemplateParameterObject=#{
VMObject=#($a)
}
New-AzureRmResourceGroupDeployment -ResourceGroupName $RG -Name "TestVmByRole" -Mode Incremental -DeploymentDebugLogLevel All -TemplateFile $templatePath -TemplateParameterObject $TemplateParameterObject -verbose
VMObject is one of the template's parameters.
Might not be the most technical / robust way to do it, but it's enough for Azure.
Update
Well the above did work. I've tried all the above and some, but the only way I have managed to pass $vmObject as an array, compatible with the deployment template, with one element is as follows (I expect MS have been playing again (this was a report and fixed bug in 2015)):
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
foreach($vmObject in $vmObjects)
{
#$vmTemplateObject = $vmObject
$asJson = (ConvertTo-Json -InputObject $vmObject -Depth 10 -Verbose) #-replace '\s',''
$DeserializedJson = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property #{MaxJsonLength=67108864}).DeserializeObject($asJson)
}
$vmObjects is the output of Get-AzureRmVM.
I pass $DeserializedJson to the deployment template' parameter (of type array).
For reference, the lovely error New-AzureRmResourceGroupDeployment throws is
"The template output '{output_name}' is not valid: The language expression property 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Expression.Expressions.JTokenExpression'
can't be evaluated.."
There is a way to deal with your situation. Leave most of you code as-is, just change the way to deal with the $serverIps object. This code can deal with $null, only one item, and many items.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
# Always use ".Count" instead of ".Length".
# This works on $null, only one item, or many items.
if ($serverIps.Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
# Always use foreach on a array-possible object, so that
# you don't have deal with this issue anymore.
$serverIps | foreach {
# The $serverIps could be $null. Even $null can loop once.
# So we need to skip the $null condition.
if ($_ -ne $null) {
# Get the index of the array.
# The #($serverIps) make sure it must be an array.
$idx = #($serverIps).IndexOf($item)
if ($idx -eq 0) { $primaryIp = $_ }
if ($idx -eq 1) { $secondaryIp = $_ }
}
}
In PowerShell Core, there is a .Count property exists on every objects. In Windows PowerShell, there are "almost" every object has an .Count property.

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.

Powershell DNS forward lookup zone list then check

Ok so we are migrating to a new NS for all of our external domains. The current one has plenty of domains that are no longer valid but have not been removed. I am attempting to export a list of all forward lookup zones in DNS, ping them to see if they are alive and sort too two different files so i can recheck the bad names manually.
First need to export to a file so it can be moved to a different location for testing.
Export
dnscmd /enumzones /Forward | out-file C:\temp\zones.txt
Test
$names = Get-Content "C:\temp\zones.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
Add-Content c:\temp\resolved.csv "$name,up"
}
else{
Add-Content C:\temp\unresolved.csv "$name,down"
}
}
Problem
The export command writes a value with extra information.
I.E.
Enumerated zone list:
Zone count = 673
Zone name Type Storage Properties
. Cache File
domain1.com.au Primary File
domain2.co.nz Primary File
domain3.com.au Primary File
No problem with just clearing out the top of the file etc, but how can I format the zone list output so the powershell can read it?
Answer
Answer for Server 2012 marked below by #sodawillow, I had to teak a little for 2008R2, but couldn't of done it with out his answer.
I also messed with it a little as i was having issues with none of the names resolving due to white space from export.
#store forward lookup zones names in an array
$zoneNames = Get-WmiObject -Namespace Root\MicrosoftDNS -Class "MicrosoftDNS_Zone" | ? { $_.ContainerName -Notlike '..RootHints' -And $_.ContainerName -NotLike '..Cache' -And !$_.Reverse } | Select Name
#declare results arrays and files paths
$resolvedZones = #()
$unresolvedZones = #()
$resolvedFile = "C:\Temp\resolved.csv"
$unresolvedFile = "C:\Temp\unresolved.csv"
#for each zone name
foreach ($zoneName in $zoneNames){
$string = $zoneName.Substring(0)
$string = $string.Trim()
#if it responds to ping
if (Test-Connection -ComputerName "$string" -Count 2 -ErrorAction SilentlyContinue) {
#build result object and add it to resolved zones array
$resolvedZones += [PSCustomObject]#{ ZoneName = $zoneName; Status = "UP" }
} else {
#build result object and add it to unresolved zones array
$unresolvedZones += [PSCustomObject]#{ ZoneName = $zoneName; Status = "DOWN" }
}
}
#export results arrays as CSV in results files (if not empty)
if($unresolvedZones.Length -gt 0) { $unresolvedZones | Export-Csv $unresolvedFile -NoTypeInformation }
if($resolvedZones.Length -gt 0) { $resolvedZones | Export-Csv $resolvedFile -NoTypeInformation }
Note : I have mixed forward and reverse in my answer, but it
demonstrates how you can retrieve and re-use DNS zone names.
There is an appropriate PowerShell cmdlet to list DNS zones, Get-DNSServerZone.
Here is an adapted script :
#store reverse lookup zones names in an array
$zoneNames = (Get-DnsServerZone | Where-Object { $_.IsReverseLookupZone -eq $true }).ZoneName
#declare results arrays and files paths
$resolvedZones = #()
$unresolvedZones = #()
$resolvedFile = "C:\Temp\resolved.csv"
$unresolvedFile = "C:\Temp\unresolved.csv"
#for each zone name
foreach ($zoneName in $zoneNames){
#if it responds to ping
if (Test-Connection -ComputerName $zoneName -Count 2 -ErrorAction SilentlyContinue) {
#build result object and add it to resolved zones array
$resolvedZones += [PSCustomObject]#{ ZoneName = $zoneName; Status = "UP" }
} else {
#build result object and add it to unresolved zones array
$unresolvedZones += [PSCustomObject]#{ ZoneName = $zoneName; Status = "DOWN" }
}
}
#export results arrays as CSV in results files (if not empty)
if($unresolvedZones.Length -gt 0) { $unresolvedZones | Export-Csv $unresolvedFile -NoTypeInformation }
if($resolvedZones.Length -gt 0) { $resolvedZones | Export-Csv $resolvedFile -NoTypeInformation }

powershell match multiple variables

So I read stackoverflow a lot and it always helps me with my understanding, at the moment, I'm a bit stumped on the matching any of multiple variables and as such, thought I'd ask the exports.
A bit of background on my task.
I am a systems admin for an AD forest with multiple child domains (+4), we are going through a process to consolidate these domains into one - however, unfortunately, as each was managed independently we have comes across the age old 'duplicate SAM' issue (and will for sure come across dupe UPNs too once we start digging).
As such - I am trying to build a script to find these duplicates so I can address them with the teams who support the users in these domains.
I've got a fair way through this, I've exported a list of the SAMs for one migrating domain, and I can parse this into PS and have it look up each SAM against all of my domains and either give me a variable with the username OR $null.
So, I now just want to understand what the 'IF' command is for matching more than one variable and showing which ones match.
So, for example, from these variables...
$one="MSmith01"
$oneNT="DOMAIN1\MSmith01"
$two="MSmith01"
$twoNT="DOMAIN2\MSmith01"
$three=$null
$threeNT=$null
$four=$null
$fourNT=$null
$five="MSmith01"
$fiveNT="DOMAIN5\MSmith01"
$six=$null
$sixNT=$null
How do I write the IF command to show me if more than one variable matches??
If I write this:
if ($one -match $two -match $three -match $four -match $five -match $six) {write-host "Duplicate Found - $oneNT, $twoNT, $threeNT, $fourNT, $fiveNT, $sixNT." -foregroundcolor "yellow"}
It will show me :
Duplicate Found - DOMAIN1\MSmith01, DOMAIN2\MSmith01, , , DOMAIN5\MSmith01,.
But, if the variables are:
$one=$null
$oneNT=$null
$two="MSmith01"
$twoNT="DOMAIN2\MSmith01"
$three="MSmith01"
$threeNT="DOMAIN3\MSmith01"
$four=$null
$fourNT=$null
$five=$null
$fiveNT=$null
$six=$null
$sixNT=$null
Then the result seems to ignore the fact that DOMAIN 2 and DOMAIN3 have matching SAMs and it appears that "x -match y -match z" is really actually only comparing x and y, so I get nothing.
Which is obviously incorrect.
Can anyone explain how I compare and look for ANY duplicate amongst several variables?
Thanks
To further elaborate:
To get my variables I'm doing this:
$SAMAccountList=gc "C:\My Documents\Domain2SAMList.txt"
$SamAccountList | % {
$one=Get-QADUser -SamAccountName $_ -IncludedProperties SamAccountName -Server dc.domain1.local | foreach { $_.SAMAccountName }
$oneNT=Get-QADUser -SamAccountName $_ -IncludedProperties NTAccountName -Service dc.domain1.local | foreach { $_.NTAccountName }
$twoSAM=Get-QADUser -SamAccountName $_ -IncludedProperties SamAccountName -Service dc.domain2.local | foreach { $_.SAMAccountName }
$twoNT=Get-QADUser -SamAccountName $_ -IncludedProperties NTAccountName -Service dc.domain2.local | foreach { $_.NTAccountName }
#...
#...
#...etc for each domain
}
This then gives me a set of variables as I mentioned above
I would put them in a customobject and search with the unique names.
$NAMES = #()
$NAMES += [pscustomobject] #{name=$one;nameNT=$oneNT}
$NAMES += [pscustomobject] #{name=$two;nameNT=$twoNT}
$NAMES += [pscustomobject] #{name=$three;nameNT=$threeNT}
$NAMES += [pscustomobject] #{name=$four;nameNT=$fourNT}
$NAMES += [pscustomobject] #{name=$five;nameNT=$fiveNT}
$NAMES += [pscustomobject] #{name=$six;nameNT=$sixNT}
$UniqueNames=($NAMES | Sort-Object -Property name -Unique).Name
ForEach($Uniquename in $UniqueNames){
if($Uniquename -ne $NULL){
$SEARCH_RESULT=$NAMES|?{$_.Name -eq $Uniquename}
if ( $SEARCH_RESULT.Count -gt 1)
{
$SEARCH_RESULT
}
}
}
I get the following results
First data set
name nameNT
---- ------
MSmith01 DOMAIN1\MSmith01
MSmith01 DOMAIN2\MSmith01
MSmith01 DOMAIN5\MSmith01
Second data set
name nameNT
---- ------
MSmith01 DOMAIN2\MSmith01
MSmith01 DOMAIN3\MSmith01

How can I force Powershell to return an array when a call only returns one object?

I'm using Powershell to set up IIS bindings on a web server, and having a problem with the following code:
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if ($serverIps.length -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
$primaryIp = $serverIps[0]
$secondaryIp = $serverIps[1]
If there's 2+ IPs on the server, fine - Powershell returns an array, and I can query the array length and extract the first and second addresses just fine.
Problem is - if there's only one IP, Powershell doesn't return a one-element array, it returns the IP address (as a string, like "192.168.0.100") - the string has a .length property, it's greater than 1, so the test passes, and I end up with the first two characters in the string, instead of the first two IP addresses in the collection.
How can I either force Powershell to return a one-element collection, or alternatively determine whether the returned "thing" is an object rather than a collection?
Define the variable as an array in one of two ways...
Wrap your piped commands in parentheses with an # at the beginning:
$serverIps = #(gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort)
Specify the data type of the variable as an array:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
Or, check the data type of the variable...
IF ($ServerIps -isnot [array])
{ <error message> }
ELSE
{ <proceed> }
Force the result to an Array so you could have a Count property. Single objects (scalar) do not have a Count property. Strings have a length property so you might get false results, use the Count property:
if (#($serverIps).Count -le 1)...
By the way, instead of using a wildcard that can also match strings, use the -as operator:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" | Select-Object -ExpandProperty IPAddress | Where-Object {($_ -as [ipaddress]).AddressFamily -eq 'InterNetwork'}
You can either add a comma(,) before return list like return ,$list or cast it [Array] or [YourType[]] at where you tend to use the list.
If you declare the variable as an array ahead of time, you can add elements to it - even if it is just one...
This should work...
$serverIps = #()
gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort | ForEach-Object{$serverIps += $_}
You can use Measure-Object to get the actual object count, without resorting to an object's Count property.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if (($serverIps | Measure).Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
Return as a referenced object, so it never converted while passing.
return #{ Value = #("single data") }
I had this problem passing an array to an Azure deployment template. If there was one object, PowerShell "converted" it to a string. In the example below, $a is returned from a function that gets VM objected according to the value of a tag. I pass the $a to the New-AzureRmResourceGroupDeployment cmdlet by wrapping it in #(). Like so:
$TemplateParameterObject=#{
VMObject=#($a)
}
New-AzureRmResourceGroupDeployment -ResourceGroupName $RG -Name "TestVmByRole" -Mode Incremental -DeploymentDebugLogLevel All -TemplateFile $templatePath -TemplateParameterObject $TemplateParameterObject -verbose
VMObject is one of the template's parameters.
Might not be the most technical / robust way to do it, but it's enough for Azure.
Update
Well the above did work. I've tried all the above and some, but the only way I have managed to pass $vmObject as an array, compatible with the deployment template, with one element is as follows (I expect MS have been playing again (this was a report and fixed bug in 2015)):
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
foreach($vmObject in $vmObjects)
{
#$vmTemplateObject = $vmObject
$asJson = (ConvertTo-Json -InputObject $vmObject -Depth 10 -Verbose) #-replace '\s',''
$DeserializedJson = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property #{MaxJsonLength=67108864}).DeserializeObject($asJson)
}
$vmObjects is the output of Get-AzureRmVM.
I pass $DeserializedJson to the deployment template' parameter (of type array).
For reference, the lovely error New-AzureRmResourceGroupDeployment throws is
"The template output '{output_name}' is not valid: The language expression property 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Expression.Expressions.JTokenExpression'
can't be evaluated.."
There is a way to deal with your situation. Leave most of you code as-is, just change the way to deal with the $serverIps object. This code can deal with $null, only one item, and many items.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
# Always use ".Count" instead of ".Length".
# This works on $null, only one item, or many items.
if ($serverIps.Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
# Always use foreach on a array-possible object, so that
# you don't have deal with this issue anymore.
$serverIps | foreach {
# The $serverIps could be $null. Even $null can loop once.
# So we need to skip the $null condition.
if ($_ -ne $null) {
# Get the index of the array.
# The #($serverIps) make sure it must be an array.
$idx = #($serverIps).IndexOf($item)
if ($idx -eq 0) { $primaryIp = $_ }
if ($idx -eq 1) { $secondaryIp = $_ }
}
}
In PowerShell Core, there is a .Count property exists on every objects. In Windows PowerShell, there are "almost" every object has an .Count property.