1

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

$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

我担心的是,这现在消除了“扇出”功能,因为每个调用New-CimSession都是单独的/只需要 1 个服务器参数。

问题

这种担心是否有效,还是New-CimSession更像 C# 中的 async/await 那样工作,因为在等待创建一个 CimSession 时,控制权交还给可以获取下一个会话的父线程?

其他想法/后续问题

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

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

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

4

1 回答 1

1

您可以尝试以下方法:

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块中。

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

  • 如果发生任何错误,那么导致失败的计算机名称将New-CimSession与 DCOM 配置一起传递 - 然后不尝试错误处理,这意味着仍然发生的任何错误都显示为非终止错误。

  • 最后,输出所有成功创建的会话。


通常,使用-ErrorAction Stopalways 会在第一个非终止错误时中止命令;如果您想收集它们供以后分析,请使用类似的东西-ErrorAction SilentlyContinue -ErrorVariable errs,其中 self-chosen 变量$errs接收发生的所有非终止错误的集合。

于 2019-08-04T15:22:21.970 回答