New-CimSession 自动降级为 DCOM/WMI

New-CimSession Auto Downgrade to DCOM / WMI

CIM 的好处之一是 "fan out" 能力;例如运行 以下会导致针对所有服务器并行创建会话,而不是按顺序创建:

$myServers = 1..10 | %{'MyServer{0:00}.mydomain.example.com' -f $_}
$cimSessions = New-CimSession -ComputerName $myServers

由于我们的一些服务器没有启用 WimRM,我想在 WimRM 不可用时优雅地降级为使用 DCOM/这样我们就不会在不必要的情况下失败。因此,我在 Get-CimSession 周围放了一个包装器,它做这样的事情(简化版):

function New-SoEgCimSession { #(soeg = Stack Overflow Exempli Gratia)
    [CmdletBinding()]
    Param (
        [Parameter(Mantatory = $true, ValueFromPipeline = $true)]
        [String]$ComputerName
    )
    Begin {
        [Microsoft.Management.Infrastructure.Options.WSManSessionOptions]$WsmanSessionOption = New-CimSessionOption -Protocol Wsman
        [Microsoft.Management.Infrastructure.Options.DComSessionOptions]$DcomSessionOption = New-CimSessionOption -Protocol Dcom
    }
    Process {
        $session = New-CimSession -ComputerName $ComputerName -SessionOption $WsmanSessionOption
        if ($null -eq $session) {
            $session = New-CimSession -ComputerName $ComputerName -SessionOption $DcomSessionOption
        }
        $session
    }
}

...我现在会这样调用:

$cimSessions = $myServers | New-SoEgCimSession

我担心的是,这现在删除了 "fan out" 能力,因为对 New-CimSession 的每次调用都是单独的/只需要 1 个服务器参数。

问题

这种担忧是否有效,或者 New-CimSession 是否更像 C# 中的 async/await,因为在等待创建一个 CimSession 时,控制权交还给父线程,它可以获取下一节课?

其他想法/跟进问题

如果我的担心是正确的,是否有更简洁的方法来解决这个问题?我考虑过使用工作流来包装这个逻辑,但这感觉就像重新发明 New-CimSession 默认提供的东西。

我还考虑过使用计算机数组调用 New-CimSession-ErrorAction Stop,然后使用 catch 块来处理任何失败;但似乎只包括第一个失败的详细信息(即 catch {$_.Exception.OriginInfo} returns 单个服务器名称,即使多个服务器失败)。我也没有对足够大的服务器组进行测试,无法知道该错误是否也会终止创建新会话的尝试;对于小数据集它没有,但这可能是因为在检测到非 winrm 服务器的错误之前所有会话都已成功创建。

我目前的想法是将原始数组的计算机名称与已创建会话中的计算机名称进行比较,以找到未为其创建会话的计算机名称,然后尝试为其余的创建 DCOM 会话。然而,这仍然感觉像是一个有点老套的解决方法/我怀疑我缺乏使用 CIM 的经验意味着我错过了一个更明显的开箱即用的解决方案......

您可以尝试以下方法:

function New-SoEgCimSession {
  [CmdletBinding()]
  Param (
      [Parameter(Mantatory, ValueFromPipeline)]
      [String[]] $ComputerName
  )

  Begin {
      $names = [Collections.Generic.List[string]]::new()
      $WsmanSessionOption = New-CimSessionOption -Protocol Wsman
      $DcomSessionOption = New-CimSessionOption -Protocol Dcom
  }

  Process {
      $names.AddRange($ComputerName)
  }

  End {
      # Try WSMan first. Successful sessions will be output,
      # errors silently collected in variable $errs.
      New-CimSession -ErrorVariable errs -ErrorAction SilentlyContinue -ComputerName $names -SessionOption $WsmanSessionOption
      if ($errs.Count) {
        # Errors occurred; try all failing computers with DCOM now.
        # Successful sessions will be output and if there are still errors,
        # they will surface.
        $failedNames = $errs.OriginInfo.PSComputerName
        New-CimSession -ComputerName $failedNames -SessionOption $DcomSessionOption
      }
  }
}
  • 计算机名称收集在一个列表中,只有收集完所有计算机名称后,才会在 end 块中开始处理。

  • 首先使用 WSMan 配置将所有名称传递给 New-CimSession,抑制 显示 任何非终止错误(-ErrorAction SilentlyContinue ), 但将它们收集在变量 $errs (-ErrorVariable errs).

  • 如果发生任何错误,导致失败的计算机名称随后会通过 DCOM 配置传递给 New-CimSession - 然后不会尝试任何错误处理,这意味着任何仍然发生的错误表面作为非终止表面。

  • 最后输出所有创建成功的session


通常,使用 -ErrorAction Stop 总是会在出现 第一个 非终止错误时中止命令;如果你想收集它们 all 供以后分析,使用类似 -ErrorAction SilentlyContinue -ErrorVariable errs 的东西,其中自选变量 $errs 接收所有非终止错误的集合发生了。