I'm looking to write a Powershell function to assign a static IP address remotely to a computer specified at runtime. I've done some research and found the Win32_NetworkAdapterConfiguration class, which seems like exactly what I need. So I sat down and wrote myself a function:
Function Set-StaticIPAddress
{
param
(
[Parameter(Mandatory = $true,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[String] $ComputerName
,
[Parameter(Mandatory = $true,
Position = 1)]
[Alias("IPv4Address")]
[String] $IPAddress
,
[Parameter(Position = 2)]
[String] $SubnetMask = "none"
,
[Parameter(Position = 3)]
[String] $DefaultGateway = "none"
,
[Parameter(Position = 4)]
[String[]] $DNSServers = ("172.16.1.36","172.16.1.78")
,
[Parameter(Position = 5)]
[PSCredential] $Credential
)
process
{
# There's some error-checking here that I've snipped out for convenience
Write-Verbose "Testing connection to $ComputerName"
if (-not (Test-Connection $ComputerName))
{
Write-Error "Unable to connect to $ComputerName."
return
}
Write-Verbose "Obtaining remote WMI reference"
if ($Credential)
{
$wmi = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ComputerName -Filter "IPEnabled = 'True'" -Credential $Credential
} else {
$wmi = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ComputerName -Filter "IPEnabled = 'True'"
}
Write-Verbose "Attempting to set DNS servers"
$wmi.SetDNSServerSearchOrder($DNSServers)
Write-Verbose "Attempting to set dynamic DNS registration"
$wmi.SetDynamicDNSRegistration($true)
Write-Verbose "Attempting to set static IP address and subnet mask"
$wmi.EnableStatic($IPAddress, $SubnetMask)
Clear-DnsClientCache #This may not be necessary; I added it as a troubleshooting step
Write-Verbose "Attempting to set default gateway"
$wmi.SetGateways($DefaultGateway, 1)
Write-Output $wmi
}
}
The trouble is, the EnableStatic method never seems to return a value - either a success or failure code. I tested this function on a machine sitting right next to me, and while the script was still waiting at the "Attempting to set static IP address and subnet mask" stage, I pulled up the configuration on the machine and found a static IP and subnet mask set. There was no default gateway (which makes sense, since I didn't get that far in the script). The computer in question didn't have network access, because the default gateway was missing.
I have also tried running the same commands from an interactive shell, and the same "freeze" happens on the same command:
$wmi.EnableStatic($IPAddress, $SubnetMask)
My best guess is that changing the network adapter configuration is breaking the remote WMI connection. Is there a way to make this work so I can script the assignment of a static IP address 100% remotely?
Edit: I MacGyvered another attempt which creates a ScriptBlock object and sends it to the remote computer using Invoke-Command. I had to do some interesting footwork to get an array of IP addresses to turn into a String literal, including the quotes, but I can now confirm that the script block has correct syntax and all that. Unfortunately, doing it this way causes my PS window to complain that the network connection has been lost (since the IP address has changed) and the script block does not complete successfully.
$wmiLiteral = '$wmi' # used so the script block actually creates and references a variable, $wmi
$script =
"$wmiLiteral = Get-WmiObject Win32_NetworkAdapterConfiguration -Filter ""IPEnabled = 'True'"";
$wmiLiteral.EnableStatic(""$IPAddress"", ""$SubnetMask"");
$wmiLiteral.SetGateways(""$DefaultGateway"", 1);
$wmiLiteral.SetDNSServerSearchOrder($DNSServersList);
Write-Output $wmiLiteral"
Write-Verbose "Script block:`n-------------`n$script`n---------------"
$scriptBlock = [scriptblock]::Create($script)
Write-Verbose "Testing connection to $ComputerName"
if (-not (Test-Connection $ComputerName))
{
Write-Error "Unable to connect to $ComputerName."
return
}
Write-Verbose "Invoking scriptblock"
if ($Credential)
{
$output = Invoke-Command -ComputerName $ComputerName -ScriptBlock $scriptBlock -Credential $Credential
} else {
$output = Invoke-Command -ComputerName $ComputerName -ScriptBlock $scriptBlock
}
$description = $wmi.DescriptionAfter you invoke EnableStatic, get the WMI object again:$wmi = Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "Description = '$description'"$wmi.GetType().BaseType; returnand see whether it outputs "System.Management.ManagementBaseObject" or "System.Array". (Then remove that line)