在远程计算机上使用Invoke-Command枚举本地组

时间:2018-01-02 14:32:55

标签: powershell invoke-command

我遇到了一个函数问题,我已写过该函数,以便在针对远程计算机运行时返回本地组的成员。我们使用辅助域帐户获取管理员权限,因此我使用了Invoke-Command,因此我们可以使用该帐户运行脚本块,但是,当我执行此操作而不是使用我的管理员凭据运行新的PowerShell窗口时,它可以& #39; t枚举本地用户不是本地用户的成员。

$computers = "blah"
$creds = Get-Credential
$sb = {
    param($c)
    Add-Type -AssemblyName System.DirectoryServices.AccountManagement
    $ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
    $context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype,$c
    $idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
    $lg = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context,$idtype,"administrators")
    $members = $lg.Members
    return $members
}

foreach ($c in $computers) {
    if ($c -eq $env:COMPUTERNAME) { & $sb -c $c }
    else {
        Invoke-Command -ComputerName $c -Credential $creds -ScriptBlock $sb -ArgumentList $c
    }
}

当我在本地登录的计算机上运行时,它将返回本地组的所有成员。如果我用我的第二个帐户启动一个新控制台,它也可以工作。如果使用Invoke-Command传递凭据,我会收到与缺少网络访问有关的错误,并且似乎在首先在计算机上成功列出两个本地帐户后发生。

失败时为lg变量返回的信息:

PSComputerName        : blah
RunspaceId            : hex...
IsSecurityGroup       : True
GroupScope            : Local
Members               : {local_admin, local_user}
Context               : System.DirectoryServices.AccountManagement.PrincipalContext
ContextType           : Machine
Description           : Administrators have complete and unrestricted access to the computer/domain
DisplayName           :
SamAccountName        : Administrators
UserPrincipalName     :
Sid                   : SID...
Guid                  :
DistinguishedName     :
StructuralObjectClass :
Name                  : Administrators

如果成功,会员部分也包括域组和用户(如果远程使用shell作为第二个帐户运行,或者在作为第二个帐户登录的服务器上本地,则相同的结果):

IsSecurityGroup       : True
GroupScope            : Local
Members               : {local_admin, local_user, domain_group, domain_group, domain_user...}
Context               : System.DirectoryServices.AccountManagement.PrincipalContext
ContextType           : Machine
Description           : Administrators have complete and unrestricted access to the computer/domain
DisplayName           :
SamAccountName        : Administrators
UserPrincipalName     :
Sid                   : SID...
Guid                  :
DistinguishedName     :
StructuralObjectClass :
Name                  : Administrators

收到两种不同的错误,方法略有不同:

An error occurred while enumerating through a collection: The network path was not found.
.
    + CategoryInfo          : InvalidOperation: (System.Director...ctionEnumerator:PrincipalCollectionEnumerator) [], RuntimeException
    + FullyQualifiedErrorId : BadEnumeration
    + PSComputerName        : blah

Cannot convert value "System.DirectoryServices.AccountManagement.PrincipalCollection" to type "System.Array". Error: "The network path was not found.
"
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException
    + PSComputerName        : blah

第一条消息来自于尝试返回成员变量,第二条消息是我尝试将该变量设为数组时。我认为他们基本上有相同的根本原因。我尝试将-EnableNetworkAccess开关添加到Invoke-Command,但这并没有改变收到的错误。

我很欣赏我已经知道了一种方法来完成这项工作,但是我们想知道是否有任何方法可以使用管理员凭据运行shell,并且只有在需要为远程服务器传递时才引入它们。它似乎不是一个身份验证问题,因为我们可以使用Invoke-Command运行更简单的命令,即ipconfig或whoami。

我使用的是PowerShell 5.1

感谢。

2 个答案:

答案 0 :(得分:0)

PoSH远程控制要求帐户是目标主机上的本地管理员,但下面列出的命令除外。这是......

'我们使用辅助域帐户获取管理员权限'

......本地管理员?

https://technet.microsoft.com/en-us/library/ff699046.aspx 某些cmdlet具有-ComputerName参数,使您可以在不使用Windows PowerShell远程处理的情况下使用远程计算机。这意味着您可以在运行Windows PowerShell的任何计算机上使用cmdlet,即使计算机未配置为Windows PowerShell远程处理也是如此。这些cmdlet包括以下

如果您尝试跨域远程操作,您将受到Windows双跳限制的攻击,并且您需要为该类型的方案进行规划和配置。见下文。

https://blogs.msdn.microsoft.com/clustering/2009/06/25/powershell-remoting-and-the-double-hop-problem 一个简单的“双跳”解决方案

您可以使用CredSSP将您的凭据委派给远程计算机,以便远程计算机的每次远程访问也可以使用。要启用此功能,您需要在客户端计算机上运行(从提升的命令提示符下)以下命令:

或者这个选项......

https://blogs.technet.microsoft.com/ashleymcglone/2016/08/30/powershell-remoting-kerberos-double-hop-solved-securely 您是否面临PowerShell远程处理和凭据的问题?你远程进入你的跳跃盒,但是那里的任何遥控都会得到一个大的红色ACCESS DENIED。也许你已经尝试过CredSSP,但人们说这不安全。阅读今天的帖子,了解为PowerShell远程处理启用Kerberos双跃点的完全合法,安全,安全且简便的方法。

答案 1 :(得分:0)

谢谢,postanote,链接。

最后,我看了一个在本地使用第二个帐户凭据调用命令的过程,同时保留在同一个shell中。这是通过创建脚本文件并在单独的进程中运行它来捕获标准输出/错误并解析返回的字符串来完成的。我希望这是有道理的,我有时会解释一些事情。

这是我回来的内容:(我应该在这里说我创建自定义.NET类型作为单独脚本的一部分,作为导入函数的模块清单的一部分加载)

[CmdletBinding(SupportsShouldProcess)]
Param (
    [string[]]$computer = $global:LoggedOnUser.Computer,
    [string]$group = "administrators",
    [System.Management.Automation.PSCredential]$creds = $global:AdminUser.Credentials
)
Begin {
    # Initialise output array
    $groupArray = @()

    # Check C:\Temp exists locally
    if (!(Test-Path C:\Temp)) { New-Item -Path C:\Temp -ItemType Directory -Force }

    # Run this locally with second administrator account in a new process
    $secondAccountSB = {
        param([System.Management.Automation.PSCredential]$creds = $creds,$c = $c, $group = $group)
        $prcsi = New-Object System.Diagnostics.ProcessStartInfo "PowerShell"
        $prcsi.Arguments = "-File C:\Temp\Local_Group_Check.ps1 -c $c -group $group"
        $prcsi.RedirectStandardError = $true
        $prcsi.RedirectStandardOutput = $true
        $prcsi.LoadUserProfile = $false
        $prcsi.UserName = $creds.UserName
        $prcsi.Password = $creds.Password
        $prcsi.Domain = "Domain"
        $prcsi.UseShellExecute = $false
        $prcsi.WorkingDirectory = "C:\Temp"
        $prc = New-Object System.Diagnostics.Process
        $prc.StartInfo = $prcsi
        $prc.Start() | Out-Null
        $prc.WaitForExit()
        $output = $prc.StandardOutput.ReadToEnd()
        $failed = $prc.StandardError.ReadToEnd()
        $output
        $failed
    }

    # Create string for a script
    $groupReportSB = "
        param(`$c,`$group)
        # Add Directory Services .NET Type
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement
        `$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine

        # Initialise array
        `$localGroups = @()

        # Connect to local Directory Services
        `$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList `$ctype,`$c
        `$idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
        Try { `$lg = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity(`$context,`$idtype,`$group) }
        Catch [System.Management.Automation.MethodInvocationException] {
            `$errorMessage = `$_.Exception.InnerException.Message
            Write-Host `"`$($`c.ToUpper()): `$(`$errorMessage.Trim())`" -ForegroundColor Yellow -BackgroundColor Black
            continue
        }

        # Obtain a list of each local group's members
        `$members = `$lg.Members

        # Create PSObject
        foreach (`$member in `$members) {
            if ((`$member.StructuralObjectClass -eq `$null) -or (`$member.StructuralObjectClass -ilike `"user`")) {
                `$groupMembers = `"N/A`"
            }
            else { [string]`$groupMembers = `$member.Members -join `"; `" }
            `$gm = New-Object -TypeName psobject
            `$gm | Add-Member -MemberType NoteProperty -Name Account -Value `$member.SamAccountName
            `$gm | Add-Member -MemberType NoteProperty -Name Computer -Value `$c.ToUpper()
            `$gm | Add-Member -MemberType NoteProperty -Name Description -Value `$member.Description
            `$gm | Add-Member -MemberType NoteProperty -Name Object -Value `$member.StructuralObjectClass
            `$gm | Add-Member -MemberType NoteProperty -Name Group -Value `$lg.Name
            `$gm | Add-Member -MemberType NoteProperty -Name Members -Value `$groupMembers
            `$gm | Add-Member -MemberType NoteProperty -Name Context -Value `$member.ContextType
            `$gm | Add-Member -MemberType NoteProperty -Name Scope -Value `$member.GroupScope
            `$localGroups += `$gm
        }

        # Return Local Groups for this machine
        `$localGroups
    "
}
Process {
    $count = 1
    $total = $computer.Count
    foreach ($c in $computer) {
        Write-Host "[$($count)/$($total)] Collecting information from $c..."
        if (Test-Path C:\Temp\Local_Group_Check.ps1) { Remove-Item C:\Temp\Local_Group_Check.ps1 -Force }
        $groupReportSB | Out-File -FilePath C:\Temp\Local_Group_Check.ps1 -Force -NoClobber
        $returnedOutput = & $secondAccountSB -c $c -group $group
        $groupArray += ConvertTo-WinPSObject -inputObject $returnedOutput -dataType LocalGroups
        $count ++
    }
}
End {
    # Return Group Array
    $groupArray
}

正如我所说,这样做的目的是使其与其他管理功能相配合,这些功能具有更好的远程运行功能,并使用户保持在一直运行的同一个shell中。一个次要的,提升的帐户。

我知道代码没有正确显示,但它会在具有PS语法功能的编辑器中显示。