PowerShell 技能连载 - 解压序列化的数据
在前一个技能中您学习到了如何使用 Export-CliXml
命令来序列化数据并且用 Compress-Archive
将巨大的 XML 文件压缩成远远小于原始文件的尺寸。
今天,我们进行相反的操作:假设获得一个包含 XML 序列化数据的 ZIP 文件,然后恢复序列化的对象。当然这假设您已基于昨天的技能创建了这样的文件。
1 | # path to existing ZIP file |
在前一个技能中您学习到了如何使用 Export-CliXml
命令来序列化数据并且用 Compress-Archive
将巨大的 XML 文件压缩成远远小于原始文件的尺寸。
今天,我们进行相反的操作:假设获得一个包含 XML 序列化数据的 ZIP 文件,然后恢复序列化的对象。当然这假设您已基于昨天的技能创建了这样的文件。
1 | # path to existing ZIP file |
通过 Export-CliXml
命令可以很方便地将处理结果保存到文件通过 Import-CliXml
命令,序列化的信息可以同样方便地恢复。然而,生成的 XML 文件可能非常大。
幸运的是,在 PowerShell 5 中有一个新的命令名叫 Compress-Archieve
。当您创建了 XML 文件之后,您可以自动地将它转为一个 ZIP 文件。
以下是一些演示代码:它获取一个进程列表并且保存到 XML 文件。然后将 XML 文件压缩为 ZIP,并且删除原始的 XML 文件。
这么做的效率很高,因为 XML 是文本文件。您常常能看到压缩率在 3-5%(ZIP 的文件大小是原始文件的 3-5%):
1 | $Path = "$env:TEMP\data1.xml" |
If you’d like to safely store credentials (usernames and password) for your personal use in a file, here is a very simple yet extremely powerful approach. Take a look at this code:
$Path = "$home\Desktop\multipass.xml"
[PSCustomObject]@{
User1 = Get-Credential -Message User1
User2 = Get-Credential -Message User2
User3 = Get-Credential -Message User3
} | Export-Clixml -Path $Path
When you run it, it asks for three credentials and saves them to a “multipass” file on your desktop. All passwords are safely encrypted with your identity and your machines identity (which is why the file can only be read by you, and only on the machine where it was created).
To later on use one of the credentials, this is how you read them back in:
$multipass = Import-Clixml -Path $Path
You can then access the credentials via the properties “User1”, “User2”, and “User3”, and use the credentials in your scripts wherever a cmdlet asks for a credential:
PS C:\> $multipass.User1
UserName Password
-------- --------
AlbertK System.Security.SecureString
Start-Job
将一个脚本块发送到一个新的 PowerShell 进程,这样它可以独立并行运行。以下是一个非常简单的例子,演示 job 背后的概念:
1 | # three separate "jobs" to do: |
如果不用 job,那么需要等待 19 秒。幸好有了 job,这个过程可以缩短到 8 秒。
然而,也有副作用。由于 job 是在独立的应用中执行的,数据必须以 XML 序列化的方式来回传递。job 要传回越多的数据,就需要越多的时间。有些时候这个副作用会盖过了优点。
一个更好的方是在原来的 PowerShell 实例的子线程中运行 job。以下代码演示这种功能。它创建了一个新的名为 Start-MemoryJob
的命令,可以替代 Start-Job
。其余的代码完全不用改变。
使用 Start-MemoryJob
,不需要任何对象序列化。您的 job 可以快速平滑地运行,而没有返回大量的数据。而且,您现在获取到的是原始的对象。不再需要处理序列化过的对象。
1 | $code = @' |
当您对脚本文件签名时,会希望签名保持完整,即便为它签名的证书将来过期了。关键的是证书在签名的时候是合法的。
要确保这一点,您需要一个受信的机构提供的时间戳服务器。通过这种方式,您不仅是对一个脚本签名,而且添加了签名的时间。在证书有效期内,一切没有问题。
我们调整了前一个技能中的代码,并且添加了一个时间戳服务器的 URL。以下代码对用户数据文件中所有未签名的脚本文件增加签名。如果您确定要对您的脚本文件增加签名,请移除 -WhatIf
参数:
1 | # read in the certificate from a pre-existing PFX file |
在前一个技能中您学习了如何创建一个自签名的代码签名证书、将证书保存为一个 PFX 文件,并且将它加载进内存。
今天,假设您已经有了一个包含代码签名证书的 PFX 文件,我们将看看如何对 PowerShell 脚本进行数字签名。
以下代码在您的用户配置文件中查找所有 PowerShell 脚本,如果脚本还未经过数字签名,将会从 PFX 文件中读取一个证书对它进行签名:
1 | # read in the certificate from a pre-existing PFX file |
当您在编辑器中查看这些脚本时,您将在脚本的底部看到一个新的注释段。它包含了使用证书加密的脚本的哈希值。他也包含了证书的公开信息。
当您右键点击一个签名的脚本并且选择“属性”,可以看到谁对脚本做了签名。如果您确实信任这个人,您可以将它们的证书安装到受信任的根证书中。
一旦脚本经过数字签名,就可以很方便地审查脚本状态。签名可以告诉您谁签名了一个脚本,以及脚本的内容是否被纂改过。以下代码检查用户配置文件中所有的 PowerShell 脚本并显示签名状态:
1 | # find all scripts in your user profile... |
如果您收到“UnknownError”消息,这并不代表是一个未知错误,而是代表脚本未受纂改但签名在系统中是未知的(或非受信的)。
在前一个技能中我们演示了如何使用 New-SelfSignedCertificate
来创建新的代码签名证书,并且将它们存储为一个 PFX 文件。今天让我们来看看如何加载一个 PFX 文件。
假设您的 PFX 文件存放在 $env:temp\codeSignCert.pfx
。以下是读取该文件的代码:
1 | $cert = Get-PfxCertificate -FilePath "$env:temp\codeSignCert.pfx" |
这段代码执行时,将会提示输入密码。这个密码是您创建证书时输入的密码,并且它保护这个文件不被滥用。
当命令成功执行以后,可以从 $cert
变量获取证书详细信息:
1 | PS C:\> $cert |
证书对象包含了一系列方法:
1 | PS C:\> $cert | Get-Member -MemberType *Method |
例如,如果您想验证证书是否合法,只需要调用 Verify()
方法。结果是一个布尔值,$false
代表证书不被 Windows 信任。
明天,我们将会使用证书对 PowerShell 脚本进行数字签名。
如果您想对您的脚本进行数字签名,首先您需要一个包含“代码签名”功能的数字证书。如果只是测试,您可以方便地创建免费的个人自签名证书。不要期望其他人信任这些证书,因为任何人都可以创建它们。这是一种很好的测试驱动代码签名的方法。
从 PowerShell 4 开始,New-SelfSignedCertificate
cmdlet 可以创建签名证书。以下代码创建一个包含私钥和公钥的 PFX 文件:
1 | #requires -Version 5 |
在接下来的技能里,我们将看一看可以用新创建的证书来做什么。
LDAP 过滤器类似 Active Directory 中使用的查询语言。并且如果您安装了 Microsoft 的 RSAT 工具,您可以很方便地用 ActiveDirectory 模块中的 cmdlet 来用 LDAP 过滤器搜索用户、计算机,或其它资源。
以下代码将查找所有无邮箱地址的用户:
1 | $filter = '(&(objectCategory=person)(objectClass=user)(!mail=*))' |
即便您没有 RSAT 工具和指定的 ActiveDirectory cmdlet 的权限,LDAP 过滤器也十分有用:
1 | $filter = '(&(objectCategory=person)(objectClass=user)(!mail=*))' |
PowerShell 远程处理是一个查询多台计算机的快速方法,因为 PowerShell 远程处理是并行工作的。以下是一个演示一系列有趣技术的真实案例。
目标是从两台计算机中获取正在运行的进程的列表,然后查找区别。
为了速度最快,进程列表是通过 PowerShell 远程处理和 Invoke-Command
,并且结果是从两台计算机获得的。
要区分输入的数据,我们使用了 Group-Object
。它通过计算机名对数据集分组。结果是一个哈希表,而计算机名是哈希表的键。
下一步,用 Compare-Object
来快速比较两个列表并查找区别:
1 | # get data in parallel via PowerShell remoting |