文章目录
- 介绍
- Windows管理规范
-
- 简单介绍
- 选择你的武器:WMI或CIM
- 使用Get-WmiObject
- 使用Get-CimInstance
- 多任务后台作业
-
- 介绍
- 创建本地作业
- WMI作业和远程处理作业
- 获取作业执行结果
- 调度作业
- 同时处理多个对象
-
- 首先方法:“批处理” Cmdlet
- CIMWMI方式:调用方法
- 后备计划:枚举对象
- 安全警报
-
- 执行策略设置
- 数字代码签名
- 其他安全措施
- 扩展命令
-
- 扩展:找到并添加插件
- 扩展:找到并添加模块
- PowerShell模块的构成
- 从Internet获取模块
- 总结
介绍
Windows管理规范
简单介绍
在最顶层,
在命名空间中,
Get-CimInstance -Namespace rootSecurityCenter2 -ClassName AntiSpywareProduct
要想很好的了解
选择你的武器:WMI或CIM
而在
- 所谓的“
WMI Cmdlets ”,例如“Get-WmiObject ”与“Invoke-WmiMethod ”——这些都是遗留命令,意味着它们依旧能工作,但是微软不会对它们进行后续的开发投入。 - 新版的“
WMI Cmdlets ”,例如“Get-CimInstance ”与“Invoke-CimMethod ”——它们或多或少等价于旧版本的“WMI Cmdlets ”,但是它们通过WS-MAN (由Windows 远程管理服务实现)交互,替代原有的RPCs 。执行“Get-Command -noun CIM* ”可显示很多微软提供的这些命令
使用Get-WmiObject
通过“
Get-WmiObject -namespace rootcimv2 -list
注意:命名空间名称中使用的是反斜杠,不是斜杠
也可以通过指定命名空间和类型查询一个类。
Get-WmiObject -namespace rootcimv2 -list -class win32_desktop Get-WmiObject win32_desktop
注意:“
rootcimv2 ”命名空间是默认命名空间,“-class ”是位置参数,所以上面两个命令结果一样
另外,“
Get-WmiObject win32_operatingsystem | gm gwmi -class win32_desktop -filter "name='NT AUTHORITY\SYSTEM'"
对于该命令和输出结果,需要注意下面事项。
筛选条件通常被引号包住
筛选操作符并不使用PowerShell 的常规操作符“-eq ”和“-like ”,而使用更加传统、更加编程化的操作符,比如= 、> 、< 、<= 、>= 和<> 。可以使用关键字“LIKE ”作为操作符,但在匹配值时必须使用“% ”作为字符通配符,如“NAME LIKE '%administrator%' ”。注意,这里不能像PowerShell 的其他地方一样使用“* ”作为通配符
字符串匹配是以单引号包住,这也是筛选表达式的最外面的引号是双引号的原因
避免在WMI 中使用反斜杠。如必须使用则使用两个反斜杠代替
Gwmi 的输出结果总会包含大量系统属性,PowerShell 的默认显示配置通常因此这些属性,系统属性名称以双下划线开始
一旦你查询到一个
gwmi -Class win32_bios -ComputerName DESKTOP-GT96S5L,localhost | Format-Table @{ label='ComputerName'; expression={$_.__SERVER}}, @{ label='BIOSSerial'; expression={$_.SerialNumber}}, @{ label='OSBuild'; expression={ gwmi -Class win32_operatingsystem -ComputerName $_.__SERVER | Select-Object -ExpandProperty BuildNumber} } -AutoSize
使用Get-CimInstance
你需要使用“
-ClassName ”代替“-Class ”
没有用于列出命名空间中所有类的“-List ”参数。而是使用“Get-CimClass ”并搭配“-Namespace ”参数获取类列表
没有“-Credential ”参数;如果你需要从远程计算机查询并被要求提供代替凭据,需要通过“Invoke-Command ”发送“Get-CimInstance ”
Get-CimInstance -ClassName Win32_LogicalDisk Invoke-Command -ScriptBlock { Get-CimInstance -ClassName Win32_Proccess } -ComputerName win8 -Credential DOMAINAdministrator
多任务后台作业
介绍
同步和异步执行命令的区别。
- 当在同步模式下运行命令时,可以相应输入请求;当使用后台模式运行命令时,根本就没有机会看到输入请求——实际上,当遇到输入请求时,会停止执行该命令
- 在同步模式下,如果遇到错误,命令会立即返回错误信息;后台模式执行的命令也会产生错误信息,但是你无法立即查看这些信息。如果需要,你必须通过一些机制来获取这些信息
- 在同步模式下,如果忽略了某个的必要参数,PowerShell会提示对应的缺失信息;如果是后台执行的命令,无法提示缺失信息,所有命令会执行失败
- 在同步模式下,当命令开始产生执行结果时,就会立即返回;但是当命令处于后台模式时,你必须等待命令执行结束,才能获取缓存的执行结果
创建本地作业
为了创建这种类型的作业,你需要使用
Start-Job -ScriptBlock { Dir } Start-Job -ScriptBlock { Get-EventLog Security -ComputerName Server-R2 }
注意:尽管本地作业是在本地运行的,但是它们也需要使用
PowerShell 的远程处理系统架构,所以如果你没有启用远程处理,那么将无法创建本地作业
WMI作业和远程处理作业
创建作业的另一种方法是使用
Get-WMIObject Win32_OperatingSystem -ComputerName( Get-Content allservers.txt) -AsJob
同样的,使用
Invoke-Command -Command { Get-Process } -ComputerName ( Get-Content .allservers.txt ) -AsJob -JobName myRemoteJob
获取作业执行结果
- 你必须指定希望获取返回结果的对应作业。可以通过作业
ID 、作业名称,或者通过Get-Job 命令获取作业列表后,通过管道传递给Receive-Job 命令 - 如果你获取了父作业的返回结果,那么该结果会包含所有子作业的输出结果。当然,你也可以获取一个或多个子作业执行结果
- 正常情况下,获取一个作业的返回结果后,会自动在作业的输出缓存中清除对应的数据,这样你不能再次获取它们。可以通过
-Keep 参数在内存中保留输出结果的一份拷贝。或者如果你希望保存一份拷贝以作它用,也可以将结果输出到CliXML 中 - 作业的返回结果可以是反序列化的对象,即返回的结果是它们产生时的一个快照,它们可能不会包含可以执行的任何方法。但是如果需要,你可以直接将作业的返回结果通过管道传递给一些
Cmdlet ,比如Sort-Object 、Format-List 、Exprot-CSV 、ConvertTo-HTML 、Out-File 等
Get-Job # 格式化输出结果 Get-Job -id 1 | Format-List * Receive-Job -ID 1 # 将结果缓存 Receive-Job -ID 3 -Keep Receive-Job -Name MyRemoteJob | Sort-Object PSComputerName | Format-Table -GroupBy PSComputerName
注意:当某个作业的输出结果没有被缓存时,对应的
HasMoreData 列为False
以下演示了如何获取子作业。
对应包含多个子作业的情况,可以使用以下命令获取其子命令。
Get-Job -ID 3 | Select-Object -Expand ChildJobs
针对作业,还有另外
- Remove-Job——该命令会移除一个作业,包括从内存中移除该作业缓存的所有输出结果
- Stop-Job——如果某个作业看起来卡住了,你可以通执行该命令停止它。但是仍然可以获取截至到该时刻产生的结果
- Wait-Job——该命令在下面场景中比较有用:当使用一段脚本开启一个作业,同时希望该脚本在作业运行完毕之后继续执行。该命令会使得
PowerShell 停止并等待作业执行,在作业执行结束后,运行PowerShell 继续执行
调度作业
术语调度作业(
你通过创建一个触发器(
# 每天凌晨2点值Get-Process命令,如果必须则唤醒计算机,同时要求作业运行在高级权限下 Register-ScheduledJob -Name DailyProcList -ScriptBlock { Get-Process } -Trigger ( New-JobTrigger -Daly -At 2am ) -ScheduledJobOption ( New-ScheduledJobOption -WaketoRun -RunElevated)
当移除这些作业,对应的结果也会从磁盘上移除。可以通过
同时处理多个对象
首先方法:“批处理” Cmdlet
很多
Get-Service | Stop-Service
在本例中,
Get-Service -Name BITS -ComputerName Serv1,Serv2,Serv3 | Start-Service -PassThru | Out-File NewServiceStatus.txt
该命令从三台计算机列表中获取指定服务,然后通过管道将这些服务传递给
-PassThru参数用于打印出该命令所接收的对象
CIMWMI方式:调用方法
不幸的是,总有一些任务无法通过调用
比如
Gwmi Win32_NetworkAdapterConfiguration -Filter "description like '%intel%'" | gm
Gwmi Win32_NetworkAdapterConfiguration -Filter "description like '%intel%'" | EnabledDHCP()
不幸的是,这是无效的。
Gwmi Win32_NetworkAdapterConfiguration -Filter "description like '%intel%'" | Invoke-WmiMethod -Name EnabledDHCP
你需要记住以下几点。
- 方法名称后无需加括号
- 方法名称不区分大小写
Invoke-WmiMethod 一次只能接收一种类型的WMI 对象。- 你可以针对
Invoke-WmiMethod 方法加上-WhatIf 和-Confirm 参数。但直接由对象调用方法时,无法使用这些参数
当你由一个
后备计划:枚举对象
总是会有些情况,我们只能通过枚举的方式一个个的处理。
我们使用
Gwmi Win32_Service -Filter "name='BITS'" | Foreach-Object -Process { $_.change($null,$null,$null,$null,$null,$null,$null,"P@ssw0rd")}
让我们把示例中的代码分解说明。
- 首先,你将看到
Cmdlet 名称,ForEach-Object - 接下来,使用
-Process 参数指定脚本段。其是位置参数,也可以不写,但建议明确写出 ForEach-Object 将会对每个通过管道传输给ForEach-Object 的对象执行脚本段。每次脚本段执行后,下一个通过管道传输进来的对象都会被置于特殊的$_ 容器- 通过在
$_ 后输入一个“. ”,告诉Shell 我们需要访问当前对象的属性或方法
接下来我们看一下下面的例子,它通过多种方式实现停止某个指定条件下的服务。
# 批处理Cmdlet Get-Service -Name *B* | Stop-Service # ForEach-Object Get-Service -Name *B* | ForEach-Object { $_.Stop() } # WMI Get-WmiObject Win32_Service -Filter "name like '%B%'" | Invoke-WmiMethod -Name StopService # WMI 和 ForEach-Object Get-WmiObject Win32_Service -Filter "name like '%B%'" | ForEach-Object { $_.Stop() } # Stop-Service Stop-Service -Name *B*
从上面的例子中我们也可以看出
- 原生
Cmdlet 过滤条件通常使用“* ”作为通配符,而WMI 过滤使用百分比符号(% )——请不要将百分比符号和ForEach-Ojbect 别名搞混。这个百分比符号封装在Get-WmiObject 的-Filter 参数内,并不是一个别名 - 原生对象通常和
WMI 有相同的功能,但是语法或许会有不同 - 原生过滤通常使用原生的比较操作符,比如说
-eq ;WMI 使用类似编程语言风格的操作符,比如说= 或者like
那么,何时该使用
- 如果你通过
Get-WmiObject 获取对象,你将需要通过使用WMI 方法来执行行为。你可以使用Invoke-WmiMethod 或ForEach-Object 方式执行方法 - 如果你通过非
Get-WmiObject 的方式获取对象,你将需要对获取到对象使用原生Cmdlet 。除非你获取到的对象只有方法而没有能够完成任务所需的Cmdlet,你可能会使用ForEach-Object 方式执行方法
注意:到这里的最低标准是
ForEach-Object :它的语法或许是最难的,但你可以使用它完成几乎所有你需要完成的工作
无论何时都无法将任何对象通过管道传递给一个方法。你只能利用管道将一个
请记住,通过管道将对象传递给
如图,请重点关注
最后,我们来分享一下
- 多使用
ForEach-Object 的完整名称,而不是使用% 或ForEach 这样的别名。完整名称更易于阅读。如果你使用别人写的示例,请将别名替换为完整名称 - 花括号内的代码段对于每一个通过管道传入的对象执行一次
- 在代码段内,
$_ 代表通过管道传入的对象之一 - 使用
$_ 本身控制所有通过管道传入的对象;使用$_ 后的加“. ”控制单独的方法或属性 - 即使方法不需要任何参数,方法名称之后也总是跟随圆括号。当需要参数时,通过逗号将参数分割放在括号内
安全警报
PowerShell 不会给被处理的对象任何额外的权限PowerShell 无法绕过既有的权限PowerShell 的安全并不是针对恶意软件的防护
下面我们具体看有哪些安全措施来实现
执行策略设置
默认设置是
你可以通过运行
- 运行
Set-ExecutionPolicy 命令。该命令会修改Windows 注册表中的HKEY_LOCAL_MACHINE 部分,但是需要在管理员权限下才能执行该命令 - 使用组策略对象(GPO)。如下图,我们可以在
“本地计算机策略”=>用户配置=>管理模板=>Windows组件=>Windows PowerShell 中找到相应的设置内容
- 通过手动运行
PowerShell.exe ,并给出-ExecutionPolicy 的命令行开关参数。采用这种方式,那么命令中指定的执行策略会覆盖本地任何设置和组策略中的设置值
你可以执行策略设置为
- Restricted——默认选项,除微软提供的一部分配置
PowerShell 的默认选项脚本外,不允许执行其他任何脚本。这些脚本有微软的数字签名,如果修改了数字签名则也无法执行这些脚本了 - AllSigned——经过受信任的证书颁发机构(
CA )设计的数字证书签名之后的任意脚本,PowerShell 均可执行 - RemotedSigned——
PowerShell 可以运行本地任何脚本,同时也可以执行受信任的CA 签发的数字证书前面之后的远程脚本 - Unrestricted——可以运行所有脚本,此选项并不建议使用
- Bypass——这个特殊的设定主要是针对应用程序开发任意,他们会将
PowerShell 嵌入到他们的应用程序中。这个设定值会忽略已经配置的执行策略,应当仅在主机应用程序提供了自身的脚本安全层时才使用该选项
微软强烈建议在执行脚本时使用
数字代码签名
数字代码签名,简称为代码签名,是指将一个密码签名应用到一个文本文件的过程,签名会显示在文件末端。签名包含了两部分重要信息:一是列出了对脚本签名的公司或组织;二是包含了对脚本的加密副本,并且
一旦你拥有了第三方证书并安装后,可以使用
- 脚本作者持有一个数字证书,该密钥包含两个密钥:一个公钥、一个私钥
- 当对脚本进行签名时,该签名会被私钥加密。私钥仅能被脚本开发者访问,同时仅有公钥能对该脚本进行解密。在签名中会包含脚本的副本
- 当
PowerShell 运行该脚本时,它会使用作者的公钥(包含在签名中)解密该签名。如果解密失败,则说明签名被篡改,那么该脚本就无法被运行。如果签名中的脚本副本与明文文本不吻合,那么该签名就会识别为损坏,该脚本也无法被运行
其他安全措施
首先,
扩展命令
扩展:找到并添加插件
一个合适管理单元
了解:通常PSSnapin的相关命令有——
Get-PSSnapin 、Add-PSSnapin 等
PSSnapin的概念逐渐被微软移除了,将来可能会越来越少。在内部,微软的重点是提供扩展模块。
扩展:找到并添加模块
Get-Content env:PSModulePath
可以通过
Get-Module 命令查看当前计算机上已经存在的模块
-
系统模块:
PowerShell 默认安装的模块大部分位于C:WindowsSystem32WindowsPowerShellv1.0Modules 文件夹中。这个路径通常是PowerShell 内部模块装用的。虽然可以将自己些的模块放到这个里面,但是不建议。 -
所有用户模块:此部分的文件夹位置是
C:Program FilesWindowsPowerShellModules 。这个路径中的模块针对所有用户,如果想让计算机上的所有用户使用,可以将模块放在此文件夹中。 -
当前用户模块:此部分文件夹位置是
C:Users<loggedInUsers>DocumentsWindowsPowerShellModules 。不管是自己些的还是下载的,放在此文件夹就只有当前登录的用户可用,对于多个计算机用户来说,有一定的隔离作用。
PowerShell模块的构成
-
.psm1 格式文件:此扩展名的文件都可以是PowerShell 模块。当然,文件中需要定义函数。通常,一个文件中的函数都是围绕一个主题了。 -
模块清单文件(
module manifest ):扩展名称为.psd1 ,此文件不是必须的,但是建议提供。这是使用PowerShell 哈希表格式编写的文本文件,哈希表中的元素用于描述模块的元数据。
此文件可以手动创建,也可以使用
New-ModuleManifest 'e:so.psd1' -Author 'Adam Bertram' -RootModule so.psm1 -Description 'This module helps in deploying software.'
生成内容如下。
# # 模块“so”的模块清单 # # 生成者: Adam Bertram # # 生成时间: 2024/1/4 # @{ # 与此清单关联的脚本模块或二进制模块文件。 RootModule = 'so.psm1' # 此模块的版本号。 ModuleVersion = '1.0' # 支持的 PSEditions # CompatiblePSEditions = @() # 用于唯一标识此模块的 ID GUID = '6adb5eb0-68e5-47a4-a8cc-6ba81131933c' # 此模块的作者 Author = 'Adam Bertram' # 此模块所属的公司或供应商 CompanyName = '未知' # 此模块的版权声明 Copyright = '(c) 2024 Adam Bertram。保留所有权利。' # 此模块所提供功能的说明 Description = 'This module helps in deploying software.' # 此模块要求的 Windows PowerShell 引擎的最低版本 # PowerShellVersion = '' # 此模块要求的 Windows PowerShell 主机的名称 # PowerShellHostName = '' # 此模块要求的 Windows PowerShell 主机的最低版本 # PowerShellHostVersion = '' # 此模块要求使用的最低 Microsoft .NET Framework 版本。此先决条件仅对 PowerShell Desktop 版本有效。 # DotNetFrameworkVersion = '' # 此模块要求使用的最低公共语言运行时(CLR)版本。此先决条件仅对 PowerShell Desktop 版本有效。 # CLRVersion = '' # 此模块要求的处理器体系结构(无、X86、Amd64) # ProcessorArchitecture = '' # 必须在导入此模块之前先导入全局环境中的模块 # RequiredModules = @() # 导入此模块之前必须加载的程序集 # RequiredAssemblies = @() # 导入此模块之前运行在调用方环境中的脚本文件(.ps1)。 # ScriptsToProcess = @() # 导入此模块时要加载的类型文件(.ps1xml) # TypesToProcess = @() # 导入此模块时要加载的格式文件(.ps1xml) # FormatsToProcess = @() # 将作为 RootModule/ModuleToProcess 中所指定模块的嵌套模块导入的模块 # NestedModules = @() # 要从此模块中导出的函数。为了获得最佳性能,请不要使用通配符,不要删除该条目。如果没有要导出的函数,请使用空数组。 FunctionsToExport = '*' # 要从此模块中导出的 cmdlet。为了获得最佳性能,请不要使用通配符,不要删除该条目。如果没有要导出的 cmdlet,请使用空数组。 CmdletsToExport = '*' # 要从此模块中导出的变量 VariablesToExport = '*' # 要从此模块中导出的别名。为了获得最佳性能,请不要使用通配符,不要删除该条目。如果没有要导出的别名,请使用空数组。 AliasesToExport = '*' # 要从此模块导出的 DSC 资源 # DscResourcesToExport = @() # 与此模块一起打包的所有模块的列表 # ModuleList = @() # 与此模块一起打包的所有文件的列表 # FileList = @() # 要传递到 RootModule/ModuleToProcess 中指定的模块的专用数据。这还可能包含 PSData 哈希表以及 PowerShell 使用的其他模块元数据。 PrivateData = @{ PSData = @{ # 应用于此模块的标记。这些标记有助于在联机库中执行模块发现。 # Tags = @() # 指向此模块的许可证的 URL。 # LicenseUri = '' # 指向此项目的主网站的 URL。 # ProjectUri = '' # 指向表示此模块的图标的 URL。 # IconUri = '' # 此模块的 ReleaseNotes # ReleaseNotes = '' } # PSData 哈希表末尾 } # PrivateData 哈希表末尾 # 此模块的 HelpInfo URI # HelpInfoURI = '' # 从此模块中导出的命令的默认前缀。可以使用 Import-Module -Prefix 覆盖默认前缀。 # DefaultCommandPrefix = '' }
从Internet获取模块
微软引入了一个名称为
使用步骤:
-
运行
Register-PSReporsitory 添加要给源的URL 。默认如上 -
使用
Find-Module 在源中查找模块。你可以在名称、特定标签等列中使用通配符缩写搜索范围 -
找到所需模块后,使用
Install-Module 下载与安装 -
使用
Update-Module 确保你的模块的副本是最新的,如不是则下载更新并安装 -
使用
Remove-Module 删除模块,使用Uninstall-Module 命令从磁盘中卸载/删除模块
# 查看 Get-Command -Module PowerShellGet # 查找 Find-Module -Name *VMware* # 安装 Find-Module -Name VMware.PowerCLI | Install-Module
最后,如果要创建我们自己的模块。记住,PowerShell模块通常由文件夹(模块容器)、 .psm1文件(模块)和.psd1文件(模块清单文件)构成。其中,模块文件夹的名称必须和模块本身的名称相同。放在上面介绍的三个位置的任意一个地方,使用
总结
以上是对