4

我正在寻找编写一个 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 和子网面具集。没有默认网关(这是有道理的,因为我在脚本中没有走那么远)。有问题的计算机没有网络访问权限,因为缺少默认网关。

我也尝试过从交互式 shell 运行相同的命令,并且相同的“冻结”发生在相同的命令上:

$wmi.EnableStatic($IPAddress, $SubnetMask)

我最好的猜测是更改网络适配器配置会破坏远程 WMI 连接。有没有办法完成这项工作,以便我可以远程 100% 编写静态 IP 地址的分配脚本?

编辑:我 MacGyvered 再次尝试创建 ScriptBlock 对象并使用 Invoke-Command 将其发送到远程计算机。我必须做一些有趣的工作才能将 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
}
4

2 回答 2

2

使用Invoke-Command在远程计算机上运行netsh可能会更幸运,它可以在一个命令中设置 IP 地址、子网掩码和网关。但是,netsh对接口使用不同的名称,您可以从Win32_NetworkAdapter类的NetConnectionID属性中获取该名称。

$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

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

这将确保您只获得一个对象,但如果有多个IPEnabled为true,它当然不能确保它一定是您想要的对象。

于 2013-07-23T23:51:18.327 回答
1

localHost我会在一个用作参数的脚本中调用您的函数,$computername然后invoke-command在远程计算机上远程执行()这个脚本,使用 powershell 远程会话(New-PSSession),或者使用这个脚本作为 WMI 的参数创建一个远程 PowerShell 进程(在这种情况下,你必须复制远程计算机上的脚本)。

您也可以在远程计算机上安排此脚本...一般的想法是在操作期间不使用网络。

于 2013-07-23T20:02:13.400 回答