VMware Cloud Community
itsupportsaim
Contributor
Contributor

Limitations of Invoke-VMScript and Certain Powershell Cmdlets - Any Ideas on a Workaround?

I am hoping someone else has already developed a workaround for this particular issue.  This is the last hurdle in an otherwise useful script to assist in our CoB (or DR) plan.  Replicated VMs are spun up at the DR site in a specific folder in vCenter and once online I'd intended to use this script to modify NIC settings and reconnect them to the VM Network, leaving staff available to handle the SQL box that everyone depends on and other manual tasks.  The same goes for failback, and of course proper error handling is a must as well as checks to ensure that VMs native to the DR site do not have their settings modified.  Several days ago I'd found a script online that tackled a portion of what I intended and as such it was a great starting point.  Now I have a script that meets all of my needs, with exception of replacement of the default gateway.  Actually without this piece, the rest is basically a waste of time Smiley Sad

I've tried both of these methods and while I can utilize remove-ipaddress to remove only the current IP address and use set-netipaddress to add a new IP and gateway, I still end up with the orphan gateway.  viewing the output of the remove-netipaddress and remove-netroute and you see why they fail: Powershell is in non-interactive mode and as such prompting is not supported, nor apparently can you override prompts.

$scripttmp = 'remove-netipaddress -InterfaceAlias "' + $intAlias + '" -IPAddress ' + $currentIP + ' -PrefixLength 24 -DefaultGateway ' + $curGateway + ' -Confirm:$false -ErrorAction SilentlyContinue'

$remnetout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

OR

$scripttmp='remove-netroute -interfaceindex ' + $intIndex + ' -NextHop ' + $curGateway + ' -DestinationPrefix ' + $DestPrefix'

$rnetout=invoke=vmscript -ScriptText $scripttmp -ScriptType Powershell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

Any assistance is of course much appreciated.

0 Kudos
7 Replies
LucD
Leadership
Leadership

Did you already try executing the following inside the guest OS with the Invoke-VMScript cmdlet?

$adapter = Get-NetAdapter

Remove-NetIPAddress -InterfaceIndex $adapter.ifIndex -Confirm:$false

New-NetIPAddress -InterfaceIndex $adapter.ifIndex -IPAddress 192.168.1.1 -DefaultGateway 192.1681.254 -PrefixLength 24 -Confirm:$false


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
itsupportsaim
Contributor
Contributor

Thank you for jumping in Lucd!  I admit I was hoping to hear from you.  In short, yes but not precisely that command so I will try that once I've finished this reply.

This is a heavily modified version of a script from herseyc, if it helps I am including it here.  Note the lack of error handling, that's coming once this hurdle is cleared Smiley Happy  Failback method is just going to be a matter of minor adjustment to variables based on the "Failover" argument.

In its entirety so far:

###################################

# Parts and pieces from herseyc script for changing IP addresses on 2012 VMs

#

#

param(

   [string] $VCenter = "faux-vcsa",

   [string] $User = "localadminuser",

   [string] $Pass = "localadminpassword",

   [boolean] $Failover = $True

)

$ErrorActionPreference = "SilentlyContinue"

Import-Module VMWare.VimAutomation.Core

Import-Module VMWare.VimAutomation.Common

Import-Module VMWare.VimAutomation.Cloud

Connect-VIServer -Server $VCenter

IF ($Failover-eq $True) {

$oIP = "10.10.50."

$nIP = "10.10.60."} Else {

$oIP = "10.10.60."

$nIP = "10.10.50."

}

###############VARIABLES###############

#Original IP to replace during failover"

$origIp = $oIP

#New IP Information

$newIp = $nIP

$newMask = "255.255.255.0"

#$newGateway = "10.10.60.10"

$newGateway = $newIP + "10"

$DNS1 = $newIP + "22"

$DNS2 = $newIP + "19"

$DNS3 = $newIP + "21"

$DNS4 = "10.10.60.60"

$DNS = "$DNS1,$DNS2,$DNS3,$DNS4"

$WINS = @($DNS1,$DNS2,$DNS3,$DNS4);

#$DNS = "10.10.60.60,10.10.60.22,10.10.60.21,10.10.60.19"

#$WINS = @("10.10.60.60","10.10.60.22","10.10.60.21","10.10.60.19");

#Guest Credentials - Must have required permissions to change IP address

$GuestUserName = $User

$GuestPassword = $Pass

# Get all VMs in the recovery folder that are powered on.

Write-Host "Getting list of VMs from COB Folder where state is PoweredOn"

$VMs = (Get-Folder "COB" | Get-VM | where {$_.PowerState -eq "PoweredOn"}).Name

foreach ($vm in $VMs) {

   Write-Host "Working on $vm"

#Get relevant NIC

   $scripttmp = 'Find-NetRoute -RemoteIPAddress "8.8.8.8" | select-object -ExpandProperty InterfaceIndex -Unique '

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $intIndex = $scriptout

   #write-host "Interface Index = " + $IntIndex

   #PowerShell used by Invoke-VMScript to retrieve current IP Address

   $scripttmp = '(Get-NetIPAddress | where-object {$_.IPAddress -match "' + $origIp + '" -and $_.AddressFamily -eq "IPv4"}).IPAddress'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $currentIP = $scriptout

   $currentIp = $currentIp -replace "`t|`n|`r",""

   #write-host "$currentIp is the current IP Address"

  

   #Adjust Original IP to Replacement IP

   $changeIp = $currentIp.replace("$origIp", "$newIp")

   $changeIp = $changeIp -replace "`t|`n|`r",""

   $NewIPObj = [ipaddress]$changeIp

   $NewGWObj = [ipaddress]$newGateway

   #Write-Host "Changing IP to $changeIp"

   #Get the Interface Name (Alias)

   $scripttmp = '(Get-NetIPAddress | where-object {$_.IPAddress -match "' + $origIp + '" -and $_.AddressFamily -eq "IPv4"}).InterfaceAlias'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $intAlias = $scriptout

   $intAlias = $intAlias -replace "`t|`n|`r",""

   #write-host "The interface name is $intAlias"

     

  #Get current gateway

   $scripttmp = 'Get-NetIPConfiguration -InterfaceAlias "' + $intAlias + '" | Select-Object -ExpandProperty IPv4DefaultGateway | select -expand NextHop'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $curGateway = $scriptout

   #write-host "The current gateway is $curGateway"

   $scripttmp = 'Get-NetIPConfiguration -InterfaceAlias "' + $intAlias + '" | Select-Object -ExpandProperty IPv4DefaultGateway | select -expand DestinationPrefix'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $desPrefix = $scriptout

   #write-host $destPrefix

   #$scripttmp ='remove-netroute -interfaceindex "' + $intIndex + '" -NextHop "' + $curGateway + '" -destinationprefix "' + $destPrefix + '" -confirm:$false'

   #$scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   #$rmGateway = $scriptout

   #write-host $rmgateway

   #$scripttmp = 'new-netroute -interfaceindex "' + $intIndex + '" -NextHop "' + $newGateway + '" -destinationprefix "' + $destPrefix + '" -confirm:$false'

   #$scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   #$ngGateway = $scriptout

   #write-host $ngGateway

   #Remove IP address

   $scripttmp = 'remove-netipaddress -InterfaceAlias "' +  $intAlias + '" -Confirm:$false'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   write-host $scriptout

   #Set new IP address and gateway using newer cmdlet

   $scripttmp = 'new-netipaddress -InterfaceAlias "' +  $intAlias + '" -IPAddress ' + $changeIp + ' -PrefixLength 24 -DefaultGateway ' + $newGateway

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   write-host $scriptout

   #Clear DNS / WINS Servers

   $scripttmp = '%WINDIR%\system32\netsh.exe interface ipv4 delete dnsservers "' + $intAlias + '" all'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType bat -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $scripttmp = '%WINDIR%\system32\netsh.exe interface ipv4 delete winsservers "' + $intAlias + '" all'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType bat -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   #Set DNS

   $scripttmp = 'set-DnsClientServerAddress -InterfaceAlias "' + $intAlias + '" -ServerAddresses ' + $DNS

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   #write-host $scriptout

   #Set WINS

   #$WINS = Get-WmiObject win32_networkadapterconfiguration | where IPAddress -eq $changeIP

   #write-host $WINS

   #$WinsSet = $WINS.SetWINSServer($DNS)

   #write-host $WinsSet

   $count = 0

   foreach($server in $WINS) {

   $count = $count + 1

   Write-Host "Setting WINS Server to $server"

   $scripttmp = '%WINDIR%\system32\netsh.exe interface ipv4 add winsservers "' + $intAlias + '" ' + $server + ' index=' + $count

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType bat -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   }

   #Connect vmNic

   Get-NetworkAdapter -VM  $vm | Set-NetworkAdapter -StartConnected:$true -Connected:$true -Confirm:$false

  

   #Register with DNS

   #Write-Host "Registering with DNS"

   $scripttmp = '%WINDIR%\System32\ipconfig /registerdns'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType bat -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   Write-Host "Finished with $vm"

   }

0 Kudos
itsupportsaim
Contributor
Contributor

OK, I've added the following, where $intIndex source can be found in the full script:

#Remove IP address

   $scripttmp = 'remove-netipaddress -InterfaceIndex ' +  $intIndex + ' -Confirm:$false'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   write-host $scriptout

   #Set new IP address and gateway using newer cmdlet

   $scripttmp = 'new-netipaddress -InterfaceIndex ' +  $intIndex + ' -IPAddress ' + $changeIp + ' -PrefixLength 24 -DefaultGateway ' + $newGateway + ' -Confirm:$false'

   $scriptout = invoke-vmscript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   write-host $scriptout

And the output is this:

Working on SAIM-TEST

Exception calling "EndProcessing" with "0" argument(s): "Windows PowerShell is

in NonInteractive mode. Read and Prompt functionality is not available."

At line:1161 char:17

+                 $__cmdletization_objectModelWrapper.EndProcessing()

+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordE

   xception

    + FullyQualifiedErrorId : PSInvalidOperationException

New-NetIPAddress : Cannot process command because of one or more missing

mandatory parameters: IPAddress.

At line:1 char:4

+ & {new-netipaddress -InterfaceIndex 12

+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (:) [New-NetIPAddress], Paramet

   erBindingException

    + FullyQualifiedErrorId : MissingMandatoryParameter,New-NetIPAddress

-IPAddress : The term '-IPAddress' is not recognized as the name of a cmdlet,

function, script file, or operable program. Check the spelling of the name, or

if a path was included, verify that the path is correct and try again.

At line:2 char:2

+  -IPAddress 10.10.60.64 -PrefixLength 24 -DefaultGateway 10.10.60.10

-Confirm:$f ...

+  ~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (-IPAddress:String) [], CommandN

   otFoundException

    + FullyQualifiedErrorId : CommandNotFoundException

0 Kudos
LucD
Leadership
Leadership

I think we could have more of what is going on by using the Start-Transcript and Stop-Transcript cmdlet at the beginning and end of the script.


I'll try to run this versus test VM in my lab.Which guest OS do you have running in the VM that your are testing against?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
LucD
Leadership
Leadership

First thing I noticed during my testing, you are capturing all output from Invoke-VMScript.

While you only need the ScriptOutput property.

Something like this

#Get relevant NIC

   $scripttmp = 'Find-NetRoute -RemoteIPAddress "8.8.8.8" | select-object -ExpandProperty InterfaceIndex -Unique '

   $scriptout = Invoke-VMScript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $intIndex = $scriptout.ScriptOutput

   write-host "Interface Index = $IntIndex"


   #PowerShell used by Invoke-VMScript to retrieve current IP Address

   $scripttmp = '(Get-NetIPAddress | where-object {$_.IPAddress -match "' + $origIp + '" -and $_.AddressFamily -eq "IPv4"}).IPAddress'

   $scriptout = Invoke-VMScript -ScriptText $scripttmp -ScriptType PowerShell -VM $vm -GuestUser $GuestUserName -GuestPassword $GuestPassword

   $currentIP = $scriptout.ScriptOutput

   $currentIp = $currentIp -replace "`t|`n|`r",""

   write-host "$currentIp is the current IP Address"


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
itsupportsaim
Contributor
Contributor

Windows 2012 R2.  I can also record a run or two and see what i get.  Be advised that if you don't have a source IP matching that network address (10.10.50.0/24) it won't do anything as it doesn't get a handle to the adapter.  One of my 'todos' is to log and break out of the loop when anything besides the expected source network address is found.

Thank you again sir.

0 Kudos
itsupportsaim
Contributor
Contributor

Thanks for the tip on the output, I had not looked at the object model for invoke-vmscript and didn't realize (but should have known) it included much more than just the stdout from the execution on the vm.

0 Kudos