PowerShell 技能连载 - 远程执行 gpupdate

您可以用这样的一段脚本远程执行 gpupdate.exe

function Start-GPUpdate
{
    param
    (
        [String[]]
        $ComputerName
    )

    $code = {
        $rv = 1 | Select-Object -Property ComputerName, ExitCode
        $null = gpupdate.exe /force
        $rv.Exitcode = $LASTEXITCODE
        $rv.ComputerName = $env:COMPUTERNAME
        $rv
    }
    Invoke-Command -ScriptBlock $code -ComputerName $ComputerName |
      Select-Object -Property ComputerName, ExitCode

}

Start-GPUpdate 接受一个或多个计算机名,然后对每台计算机运行 gpupdate.exe,并返回执行结果。

这段脚本利用了 PowerShell 远程管理技术,所以它需要目标计算机启用了 PowerShell 远程管理,并且您需要这些机器的本地管理员权限。

PowerShell 技能连载 - 获取数据库连接字符串

您是否疑惑过一个数据库的连接字符串到底长什么样?当您从控制面板中创建一个数据源时,一个向导将指引您完成整个创建过程。以下是一个利用这个向导并获取生成的连接字符串的方法。

请注意该向导的选择要依赖于您机器上所安装的数据库驱动。

function Get-ConnectionString
{

  $Path = Join-Path -Path $env:TEMP -ChildPath 'dummy.udl'

  $null = New-Item -Path $Path -ItemType File -Force

  $CommandArg = """$env:CommonProgramFiles\System\OLE DB\oledb32.dll"",OpenDSLFile "  + $Path


  Start-Process -FilePath Rundll32.exe -Argument $CommandArg -Wait
  $ConnectionString = Get-Content -Path $Path | Select-Object -Last 1
  $ConnectionString | clip.exe
  Write-Warning 'Connection String is also available from clipboard'
  $ConnectionString

}

当您调用 Get-ConnectionString 方法时,将会创建一个临时的 udl 文件,并且用控制面板向导打开它。您可以通过向导完成配置。配置完成之后,PowerShell 将会检测临时文件并且返回连接字符串。

它的工作原理是 Get-Process 函数带了 -Wait 参数,它能够挂起脚本的执行,直到向导退出。在向导退出以后,脚本就可以安全地访问 udl 文件了。

PowerShell 技能连载 - 用 Splatting 技术封装 WMI 调用

Splatting 是向 cmdlet 传递多个参数的好方法。以下例子演示了如何封装 WMI 调用,并且使它们支持不同的名称:

function Get-BIOSInfo
{
    param
    (
        $ComputerName,
        $Credential,
        $SomethingElse
    )

    $null = $PSBoundParameters.Remove('SomethingElse')

    Get-WmiObject -Class Win32_BIOS @PSBoundParameters
}

Get-BIOSInfo 通过 WMI 获取 BIOS 信息,并且它支持本地、远程以及通过证书的远程调用。这是因为用户向 Get-BIOSInfo 传递的实参实际上传递给了 Get-WmiObject 对应的参数。所以当一个用户没有传递 -Credential 参数,那么就不会向 Get-WmiObject 传递 -Credential 参数。

Splatting 技术通常使用一个自定义的哈希表,它的每个键代表一个形参,每个值代表一个实参。在这个例子中,使用了一个预定义的 $PSBoundParameters 哈希表。它事先插入了要传递给函数的参数。

请确保不要传给目标 cmdlet 它不知道的参数。举个例子,Get-BIOSInfo 函数定义了一个“SomethingElse”参数。而 Get-WmiObject 没有这个参数,所以您在 splat 之前,您必须先调用 Remove() 方法从哈希表中把这个键移掉。

PowerShell 技能连载 - 用 Splatting 技术提交参数

Splatting 是 PowerShell 3.0 引入的概念,但是许多用户还没有听说这个概念。这是一种以可编程的方式将参数传给 cmdlet 的技术。请看:

$infos = @{}
$infos.Path = 'c:\Windows'
$infos.Recurse = $true
$infos.Filter = '*.log'
$infos.ErrorAction = 'SilentlyContinue'
$infos.Remove('Recurse')

dir @infos

这个例子定义了一个包含键值对的哈希表。每个键对应 dir 命令中的一个参数,并且每个值作为实参传递给对应的形参。

当您的代码需要决定哪些参数需要传给 cmdlet 时,Splatting 十分有用。您的代码可以只需要维护一个哈希表,然后选择性地将它传给 cmdlet。

PowerShell 技能连载 - 快速获取成员身份

如果您需要了解您的用户账户所在的 Active Directory 组,通常需要查询 Active Directory,并且还需要查找嵌套的组成员身份。

以下是一种快速获取您所在的组(包括嵌套的以及本地组)成员身份的方法。这段脚本查看您的存取令牌(它管理了您的各种权限)然后从您的令牌中读取所有 SID 并将 SID 转换为真实名称。

请注意您只能对当前用户使用这种技术。它很适合用作登录脚本,用来做一些基于组成员身份的操作。

[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups.Value |
  ForEach-Object {
    $sid = $_
    $objSID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
    $objUser.Value
  }

PowerShell 技能连载 - 设置注册表权限

设置注册表项的权限并不是一件小事。不过通过一些技巧,并不是一件大事。

首先,运行 REGEDIT 并创建一个测试项。然后,右击该项并且使用图形界面设置您想要的权限。

然后,运行这段脚本(请将 -Path 值设为您刚才定义的注册表项):

$path = 'HKCU:\software\prototype'
$sd = Get-Acl -Path $Path
$sd.Sddl | clip

这段代码将从您的注册表项中读取安全信息并将它复制到剪贴板中。

接下来,使用这段脚本为新创建的或已有的注册表项应用相同的安全设置。只需要将这段脚本中的 SDDL 定义替换成您刚创建的值:

# replace the content of this variable with the SDDL you just created
$sddl = 'O:BAG:S-1-5-21-1908806615-3936657230-2684137421-1001D:PAI(A;CI;KR;;;BA)(A;CI;KA;;;S-1-5-21-1907506615-3936657230-2684137421-1001)'

$Path = 'HKCU:\software\newkey'
$null = New-Item -Path $Path -ErrorAction SilentlyContinue

$sd = Get-Acl -Path $Path
$sd.SetSecurityDescriptorSddlForm($sddl)
Set-Acl -Path $Path -AclObject $sd

您可能需要以完整 Administrator 权限来运行这段脚本。如您所见,第一段脚本和您的测试注册表项只是用来生成 SDDL 文本。当您得到 SSDL 文本之后,您只需要将它粘贴入第二段脚本中。第二段脚本不再需要用到那个测试注册表项。

PowerShell 技能连载 - 阻止非管理员权限运行脚本

如果您明确知道您的脚本需要管理员权限,那么您必须在脚本的头部加上这行代码:

#requires -runasadministrator

这行代码确保脚本只能在调用者用后本地管理员权限的情况下运行。这行代码不仅会试图提升脚本权限,而且会确保脚本不会启动后运行一半失败。

PowerShell 技能连载 - 移除选定的 NTFS 权限

您也许需要从 NTFS 权限中移除某些权限。我们假设您希望移除某个用户的所有权限,因为他已经离开了这个部门。

请注意:您当然可以针对每个用户组来维护 NTFS 权限,并且为每个用户设置权限通常不是个好主意。但是,常常需要针对单个用户设置权限,以下示例脚本不仅可以移除这些权限,并且通过一些小修改还能成为查找这些权限的审计工具。

以下是一个简单的示例脚本。通过设置 $Path$Filter,脚本可以扫描 $Path 文件夹以及它的所有子文件夹中所有访问控制项和 $Filter 字符串相匹配的项目。它只会处理非继承的访问控制项。

输出结果中将被删除的访问控制项标记为红色如果所有访问控制项和过滤器都不匹配,则显示绿色。如果脚本没有返回任何东西,那么表示您扫描的文件夹中没有直接的访问控制项。

$Path = 'C:\somefolder
$Filter = 'S-1-5-*'

Get-ChildItem -Path C:\Obfuscated -Recurse -ErrorAction SilentlyContinue |
  ForEach-Object {

    $acl = Get-Acl -Path $Path
    $found = $false
    foreach($acc in $acl.access )
    {
        if ($acc.IsInherited -eq $false)
        {
            $value = $acc.IdentityReference.Value
            if($value -like $Filter)
            {
                Write-Host "Remove $Value from $Path " -ForegroundColor Red
                $null = $ACL.RemoveAccessRule($acc)
                $found = $true
            }
            else
            {
              Write-Host "Skipped $Value from $Path " -ForegroundColor Green
            }
        }
    }
    if ($found)
    {
# uncomment this to actually remove ACEs
#        Set-Acl -Path $Path -AclObject $acl -ErrorAction Stop
    }
}

PowerShell 技能连载 - PowerShell 上帝模式

若您要运行一个 PowerShell 脚本,您必须通过执行策略的允许。通常您需要使用这行代码来允许脚本运行:

然而,如果组策略禁止了脚本执行,那么这行代码将不起作用。在这种情况下,您可以使用这段代码来重新启用允许脚本执行(单个 PowerShell 会话中有效):

$context = $executioncontext.GetType().GetField('_context','nonpublic,instance').GetValue($executioncontext)
$field = $context.GetType().GetField('_authorizationManager','nonpublic,instance')
$field.SetValue($context,(New-Object Management.Automation.AuthorizationManager 'Microsoft.PowerShell'))

请注意这是一种取巧的办法,它重设了认证管理器,不能保证是否有副作用。使用后果自负。

顺便说一下,这种技术不算是一个安全问题。执行策略通常不是一个安全边界。它并不是设计成用来把坏人挡在外面的。它只是为了保护您自己不做错事。所以无论您是通过 cmdlet 还是通过这段代码来启用脚本执行,您都是对自己执行 PowerShell 代码负责。

PowerShell 技能连载 - 检查 Windows 更新

要检查 Windows 中安装的所有更新,有一个 COM 库可以帮您完成这个任务。但是这个库用起来不是很直观,而且也不支持远程。

所以我们设计了一个 PowerShell 函数,叫做 Get-WindowsUpdate。它默认情况下获取本地安装的更新,但是您也可以指定一个或多个远程计算机,并且获取它们的更新。

远程操作是借助 PowerShell 远程操作来实现的,所以只有远程计算机的 PowerShell 远程操作启用以后(例如,Windows Server 2012 默认启用 PowerShell 远程操作)才能使用,并且您需要远程计算机上的本地 Administrator 权限。

function Get-WindowsUpdate
{
  [CmdletBinding()]
  param
  (
    [String[]]
    $ComputerName,
    $Title = '*',
    $Description = '*',
    $Operation = '*'
  )

  $code = {
    param
    (
      $Title,
      $Description
    )


    $Type = @{
      name='Operation'
      expression={

    switch($_.operation)
    {
            1 {'Installed'}
            2 {'Uninstalled'}
            3 {'Other'}
    }
  }
}

    $Session = New-Object -ComObject 'Microsoft.Update.Session'
    $Searcher = $Session.CreateUpdateSearcher()
    $historyCount = $Searcher.GetTotalHistoryCount()
    $Searcher.QueryHistory(0, $historyCount) |
    Select-Object Title, Description, Date, $Type |
    Where-Object { $_.Title -like $Title } |
    Where-Object { $_.Description -like $Description } |
    Where-Object { $_.Operation -like $Operation }
  }

  $null = $PSBoundParameters.Remove('Title')
  $null = $PSBoundParameters.Remove('Description')
  $null = $PSBoundParameters.Remove('Operation')

  Invoke-Command -ScriptBlock $code @PSBoundParameters -ArgumentList $Title, $Description
}

这个函数也支持过滤器,所以要获得所有已安装的 Office 更新,您只需要这样做: