Powershell удаленный статический IP через скрипт


Я собираюсь написать функцию Powershell для удаленного назначения статического IP-адреса компьютеру, указанному во время выполнения. Я провел небольшое исследование и нашел класс Win32_NetworkAdapterConfiguration, который кажется именно тем, что мне нужно. Поэтому я сел и написал себе функцию:

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
    }
}
Проблема в том, что методEnableStatic никогда не возвращает значения - ни кода успеха, ни кода неудачи. Я протестировал эту функцию на машине, сидящей рядом со мной, и в то время как сценарий все еще ждал на этапе "попытка установить статический IP-адрес и маску подсети", я поднял конфигурацию на машине и нашел статический IP-адрес и маску подсети. Там не было шлюза по умолчанию (что имеет смысл, так как я не зашел так далеко в сценарии). Компьютер, о котором идет речь, не имел доступа к сети, поскольку отсутствовал шлюз по умолчанию.

Я также пробовал запускать те же команды из интерактивной оболочки, и та же самая "заморозка" происходит на том же самом команда:

$wmi.EnableStatic($IPAddress, $SubnetMask)

Мое лучшее предположение заключается в том, что изменение конфигурации сетевого адаптера нарушает удаленное соединение WMI. Есть ли способ сделать это так, чтобы я мог написать сценарий назначения статического IP-адреса 100% удаленно?

Edit: I MacGyvered еще одна попытка, которая создает объект ScriptBlock и отправляет его на удаленный компьютер с помощью команды Invoke. Мне пришлось проделать кое-какую интересную работу ногами, чтобы получить массив IP-адресов, чтобы превратить его в строковый литерал, включая кавычки, но теперь я могу подтвердить, что блок скрипта имеет правильный синтаксис и все такое. К сожалению, это приводит к тому, что мое окно PS жалуется на то, что сетевое соединение было потеряно (так как IP-адрес изменился), и блок сценария не завершается успешно.

$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
}
2 3

2 ответа:

Возможно, Вам повезет больше, если вы используете Invoke-Command для запуска netsh на удаленном компьютере, который устанавливает IP-адрес, маску подсети и шлюз в одной команде. Однако netsh использует другое имя для интерфейса, которое можно получить из свойства NetConnectionID класса Win32_NetworkAdapter.

$InterfaceName = $Get-WmiObject Win32_NetworkAdapter | ?{$_.Description -eq $wmi.Description} | select -ExpandProperty NetConnectionID
Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock {netsh interface ip set address $InterfaceName static $IPAddress $SubnetMask $DefaultGateway 1}

Вы можете обернуть вторую строку в другой блок if ($Credential).

Тем не менее, я настоятельно рекомендую вам проверить, что фильтр возвращает один объект, а не массив объектов, как предлагалось в моих комментариях выше. Или, на всякий случай, изменить

$wmi = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ComputerName -Filter "IPEnabled = 'True'" -Credential $Credential

To

$wmi = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ComputerName -Filter "IPEnabled = 'True'" -Credential $Credential | select -First 1

Это гарантирует, что вы получаете только один объект, но, конечно, он не может гарантировать, что это обязательно тот, который вы хотите, если есть более одного с IPEnabled true.

Я бы вызвал вашу функцию в скрипте, который использует localHost в качестве параметра $computername, а затем удаленно выполнил (invoke-command) этот скрипт на удаленном компьютере, используя удаленные сеансы powershell (New-PSSession) или создав удаленный процесс PowerShell с этим скриптом в качестве параметра с помощью WMI (в этом случае вам нужно скопировать srcript на удаленный комптер).

Вы также можете запланировать этот сценарий на удаленном компьютере ... общая идея заключается в том, чтобы не использовать сеть во время работы.