PowerShell: 远程调用

通过本地程序调用 PowerShell 自定义脚本,我们可以有周期、计划性的执行一些扩展的操作,这在一定程度上提高了本地程序自身逻辑的扩展性。而在编写一段 PowerShell 脚本的过程中,也难免会遇到需要一段脚本在另一台计算机上远程执行的情形。例如,我们在执行一段 PowerShell 脚本时,需要向某个 AD Group 中添加一个 AD User,而这一操作需要在域控机器上才能执行,而此时,我们就会用到 PowerShell 远程调用执行代码。

在调用之前,我们需要保证 PowerShell 的远程调用设置是开启的。
1 启用远端计算机的 Windows Remote Management (WS-Management) service.
(1) 通过 services.msc 启动服务管理器
(2) 在其中找到 Windows Remote Management (WS-Management) 这一项
(3) 确定该项服务正处于运行状态,并且启动类型被设置为 Automatic

2 设定本地组策略.
(1) 通过 gpedit.msc 开启本地组策略编辑器
(2) 找到 Computer Configuration > Administrative Templates > Windows Components > Windows Remote Management (WinRM) > WinRM
Service
(3) 在右侧的项目中,双击 “Allow remote server management through WinRM”
(4) 在新弹出的窗口中,选择Enabled,从而启用策略,然后再Options的设置区域,位IPv4和IPv6 filter添加规则”*”,从而使所有IP都不受到过滤器的限制。当然,这里也可以根据具体的环境进行配置,相关配置可参考下图。

WinRM Local Policy Settings for PowerShell

(6) 应用并保存配置后即可。

随后,我们就可以通过 PowerShell 的 Invoke-Command 命令使 PowerShell 脚本片段在远程机器上执行,我们同样也可以向远程脚本传递参数。

# 定义传递给远端服务器脚本的参数,这里我们定义了一个字典对象来统一存放这些参数。
$tempParams = @{}
$tempParams.Add("adGroups", $adGroups)
$tempParams.Add("targetUsers", $targetUsers)

# 如果下边不希望指定计算机名,而是IP,则必须提供Credential参数
# 援引 MSDN 的描述:
# To use an IP address in the value of the ComputerName parameter, 
# the command must include the Credential parameter. Also, 
# the computer must be configured for HTTPS transport or 
# the IP address of the remote computer must be included 
# in the WinRM TrustedHosts list on the local computer.

$results = Invoke-Command -Credential $登录认证凭据对象 -ComputerName $远程执行的计算机名 –ArgumentList $tempParams -ScriptBlock {
    # 这里开始编写用于远程执行的 PowerShell 脚本
    Param($tempParams)
    try
    {
        # 引用远端计算机上的某个Module,以完成后续的操作,这里假定我们的后续操作是修改AD
        Import-module ActiveDirectory

        # todo ...
            
        return "success"
    }
    catch
    {
        return $_
    }
} 

关于 Invoke-Command 的详细参数介绍,可参考MSDN

下面给出了一个操纵AD Group的示例,我将一些AD用户从指定的AD组中移除。
* 这里 $ADDomainAdminPasswordEncryptedText 保存的是实际密码的密文,这样可以防止用户密码被显式的保存在脚本中。关于这一机制的介绍,可以参考我的另一篇blog.

#
# Configurations
#
$ADDomainControllerComputerName = "DCHostName"
$ADDomainAdminUsername = "DOMAINDomainAdminUsername"
$ADDomainAdminPasswordEncryptedText = "01000000d08c9ddf0115d1118c7a00c04fc297eb0100000053c3945ec9b08c4b8d63f85a8edcd1190000000002000000000003660000c00000001000000014a97ff6bf4780b14e38d3ef9ae549640000000004800000a0000000100000006f8b17e3fad7da6f892a895a683d8f75180000007fe0d199ace1ed4306af34cdbb9fdcc2745a5bc9bd3abda8140000001bc56921787de674117e8c49f368745ac62400aa"
#

$logger = (Split-Path -Parent $MyInvocation.MyCommand.Definition) + "debug.log"

# Main
try
{

    # ... ...

    # Add User to AD Group
    $targetUsers = $selectedUser.Split(";")    
    $adGroups = $selectedADGroup.Split(";")
    
    $domainAdminPassword = $ADDomainAdminPasswordEncryptedText | ConvertTo-SecureString
    $dcCred = New-Object -TypeName System.Management.Automation.PSCredential($ADDomainAdminUsername, $domainAdminPassword)
  
    $tempParams = @{}
    $tempParams.Add("adGroups", $adGroups)
    $tempParams.Add("targetUsers", $targetUsers)

    $results = Invoke-Command -Credential $dcCred -ComputerName $ADDomainControllerComputerName -ScriptBlock {
        Param($tempParams)
        try
        {
            Import-module ActiveDirectory
            
            $adGroups = $tempParams["adGroups"]
            $targetUsers = $tempParams["targetUsers"]
            
            foreach($adGroup in $adGroups)
            {
                if(!$adGroup.Equals(""))
                {
                    if($adGroup.Contains(""))
                    {
                        $adGroup = $adGroup.Split("")[1];
                    }

                    foreach($targetUser in $targetUsers)
                    {
                        try
                        {
                            if($targetUser.Contains(""))
                            {
                                $targetUser = $targetUser.Split("")[1]
                            }
                            $checkIsADGroup = $null
                            try { $checkIsADGroup = Get-ADGroup '$adGroup' -ErrorAction Continue } catch {}
                            # if the user is a AD Group and it has the different name with target AD group 
                            if($checkIsADGroup -eq $null -or $checkIsADGroup -ne $null -and $adGroup -ne $targetUser)
                            {
                                Add-ADGroupMember -Identity $adGroup -Member $targetUser    
                            }
                        }
                        catch
                        {
                            if(-not $_.Exception.Message.ToLower().Contains("The specified account name is already a member of the group".ToLower()))
                            {
                                throw $_
                            }
                        }
                    }
                }
            }
            return "success"
        }
        catch
        {
            return $_
        }
    } –ArgumentList $tempParams

    if($results -eq "success")
    {
        ac $logger "Add user to AD group successful."
    }
    else
    {
        ac $logger "Add user to AD group failed."
        ac $logger $results
    }
}
catch
{         
    ac $logger "Main Error: $_"
}
Hendry

About Hendry

不经历复杂的简单,只是一种苍白。

发表评论

电子邮件地址不会被公开。