PowerShell笔记 - 9.脚本
本系列是一个重新学习PowerShell的笔记,内容引用自
PowerShell中文博客
编写和运行脚本
一个Powershell脚本仅仅是一个包含Powershell代码的文本文件。
如果这个文本文件执行,Powershell解释器会逐行解释并执行它的的语句。Powershell脚本非常像以前CMD控制台上的批处理文件。您可以通过非常简单的文本编辑工具创建Powershell脚本。
通过重定向创建脚本
PS C:\PowerShell> '"这是一个PowerShell脚本,ps1格式"' > test.ps1 PS C:\PowerShell> .\test.ps1 这是一个PowerShell脚本,ps1格式
通过工具创建或编辑脚本
PS C:\PowerShell> notepad.exe test2.ps1 PS C:\PowerShell> ls
Directory: C:\PowerShell
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2021/9/15 10:46 0 test.exe
-a---- 2021/9/15 13:48 54 test.ps1
-a---- 2021/9/14 13:51 58 test.txt
-a---- 2021/9/15 13:50 0 test2.ps1
执行策略限制
Powershell一般初始化情况下都会禁止脚本执行。脚本能否执行取决于Powershell的执行策略。
PS E:> .\MyScript.ps1
无法加载文件 E:MyScript.ps1,因为在此系统中禁止执行脚本。有关详细信息,请参阅 "get-help about_sign
ing"。
所在位置 行:1 字符: 15
+ .MyScript.ps1 < <<<
+ CategoryInfo : NotSpecified: (:) [], PSSecurityException
+ FullyQualifiedErrorId : RuntimeException
只有管理员才有权限更改这个策略。非管理员会报错。
查看脚本执行策略,可以通过:
PS C:\PowerShell> Get-ExecutionPolicy RemoteSigned
更改脚本执行策略,可以通过
PS C:\PowerShell> Set-ExecutionPolicy UnRestricted
Execution Policy Change
The execution policy helps protect you from scripts that you do not trust. Changing the execution policy might expose
you to the security risks described in the about_Execution_Policies help topic at
https:/go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the execution policy?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"):
脚本执行策略类型为:Microsoft.PowerShell.ExecutionPolicy
查看所有支持的执行策略:
PS C:\PowerShell> $execut = Get-ExecutionPolicy PS C:\PowerShell> $execut.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True ExecutionPolicy System.Enum
PS C:\PowerShell> $execut.GetType().FullName Microsoft.PowerShell.ExecutionPolicy
PS C:\PowerShell> [System.Enum]::GetNames([Microsoft.PowerShell.ExecutionPolicy]) Unrestricted
RemoteSigned
AllSigned
Restricted
Default
Bypass
Undefined
Unrestricted:权限最高,可以不受限制执行任何脚本。
Default:为Powershell默认的策略:Restricted,不允许任何脚本执行。
AllSigned:所有脚本都必须经过签名才能在运行。
RemoteSigned:本地脚本无限制,但是对来自网络的脚本必须经过签名。
关于Powershell脚本的签名在后续会谈到。
像命令一样执行脚本
怎样像执行一个命令一样执行一个脚本,不用输入脚本的相对路径或者绝对路径,甚至*.ps1扩展名。
那就将脚本的执行语句保存为别名吧:
Get-Date
$arr = 1..5
$arr
PS C:\PowerShell> Set-Alias MarkExe \test.ps1 PS C:\PowerShell> MarkExe
2021年9月15日 14:03:12
1
2
3
4
5
给脚本传递参数
怎样将一个脚本稍作润色,让它能够根据用户的输入,处理并输出相应的结果,而不是只产生一成不变的输出。怎样将参数传递给脚本,这是本篇讨论的内容。
$args返回所有的参数
传递给一个函数或者一个脚本的参数都保存在$args变量中。可以先打开记事本,输入脚本:
Write-Host "Hello,$args"
保存后,通过控制台执行脚本:
PS C:\PowerShell> 'Write-Host "Hello,$args"' > test.ps1 PS C:\PowerShell> .\test.ps1 Hello,
PS C:\PowerShell> .\test.ps1 1 2 -Name c Hello,1 2 -Name c
$args数组参数
默认情况下,传递给一个Powershell脚本的参数类型为数组,例如:
PS C:\PowerShell> .\test.ps1 Hua Hua Hello,Hua Hua
上面的文本中包含多个连续的空格,可是当脚本把参数输出时却不存在连续的空格了。
那是因为脚本会把文本根据白空格截断并转换成数组。如果不想文本被当成数组那就把它放在引号中。
PS C:\PowerShell> .\test.ps1 "Hua Hua" Hello,Hua Hua
在$args中逐个访问参数
因为$args是一个数组,自然可以通过索引访问数组的每一个元素。可以将test.sp1的内容改为:
For($i=0;$i -lt $args.Count; $i++)
{
Write-Host "parameter $i : $($args[$i])"
}
然后控制台执行:
PS C:\PowerShell> .\test.ps1 Hua Hua "Hua Hua" parameter 0 : Hua
parameter 1 : Hua
parameter 2 : Hua Hua
在脚本中使用参数名
通过Powershell传递参数固然方便,但是如果用户不知道参数的传递顺序,也是很郁闷的,例如在Myscript.ps1中输入:
\(args[0]-\)args[1]
执行脚本发现参数的顺序不同,结果也不同:
PS C:\PowerShell> '$args[0]-$args[1]' >test.ps1 PS C:\PowerShell> .\test.ps1 1 2 -1
PS C:\PowerShell> .\test.ps1 2 1 1
所以最好的方式给参数指定名称,其中param给参数指定名称,输入以下的脚本并执行:
param($num1,$num2)
$num1 - $num2
PS C:\PowerShell> .\test.ps1 -num1 1 -num2 2 -1
验证参数
给脚本的参数绑定数据类型,绑定帮助信息。一旦脚本缺少参数,或者输入的参数类型不正确,就提醒用户:
输入脚本:
param([int]$num1 = $(throw"这不是一个正确的数字"), [int]$num2 = $(throw "这也不是个正确的数字"))
$num1 - $num2
执行脚本:
PS C:\PowerShell> \test.ps1 throw这不是一个正确的数字 : The term 'throw这不是一个正确的数字' is not recognized as the name of a cmdlet, function, script file, or opera
ble program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At \test.ps1:1 char:22
+ param([int]$num1 = $(throw"这不是一个正确的数字"), [int]$num2 = $(throw "这也不是个正 ...
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (throw这不是一个正确的数字:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
这也不是个正确的数字
At \test.ps1:1 char:57
+ ... int]$num1 = $(throw"这不是一个正确的数字"), [int]$num2 = $(throw "这也不是个正确的数字"))
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (这也不是个正确的数字:String) [], RuntimeException
+ FullyQualifiedErrorId : 这也不是个正确的数字
变量的作用域
Powershell默认使用全局作用域global: ,但是在函数和脚本中分别使用函数作用域function:和脚本作用域script: 。
一旦脚本执行结束,存在于脚本作用域的变量也会消失。但是有一点,如果一个变量在脚本外定义,在脚本内没有定义,在脚本内使用时会把外面的变量引渡过来。
在脚本中输入:
$temp
执行脚本:
PS C:\PowerShell> '$temp' > test.ps1 PS C:\PowerShell> $temp = 1 PS C:\PowerShell> .\test.ps1 1
在脚本中尝试改变变量$temp,但是脚本内的变量不会影响脚本外的变量,输入并执行脚本:
PS C:\PowerShell> '$temp = 2;$temp' > test.ps1 PS C:\PowerShell> .\test.ps1 2
PS C:\PowerShell> $temp 1
增强脚本的可读性
如果你愿意,你可以把一个脚本写的非常长,问题是脚本的代码量越大,可读性越差。最好的方式在写脚本时融入函数和类库的概念:
函数:把实现一些小功能的代码写成一个函数,不仅可以增强代码的可读性,还可以很方便的重用。一旦你创建了一个实现特定功能的函数,也可以下次在其它脚本中使用。
类库:把需要的函数嵌入进类库中,就不用每次在执行脚本时拷贝函数,并且还可以在需要时扩充它。另外以函数的方式构建类库,还可以让你更专注特定功能的具体实现,降低脚本开发的复杂度。
在脚本中使用函数
要在脚本中使用函数,最简单的方法自然是将函数直接写在脚本中,不像其它脚本语言,Powershell中的函数必须先定义后使用。
在MyScript.ps1中输入:
param([int]$n = $(throw "请输入一个正整数"))
Function Factorial([int]$n) {
$total = 1
for ($i = 1; $i -le $n; $i++) {
$total *= $i
}
return $total
}
Factorial $n
PS C:\PowerShell> test.ps1 10 3628800
将脚本分为工作脚本和类库
真正的脚本开发需要处理的问题可能包含许多函数。如果在一个脚本的开头定义许多函数,脚本会显得很凌乱。把函数和工作脚本分开,可以隔离函数,使它不容易被修改。
将Factorial函数保存在PSLib.ps1
Function Factorial([int]$n)
{
$total=1
for($i=1;$i -le $n;$i++)
{
$total*=$i
}
return $total
}
将脚本修改为:
param([int]$n=$(throw "请输入一个正整数"))
. .PSLib.ps1
Factorial $n
执行脚本:
PS C:\PowerShell> test.ps1 10 3628800
脚本在执行时,先加载类库中的函数。加载函数类库和执行脚本类似,只需要在前面增加一个句号[.
],中间有空格。
类库脚本集中存放
在开始使用类库脚本工作之前,最好先制定出一个存储脚本类库的策略。一种方法是和工作脚本存放在一起,可以使用相对路径;另一种方法是分开存放,加载时就得使用绝对路径了。最好在当前用户的私人目录中存放脚本,相对来说比较安全。
例如下面的例子:
PS E:> md $env:appdataPSLib
目录: C:UsersbaozhenAppDataRoaming
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2012/4/30 22:47 PSLib
PS E:> copy .PSLib.ps1 $env:APPDATAPSLib
创建管道脚本
我们可以像创建管道函数那样创建管道脚本,具体采用低速顺序模式,还是高速流模式,这取决于具体的编程实现。
低速顺序模式
如果你在脚本中使用管道,脚本收集上一个语句的执行结果,默认保存在$input自动变量中。但是直到上一条语句完全执行彻底,管道脚本才会执行。
创建脚本:
pipeline.ps1
foreach ($element in $input)
{
if($element.Extension -eq ".exe")
{
Write-Host -fore "red" $element.Name
}
else
{
Write-Host -fore "Green" $element.Name
}
}
执行脚本:
PS C:\PowerShell> ls $env:windir | .\pipeline.ps1
如果这样执行:
PS C:\PowerShell> ls $env:windir -Recurse | .\pipeline.ps1
控制台会被冻结,因为存储的中间结果在玩命的吃内存。这个也是低速顺序模式的缺点。
高速流模式
在Powershell脚本的处理中,绝大多数情况下遇到的都是集合,一旦上一条命令产生一个中间结果,下一条命令就对这个中间结果及时处理,及时释放资源。这样可以节省内存,也减少了用户的等待时间。在处理大量数据时,尤其值得推荐。高速流模式的管道定义包括三部分:begin
,process
,end
。上面的描述中提到了中间结果,中间结果保存在$_自动化变量中。
输入脚本文件:
begin
{
Write-Host "管道脚本环境初始化"
}
process
{
$ele=$_
if($_.Extension -ne "")
{
switch($_.Extension.tolower())
{
".ps1" {"脚本文件:"+ $ele.name}
".txt" {"文本文件:"+ $ele.Name}
".gz" {"压缩文件:"+ $ele.Name}
}
}
}
end
{
Write-Host "管道脚本环境恢复"
}
执行脚本文件:
PS C:\PowerShell> ls | .\pipeline.ps1 管道脚本环境初始化
脚本文件:pipeline.ps1
脚本文件:test.ps1
文本文件:test.txt
管道脚本环境恢复
自动执行脚本之profile
在Powershell控制台的许多更改只会在当前会话有效。一旦关闭当前控制台,你自定义地所有别名、函数、和其它改变将会消失,除非将更改保存在windows环境变量中。这也就是为什么我们需要profile来保存一些基本的初始化工作。
四中不同的profile脚本
Powershell支持四种可以用来初始化任务的profile脚本。应用之前要弄清楚你的初始化是当前用户个人使用,还是所有用户。如果是个人使用,可以使用”当前用户profile“,但是如果你的初始化任务是针对所有用户,可是使用“所有用户profile”。
Profile | 描述 位置 |
---|---|
所有用户 | 所有用户共有的profile $pshome\profile.ps1 |
所有用户(私有) | powershell.exe 中验证。 $pshome\Microsoft.PowerShell_profile.ps1 |
当前用户 | 当前用户的profile $((Split-Path $profile -Parent)+ “\profile.ps1”) |
当前用户(私有) | 当前用户的profile;只在Powershell.exe中验证 $profile |
我们注意到上面的四种profile有两个private。一旦声明为private,只有个microsoft的Powershell自身才会去调用,不会对其它引用powershell的组件有效。
创建自己的profile
Profile脚本并不是强制性的,换言之,profile可有可无。
下面会很方便的创建自己的profile。 在控制台执行: notepad $((Split-Path $profile -Parent) + "\profile.ps1")
如果不存在profile默认会创建,在打开的记事本中输入: Set-Alias edit notepad.exe
也就是给notepad添加edit
别名,保存关闭,之后重启控制台,输入: edit $((Split-Path $profile -Parent) + "\profile.ps1")
控制台会调用记事本打开之前的profile,可见edit别名已经生效。
创建全局profile
创建全局的profile也是很容易的,如上,只是文件的位置稍有改变; 需要注意的是,创建全局profile需要管理员权限,没有管理员权限,该文件或者文件夹拒绝访问。还有一点也须注意:在vista系统中,即使你拥有管理员权限,但是没有通过administrator登录,并且系统没有禁用UAC,也是拒绝更改的。除非你鼠标右键单击Powershell快捷方式,以管理员权限运行。
脚本数字签名
脚本很容易被冒名顶替或者更改,因为它们是由纯文本构成的。数字签名为脚本提供了更高的安全性,因为它能确定脚本和脚本的编辑者的唯一性,并且不能被更改。作为脚本的发布者,你能确定你的脚本没有被恶意篡改。即使专家也无能为力,因为这种机制是基于复杂逻辑的。幸运的是,在实际应用中,你不需要深究这些细节,只需要掌握Powershell脚本签名的机制和过程。
准备一个合适的证书
因为不能使用传统的纸质签名给Powershell脚本进行签名,你需要另一个工具“证书”。证书就像一把私有并且安全的钥匙。证书是你的个人电子身份特征。这把私密的钥匙确保只有证书的拥有者使用证书进行脚本签名。
可以通过mmc添加管理单元查看证书,但是在Powershell中有专门查看证书的支持。可以通过虚拟驱动器cert:
查看本机支持的证书。
创建自签名证书
创建一个自签名证书,需要用到microsoft的工具,makecert.exe 。这个工具不能单独下载,但是它包含在微软的.NET framework中,如果你的电脑上已经安装了Visual studio 那就方便多了。
开始->所有程序-Microsoft Visual Studio 2019->Visual Studio Tools->Visual Studio 命令提示(2019)
makecert.exe -pe -r -n "cn=MosserPowerShellTestCert" -eku 1.3.6.1.5.5.7.3.3 -ss "my"
Succeeded
这里要稍微注意 -eku 参数:1.3.6.1.5.5.7.3.3,不能是其它,否则证书的预期目的属性就不是代码签名了。
上面创建的证书会自动保存在CurrentUserMy 路径下面。可以在Powershell中查看:
PS E:> ls cert:CurrentUserMy | where {$_.subject -eq "CN=MosserPowerShellTestCert"}
目录: Microsoft.PowerShell.SecurityCertificate::CurrentUserMy
Thumbprint Subject
---------- -------
BA61AF0B8A856422AD9EF86104C8CEDB2583A21A CN=MosserPowerShellTestCert
验证代码签名证书
查看支持代码签名的证书
查看证书的签发者,代表,序列号,指纹。
## 查看预期目的为代码签名的证书:
$certs = @(Dir cert:CurrentUserMy -codeSigningCert)
"找到 {0} 个代码签名证书" -f $certs.count
# 找到 1 个代码签名证书
## 选择 刚才创建的证书
$certificate=ls cert:CurrentUserMy | where {$_.subject -eq "CN=MosserPowerShellTestCert"}
## 证书的代表
$certificate.subject
# CN=MosserPowerShellTestCert
## 证书的签发者
$certificate.issuer
# CN=MosserPowerShellTestCert
## 证书的序列号,指纹
$certificate | select SerialNumber,Thumbprint | fl *
# SerialNumber : C23F35EA85D9A5AB466C07A7C0469A78
# Thumbprint : 586A4332F0528867DA6A0900FCF0938EDD277E22
声明一个证书受信任
你会发现,在你指定证书的类型,颁发者的名称等信息后,证书的原始数据(RawData)会自动生成。这样你不能假冒别人生成一个证书,别人也不能假冒你的名字生成一个证书。如果通过Powershell查看之前生成的证书是否受信任,答案为否
PS E:> $certificate.Verify()
False
为什么我们刚才生成的证书不受信任呢?我们可以通过一个简单的步骤找到答案。在.NET 中有一个方法:DisplayCertificate()可以通过对话框显示证书,位于System.Security.dll中。这个dll默认没有引用,需要添加引用,之后显示证书对话框。
PS E:> [System.Reflection.Assembly]::LoadWithPartialName("System.Security")
GAC Version Location
--- ------- --------
True v2.0.50727 C:windowsassemblyGAC_MSILSystem.Security2.0.0.0__b03f5f7f11d50a3aSys...
PS E:>[System.Security.Cryptography.x509Certificates.X509Certificate2UI]::DisplayCertificate($certificate)
对话框提示:此CA根证书不受信任,要启用信任,请将该证书安装到”受信任的根证书颁发机构“存储区。
所以接下来可以将该证书复制到受信任的存储区。可以通过certmgr.msc 手动操作,也可以通过Powershell自动化操作。
PS E:> $rootStore= New-Object system.security.cryptography.X509Certificates.x509Store("root","Curre
ntuser")
$rootStore.Open("ReadWrite")
$rootStore.Add($certificate)
$rootStore.Close()
在执行Add操作时,会有一个确认的对话框,确定即可。
接下来我们查看一下验证信息。
PS E:> $certificate.Verify()
True
给Powershell 脚本签名
给Powershell脚本进行数字签名只需要两步:找的一个受信任的代码签名证书,剩下的工作请交给:Set-AuthenticodeSignature
吧。
PS E:> 'Write-Host "我的第一个签名脚本"' > firstSignScript.ps1
PS E:> $certificate=ls cert:CurrentUserMy | where {$_.subject -eq "CN=MosserPowerShellTestCert"}
PS E:> Set-AuthenticodeSignature .firstSignScript.ps1 $certificate
目录: E:
SignerCertificate Status Path
----------------- ------ ----
586A4332F0528867DA6A0900FCF0938EDD277E22 Valid firstSignScript.ps1
PS E:> Get-Content .firstSignScript.ps1
Write-Host "我的第一个签名脚本"
# SIG # Begin signature block
# MIIEIQYJKoZIhvcNAQcCoIIEEjCCBA4CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUnxRdr+yE6sFotfvZjfn8k15W
# OtigggI0MIICMDCCAZ2gAwIBAgIQwj816oXZpatGbAenwEaaeDAJBgUrDgMCHQUA
# MCMxITAfBgNVBAMTGE1vc3NlclBvd2VyU2hlbGxUZXN0Q2VydDAeFw0xMjA2MTYx
# MzAyMjZaFw0zOTEyMzEyMzU5NTlaMCMxITAfBgNVBAMTGE1vc3NlclBvd2VyU2hl
# bGxUZXN0Q2VydDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAr/2eZ6iS3Zi4
# Q2RsXFPRmDynztxPwArZ6SK663R6X2Dfqwv+kuev4VbEHJ20Bvd9yLvCS4QgCCR6
# n0D+ELfBy6aRpst51dNKNGV74TZIBu1M5EKG2+didLrKTx3lwEC66Bl+QyFiOzcH
# ZhQcaZzgdx8m8EN10/B2cDg9Tm9ppQsCAwEAAaNtMGswEwYDVR0lBAwwCgYIKwYB
# BQUHAwMwVAYDVR0BBE0wS4AQjHzaaSg4KlNdyvIpJNjeiqElMCMxITAfBgNVBAMT
# GE1vc3NlclBvd2VyU2hlbGxUZXN0Q2VydIIQwj816oXZpatGbAenwEaaeDAJBgUr
# DgMCHQUAA4GBAFA3lvWcbA8mWndKdIOCzQUbC9/+1vIeQRGaH7L6U6OHZuV2IBw1
# EpLxz1/dyFEMNZmy9z+/YjfJi774UY1eTzOJnz0AYKGPpM0BK2ieGZzPDIlbkpv1
# ywrv5BtRt053MNHRYaZQP0v9Sp6pOB4h10tKnvh0DW882zRPeB4hkK+fMYIBVzCC
# AVMCAQEwNzAjMSEwHwYDVQQDExhNb3NzZXJQb3dlclNoZWxsVGVzdENlcnQCEMI/
# NeqF2aWrRmwHp8BGmngwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKA
# AKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFMgyEZ64UFors3z9JGuKLVxh
# P2hLMA0GCSqGSIb3DQEBAQUABIGAMHFJHMVlauxKGIo2p9ieFBVp4Am6n533k89j
# 7pQXKOGmU/sG9d8PILifHLJZw7BU66+uZFvOSXlUxvqaPRAdeosc2BLDPf5Cu6o7
# 61BfSJc2H5dQCgbK/90OKmeJp4KJQRCk7HLEBvV23ddVSyl4CPplbUcTVmo92Zd1
# B/Moxro=
# SIG # End signature block
递归给所有脚本文件签名
给当前文件下的所有脚本签名:
PS E:> Set-AuthenticodeSignature (ls *.ps1) $certificate
目录: E:
SignerCertificate Status Path
----------------- ------ ----
586A4332F0528867DA6A0900FCF0938EDD277E22 Valid firstSignScript.ps1
586A4332F0528867DA6A0900FCF0938EDD277E22 Valid MyScript.ps1
586A4332F0528867DA6A0900FCF0938EDD277E22 Valid pipeline.ps1
586A4332F0528867DA6A0900FCF0938EDD277E22 Valid PSLib.ps1
如果你喜欢你甚至可以递归使用:
Set-AuthenticodeSignature (Dir -recurse -include *.ps1) $certificate
使用对话框选择证书
如果机器上安装了代码签名的证书有许多,你可以通过friendName 或者证书的名称,证书的指纹,过滤一个证书供脚本签名。
Dir cert:CurrentUserMy | where {$_.subject -eq "CN=MosserPowerShellTestCert"}
另一种方法是通过.NET中的内置的对话框进行选择。将查询到的证书传递给SelectFromCollection()方法,在在作此操作之前必须将证书放在一个特殊的集合中。
# 对话框文本:
$title = "可用的证书"
$text = "请选择用于代码签名的证书:"
# Find certificates:
$certificates = Dir cert: -recurse -codeSigningCert
# 加载 System.Security 类库
# 将证书存放在特殊的集合(X509Certificate2Collection)中:
[Reflection.Assembly]::LoadWithPartialName("System.Security")
$collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$certificates | ForEach-Object { $collection.Add($_) }
# 显示选项:
$certificate =[System.Security.Cryptography.x509Certificates.X509Certificate2UI]::`
SelectFromCollection($collection, $title, $text, 0)
# 使用选择的证书进行数字签名
Set-AuthenticodeSignature -Certificate $certificate[0] -FilePath .firstSignScript.ps1
Powershell脚本签名验证
在脚本中签名到底能带来什么好处,那就是可以进行验证。可以手动验证,也可以自动验证。签名验证会告诉你脚本是否信任,或者是否包含了恶意篡改。
用户自行验证:手动验证,可以检查一个脚本是否包含签名代码,签名者是谁?该签名者是否受信任。
自动验证:如果你将Powershell的脚本执行策略设置为AllSigned. Powershell会在你尝试运行脚本时自动验证,代码和脚本签名是否一致。并且会询问签名者是否受信任。
手动验证
Get-AuthenticodeSignature
命令可以验证签名。例如创建一个脚本,不进行签名,通过该命令进行验证。属性StatusMessage会告诉你签名验证的结果。
"'未签名'" >notsign.ps1
$checkResult=Get-AuthenticodeSignature .notsign.ps1
$checkResult.Status
NotSigned
$checkResult.StatusMessage
文件 E:notsign.ps1 未经数字签名。系统将不执行该脚本。有关详细信息,请参阅 "get-help about_signing"
。
$checkResult.Status.GetType().fullName
System.Management.Automation.SignatureStatus
如果运行该未签名的脚本,也会收到错误提示信息,也就是StatusMessage中包含的信息。脚本的验证结果状态包括:
成员名称 | 描述 |
---|---|
HashMismatch | 文件的哈希码和存储的签名不匹配 |
Incompatible | 无法验证签名,因为与当前操作系统不兼容 |
NotSigned | 文件没有签名 |
NotSupportedFileFormat | 指定的文件格式不支持的系统签名。这通常意味着系统不知道如何签名或验证文件的类型。 |
NotTrusted | 证书的发布者在系统中不受信任. |
UnknownError | 文件签名无效 |
Valid | 该文件有一个有效的签名。这意味着只有签名的语法上是合法的。这并不意味着信任。 |
自动验证
你不须要去验证脚本的签名,当你运行一个脚本时,Powershell会自动验证。即使验证过的脚本,如果有部分内容更新,自动验证也会给出警告。
在用户将脚本执行策略设置为AllSigned和RemoteSigned时,自动验证就会激活,如果将执行策略设置为AllSigned,所有的脚本都会验证。如果你选择RemoteSigned,从网络上下载的脚本执行会提示需要签名。
# 设置 ExecutionPolicy 为 AllSigned. 所有
# 脚本必须有正确的签名:
Set-ExecutionPolicy AllSigned
# 创建一个没有签名的脚本.
# 该脚本不会执行:
无法加载文件 E:unSigned.ps1。文件 E:unSigned.ps1 未经数字签名。系统将不执行该脚本。有关详细信息,
请参阅 "get-help about_signing"。。
所在位置 行:1 字符: 15
+ .unSigned.ps1 < <<<
+ CategoryInfo : NotSpecified: (:) [], PSSecurityException
+ FullyQualifiedErrorId : RuntimeException
即使签名可以通过验证,也需要用户的批准,才能执行。
.firstSignScript.ps1
是否要运行来自此不可信发布者的软件?
文件 E:firstSignScript.ps1 由 CN=MosserPowerShellTestCert
发布,该文件对于您的系统是不可信的。请只运行来自可信发布者的脚本。
[V] 从不运行(V) [D] 不运行(D) [R] 运行一次(R) [A] 始终运行(A) [?] 帮助 (默认值为“D”): a
我的第一个签名脚本
#第二次执行,不会询问
我的第一个签名脚本