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 更新,您只需要这样做:

评论

PowerShell 技能连载 - 获取免费的速查表

有两个很棒的 PowerShell 速查表版本。一个是由 powershellmagazine.com 创建的一系列速查表。它刚刚升级,现在覆盖了 PowerShell 4.0 并包括了一个期望状态配置(DSC)的速查表。您可以从 Microsoft 下载它:

http://www.microsoft.com/en-us/download/details.aspx?id=42554

另外一个是我们的每月技巧文摘,地址如下:

http://powershell.com/cs/media/28/default.aspx

评论

PowerShell 技能连载 - 开始学习 DSC

期望状态配置(DSC)是 PowerShell 4.0 中的一个新特性。通过 DSC,您可以编写简单的配置脚本并且将它们应用到本地或远程的机器上。以下是一个供您入门的示例脚本:

Configuration MyConfig
{
  # Parameters are optional
  param ($MachineName)
  # A Configuration block can have one or more Node blocks
  Node $MachineName
  {
    Registry RegistryExample
    {
      Ensure = 'Present' # You can also set Ensure to "Absent"
      Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\ExampleKey'
      ValueName ='TestValue'
      ValueData ='TestData'
    }
  }
}

MyConfig -MachineName $env:computername -OutputPath c:\dsc
Start-DscConfiguration -Path c:\dsc -Wait 

配置项“MyConfig”使用了“Registry”资源来确保指定的注册表项存在。您可以在 DSC 脚本中使用更多的资源,例如增加(或删除)本地用户或文件,解压一个 MSI 包或 ZIP 文件,或启动/停止一个服务等等。

运行该配置只会创建一个 MOF 文件。要应用该 MOF 文件,请使用 Start-DSCConfiguration cmdlet。请使用 -Wait 来等待配置生效。否则,该配置将会在后台以任务的方式完成。

评论

PowerShell 技能连载 - 编译二进制 Cmdlet

PowerShell 的函数可以模拟一个真实二进制 cmdlet 的所有特性,但是 PowerShell 函数是 PowerShell 明文的代码,每个人都可以看到它的内容。

如果您是一个开发者并且有兴趣创开发二进制 cmdlet,以下是一个快速的入门。该入门演示如何用纯 PowerShell 创建并编译真正的 cmdlet:

# C# definition for cmdlet                
$code = @'
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Management.Automation;

namespace CustomCmdlet
{
    [Cmdlet("Get", "Magic", SupportsTransactions = false)]
    public class test : PSCmdlet
    {
        private int _Age;

        [Alias(new string[]
        {
            "HowOld", "YourAge"
        }), Parameter(Position = 0,ValueFromPipeline = true)]

        public int Age
        {
            get { return _Age; }
            set { _Age = value; }
        }

        private string _Name;

        [Parameter(Position = 1)]
        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }


        protected override void BeginProcessing()
        {
            this.WriteObject("Good morning...");
            base.BeginProcessing();
        }
        protected override void ProcessRecord()
        {
            this.WriteObject("Your name is " + Name + " and your age is " + Age);
            base.ProcessRecord();
        }
        protected override void EndProcessing()
        {
            this.WriteObject("That's it for now.");
            base.EndProcessing();
        }
    }
}

'@
# compile C# code to DLL
# use a timestamp to create unique file names
# while testing, when a DLL was imported before, it is in use until PowerShell closes
# so to do repeated tests, use different DLL file names
$datetime = Get-Date -Format yyyyMMddHHmmssffff
$DLLPath = "$env:temp\myCmdlet($datetime).dll"
Add-Type -TypeDefinition $code -OutputAssembly $DLLPath

# import a module
Import-Module -Name $DLLPath -Verbose

现在您可以可以使用新创建的 Get-Magic cmdlet。它包含了一个 cmdlet 能实现的所有特性,包括参数、参数别名,甚至支持管道:

请注意例子中主要的 PowerShell 代码只是为了创建并编译 DLL。当 DLL 已经存在时,您需要的只是这行代码(例如,在分发的产品中):

Import-Module -Name $DLLPath

要开发复杂的二进制 cmdlet,您可能更希望在 C# 开发环境,例如 Visual Studio 中工作。您所需的只是添加 PowerShell 程序集的引用。PowerShell 程序集的路径可以用这行代码方便地获取到:

它将会把 PowerShell 程序集的路径输出到您的剪贴板中。

请注意只是编译 C# 代码并不会为您的知识资产带来更多的保护,因为它可以被反编译。所以不要用这种方式来“保护”秘密的信息,比如说密码。通过二进制 cmdlet,您可以有机会使用专业的防拷贝软件以及混淆器。额外的保护层并没有纯 PowerShell 代码的版本。

评论

PowerShell 技能连载 - 添加或重置 NTFS 权限

当您需要为一个文件添加一个新的 NTFS 存取规则,或禁用继承并添加新的规则,以下是一个示例脚本,演示这个技巧并且为您提供一个模板。

这个脚本创建一个测试文件,然后以当前用户的身份定义一个新的存取规则。这个规则包含读取和写入权限。这个新规则被添加到已存在的安全描述符中。另外,将禁用继承。

# create a sample file to apply security rules to
$Path = "$env:temp\examplefile.txt"
$null = New-Item -Path $Path -ItemType File -ErrorAction SilentlyContinue

# use current user or replace with another user name
$username = "$env:USERDOMAIN\$env:USERNAME"

# define the new access rights
$colRights = [System.Security.AccessControl.FileSystemRights]'Read, Write' 
$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None 
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None 
$objType =[System.Security.AccessControl.AccessControlType]::Allow 
$objUser = New-Object System.Security.Principal.NTAccount($username) 

# create new access control entry
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
    ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType) 

# get existing access control list for a file or folder
$objACL = Get-Acl -Path $Path 

# add rule
$objACL.AddAccessRule($objACE) 

# disable inheritance (if needed)
$objACL.SetAccessRuleProtection($true, $false)

# apply changed access control list to file
Set-Acl -Path $Path -AclObject $objACL

# show file in the File Explorer
explorer.exe "/SELECT,$Path"

执行完成之后,该脚本在文件管理器中打开测试文件,并选中它。您可以右键单击该文件并选择 属性 > 安全 来查看新的设置。

要查看有哪些存取权限可用,请在 ISE 编辑器中键入以下这行:

这将自动打开上下文菜单并列出所有可用的设置。

评论

PowerShell 技能连载 - 将文本数组转换为字符串

某些时候,文本文件中的文字内容需要由其它命令来读取和处理。通常,您需要用 Get-Content 命令来读取文本文件内容,然后将结果传递给其它命令。但这有可能会失败。

以下是注意点:请牢记 Get-Content 总是返回一个文本行的数组,而不是单个文本行。所以当使用一个接收字符串而不是接收一系列文本行(字符串数组)的命令时,您需要将这些文本行转换为文本。

从 PowerShell 3.0 开始,Get-Content 拥有一个新的开关变量 -Raw。它不仅提升了读取大量文本的速度,而且一次性返回原始文本文件的整个内容,而不是将其分割成文本行。

PS> $info = Get-Content $env:windir\windowsupdate.log
PS> $info -is [Array]
True

PS> $info = Get-Content $env:windir\windowsupdate.log -Raw
PS> $info -is [Array] 
False

如果您已经有文本数组并且希望将它们转换为一个单一的文本,请使用 Out-String

PS> $info = 'One', 'Two', 'Three'
PS> $info -is [Array]
True

PS> $all = $info | Out-String
PS> $all -is [Array]
False 
评论

PowerShell 技能连载 - ISE 的缺陷导致调试器阻塞

在 Powershell ISE 中,有一个模糊的缺陷,可能会导致调试器死锁。受影响的 PowerShell 版本有 3.0 和 4.0。

以下是一段测试脚本:

$test = @"
    Some lines
    of text
"@

$test 

在 ISE 编辑器中将这段代码保存为脚本,然后在第一行中设置一个断点:单击第一行的任何地方,然后按下 F9 键。该行将会变成红色。

当您启动脚本时,调试器将会在断点处停下,然后您可以按 F10 键单步跟踪代码。这可以正常工作。

现在,在变量定义之前加入一些空格:

   $test = @"
    Some lines
    of text
"@

$test 

当您现在调用调试器的时候,它将会死锁,并且 ISE 不会恢复。您还可以保存未保存的脚本,但您再也无法停止 ISE 的运行空间。

这个缺陷在对通过 here string 定义的脚本变量缩进的时候暴露出来。

评论