Search code examples
powershellpowercli

How can I stop iterating through entire list multiple times in ForEach?


How can I apply 1 IP address in a list to 1 server in another list? Then move onto the next IP and apply it to the next server.

servers.txt looks like:

server1
server2
server3

ip.txt looks like:

10.1.140.80
10.1.140.81
10.1.140.83

I just want to go through the list and apply

10.1.140.80 to server1
10.1.140.81 to server2
10.1.140.83 to server3

Instead, my script is applying all 3 IP addresses to each server. I dont want to cycle through all IP addresses over and over.

How can I iterate through the lists properly and correct this?

$computers = "$PSScriptRoot\servers.txt"
$iplist        = gc "$PSScriptRoot\ip.txt"

function changeip {
    get-content $computers | % {
        ForEach($ip in $iplist) {

            # Set IP address
            $remotecmd1 = 'New-NetIPAddress -InterfaceIndex 2 -IPAddress $ip -PrefixLength 24 -DefaultGateway 10.1.140.1'

            # Set DNS Servers  -  Make sure you specify the server's network adapter name at -InterfaceAlias
            $remotecmd2 = 'Set-DnsClientServerAddress -InterfaceAlias "EthernetName" -ServerAddresses 10.1.140.5, 10.1.140.6'

            Invoke-VMScript -VM $_ -ScriptText $remotecmd1 -GuestUser Administrator -GuestPassword PASSWORD -ScriptType PowerShell
            Invoke-VMScript -VM $_ -ScriptText $remotecmd2 -GuestUser Administrator -GuestPassword PASSWORD -ScriptType PowerShell

        }
    }
}

changeip

Solution

  • Use your the Get-Content cmdlt to place both file contents into an array then pull the individual values by array position. You'll probably want some logic to check if the array size matches and custom handling if it does not. In your above example, you are basically putting a for each loop inside another foreach loop which is giving you the behavior you are seeing.

    $computers = GC "C:\server.txt"
    $iplist   = GC "C:\ip.txt"
    
    for ($i = 0; $i -lt $iplist.Count ; $i++) {
        Write-host  ("{0}  -  {1}" -f $computers[$i],$iplist[$i])
    }
    

    Or if you are married to using the foreach logic for one list to be all fancy like instead of basic iterating with a for loop then you can add a counter in your foreach loop. Then you can look up your array index of your already parsed iplist array. It's basically doing the same thing though..

    $computers = "C:\server.txt"
    $iplist   = GC "C:\ip.txt"
    
    get-content $computers | % {$counter = 0} {
    Write-host  ("{0}  -  {1}" -f $_,$iplist[$counter])
    $counter++
    }
    

    Just for clarity purposes as well, please note in this line:

    "get-content $computers | %"

    The % is actually an alias for ForEach-Object which is why you are getting the foreach inside a foreach output that you are seeing.