PowerShell 技能连载 - 用 PowerShell 为 VBScript 文件签名

您很可能知道 Set-AuthenticodeSignature 可以用来为 PowerShell 脚本签名。但您是否知道这个 cmdlet 可以为任何支持目标接口包 (SIP) 的任何文件呢?

这段代码可以从一个 PFX 文件中读取数字证书,然后从您的 home 文件夹中扫描 VBScript 文件,然后将数字签名应用到脚本文件上:

# change path to point to your PFX file:
$pfxpath = 'C:\Users\Tobias\Documents\PowerShell\testcert.pfx'
# change password to the password needed to read the PFX file:
# (this password was set when you exported the certificate to a PFX file)
$password = 'topsecret'

# load certificate
Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $password, 'Exportable')

# apply signature to all VBScript files
# REMOVE -WHATIF TO ACTUALLY SIGN
Get-ChildItem -Path $home -Filter *.vbs -Recurse -ErrorAction SilentlyContinue |
  Set-AuthenticodeSignature -Certificate $cert -WhatIf

PowerShell 技能连载 - 正确地按日期时间筛选

当您使用 Where-Object 通过日期或时间来过滤信息时,它工作得很好——前提是您使用了正确的过滤格式。请不要使用输出结果中的格式。

要指定一个日期或时间,请永远使用中性文化的格式:

“year-month-day hour:minute:second”,所以 2014 年 5 月 14 日应该表述成这样:“2014-05-12 12:30:00”。

或者换种方法处理:当您输出结果时,PowerShell 将根据您控制面板的设置来格式化日期和时间。当您输入信息(例如过滤规则)时,PowerShell 永远期望接收一个通用的日期和时间格式。这是有道理的:脚本须在任何文化环境中运行一致。而结果需要格式化成读者的语言文化格式。

所以要在您的 Windows 文件夹中查找所有自从 2012 年 4 月 30 日以来没有修改过的文件,请尝试以下代码:

PowerShell 技能连载 - 在 PowerShell ISE 中创建干净的测试环境

当您在 PowerShell ISE 编辑器中开发 PowerShell 脚本时,您需要在一个干净的环境中运行最终的测试,确保不会被之前运行时残留的变量或者函数干扰。

创建一个干净的测试环境的最简单方法是:选择文件菜单,然后选择“新建 PowerShell 选项卡”。这将为您带来一个新的标签页,并且该页代表一个全新的 PowerShell 宿主。完美的测试环境!

PowerShell 技能连载 - 从 PFX 文件中导入多个证书

Get-PfxCertificate 可以从 PFX 文件中导入数字证书。然而,他只能获取一个证书。所以如果您的 PFX 文件中包含多个证书,您无法使用这个 cmdlet 获取其它的证书。

若要从一个 PFX 文件中导入多个证书,只要使用以下代码:

$pfxpath = 'C:\PathToPfxFile\testcert.pfx'
$password = 'topsecret'

Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$cert.Import($pfxpath, $password, 'Exportable')
$cert

PowerShell 技能连载 - 在 Remoting 中使用完整限定名

当您尝试使用 PowerShell Remoting 时,您也许会因为您使用的机器名不是完整限定名而导致连接错误。Kerberos 验证可能需要也可能不需要使用完整限定名,这取决于您的 DNS 配置。

所以也许您使用如下方式连接的时候会发生错误:

Enter-PSSession -ComputerName storage1

当发生错误的时候,请向 DNS 查询完整限定名:

[System.Net.Dns]::GetHostByName('storage1').HostName

然后,用查出的名字来代替主机名。如果主机启用了 Remoting 并且正确地配置了,您现在应该可以连上了。

PowerShell 技能连载 - 确保向后兼容

假设您创建了这个函数:

function Test-Function
{
  param
  (
    [Parameter(Mandatory=$true)]
    $ServerPath
  )

  "You selected $ServerPath"
}

它现在可以正常工作,但是在半年之后的代码审查中,您的老板希望您使用标准的参数名称,将“ServerPath”改名为“ComputerName”。那么您对您的代码做出适当的修改:

function Test-Function
{
  param
  (
    [Parameter(Mandatory=$true)]
    $ComputerName
  )

  "You selected $ComputerName"
}

然而,您不能很容易地控制哪些人调用了您的函数,而且他们使用了旧的参数。所以要确保向后兼容,请确保您的函数使用旧的参数名也可以工作:

function Test-Function
{
  param
  (
    [Parameter(Mandatory=$true)]
    [Alias("ServerPath")]
    $ComputerName
  )

  "You selected $ComputerName"
}

旧的代码任然可以运行,并且新的代码(以及代码自动完成)将会使用新的名称:

PowerShell 技能连载 - 从 PFX 文件中导入证书

您可以使用 Get-PfxCertificate 来从 PFX 文件中读取数字证书,然后用数字证书来为脚本文件签名,例如:

$pfxpath = 'C:\PathToPfxFile\testcert.pfx'
$cert = Get-PfxCertificate -FilePath $pfxpath
$cert

Get-ChildItem -Path c:\myscripts -Filter *.ps1 | Set-AuthenticodeSignature -Certificate $cert

然而,Get-PfxCertificate 将会交互式地询问您导出证书至 PFX 文件时所用的密码:

要静默地导入证书,请使用这段代码:

$pfxpath = 'C:\PathToPfxFile\testcert.pfx'
$password = 'topsecret'

Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $password, 'Exportable')
$cert

PowerShell 技能连载 - 读取注册表的可扩充字符串值

当您读取一个“可扩充字符串”类型的注册表值时,它将自动展开文本中的所有环境变量值。

这个例子将从注册表中读取系统设备路径:

$path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
$key = Get-ItemProperty -Path $path
$key.DevicePath

该结果将是实际的路径。这问题不大,除非您希望获取原始(未展开的)注册表值。以下是读取原始值的例子:

$path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
$key = Get-Item -Path $path
$key.GetValue('DevicePath', '', 'DoNotExpandEnvironmentNames')

通过这种方式存取注册表值可以提供额外的信息:您还可以获取该值的数据类型:

$path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
$key = Get-Item -Path $path
$key.GetValueKind('DevicePath')

PowerShell 技能连载 - 设置(及删除)环境变量

PowerShell 可以很容易地读取环境变量。以下代码返回当前的 Windows 文件夹:

$env:windir

然而,如果您想永久地改变用户或机器的环境变量,您需要使用 .NET 的功能。以下是一个可以快速设置或删除环境变量的简单函数:

function Set-EnvironmentVariable
{
    param
    (
        [Parameter(Mandatory=$true, HelpMessage='Help note')]
        $Name,

        [System.EnvironmentVariableTarget]
        $Target,

        $Value = $null

    )

    [System.Environment]::SetEnvironmentVariable($Name, $Value, $Target )
}

要创建一个永久的环境变量,试试以下代码:

PS> Set-EnvironmentVariable -Name TestVar -Value 123 -Target User

请注意新的用户变量只对新运行的应用程序可见。已运行的应用程序将会保持它们的运行环境副本,除非它们显式地请求改变后的变量。

以下是删除该环境变量的代码:

PS> Set-EnvironmentVariable -Name TestVar -Value '' -Target User

PowerShell 技能连载 - 带对话框的必选参数

通常地,当您将一个函数参数标记为“必选的”,如果用户遗漏了这个参数,PowerShell 将提示用户:

function Get-Something
{
      param
      (
            [Parameter(Mandatory=$true)]
            $Path
      )

      "You entered $Path"
}

结果如下所示:

PS> Get-Something
cmdlet Get-Something at command pipeline position 1
Supply values for the following parameters:
Path:

以下是另一种选择:如果用户遗漏了 -Path,该函数弹出一个打开文件对话框:

function Get-Something
{
      param
      (
            $Path = $(
              Add-Type -AssemblyName System.Windows.Forms
              $dlg = New-Object -TypeName  System.Windows.Forms.OpenFileDialog
              if ($dlg.ShowDialog() -eq 'OK') { $dlg.FileName } else { throw 'No Path submitted'}
            )
      )

      "You entered $Path"
}