当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 获取已安装的 PowerShell 版本

作者:精品下载站 日期:2024-12-14 07:58:18 浏览:15 分类:玩电脑

获取已安装的 PowerShell 版本


正如典型一天的常态一样,当我被一个闪亮的兔子洞分散注意力时(混合一些隐喻),我正在做一件事。半天后,我有了一个新的 PowerShell 函数,您可能会觉得它很有用,而且我认为它有一些不错的脚本功能,您可能想亲自尝试一下。

我遇到的问题是远程查询 Windows 系统并确定安装了哪些版本的 PowerShell。对于服务器,您可以使用 Get-WindowsFeature 检查是否安装了 PowerShell-V2。但是 PowerShell Core、PowerShell 7 甚至预览版又如何呢?我的解决方案是查询注册表。

[玩转系统] 获取已安装的 PowerShell 版本

Windows PowerShell 使用与 PowerShell Core(包括 PowerShell 7)不同的键和属性名称。一旦我知道在哪里查找,我就可以编写一个函数。因为您只能查询“本地”计算机上的注册表,所以我的函数需要使用 PowerShell 远程处理。这是代码,位于 Github 上。

Get-PSInstalled.ps1:

#requires -version 5.1

#this function is only as good as far as you trust what you find in the registy.

Function Get-PSInstalled {

 Get-PSInstalled -computername "dom1","srv1","win10" -Credential $artd

Computername PSVersions                Date
------------ ----------                ----
DOM1         {2.0, 5.1.14393.0, 7.0.0} 5/6/2020 5:00:18 PM
SRV1         {5.1.14393.0}             5/6/2020 5:00:18 PM
WIN10        {2.0, 5.1.18362.1, 7.0.0} 5/6/2020 5:00:18 PM

Query computers using an alternate credential.

.Example
PS C:\> Get-PSSession | Get-PSInstalled

Computername PSVersions                       Date
------------ ----------                       ----
WIN10        {2.0, 5.1.18362.1, 7.0.0}        5/6/2020 9:01:07 PM
SRV1         {5.1.14393.0}                    5/6/2020 9:01:07 PM
DOM1         {2.0, 5.1.14393.0, 7.0.0}        5/6/2020 9:01:07 PM
THINKP1      {5.1.18362.1, 7.0.0, 7.0.0-rc.1} 5/6/2020 9:01:07 PM
SRV2         {2.0, 5.1.14393.0}               5/6/2020 9:01:07 PM
SRV3         {2.0, 5.1.17763.1}               5/6/2020 9:01:07 PM

Query for installed versions of PowerShell using existing PSSessions

.Example
PS C:\> Get-PSSession | Get-PSInstalled | Where-Object {$_.PSVersions -contains "2.0"}

Computername PSVersions                Date
------------ ----------                ----
WIN10        {2.0, 5.1.18362.1, 7.0.0} 5/6/2020 9:17:00 PM
DOM1         {2.0, 5.1.14393.0, 7.0.0} 5/6/2020 9:17:00 PM
SRV3         {2.0, 5.1.17763.1}        5/6/2020 9:17:00 PM
SRV2         {2.0, 5.1.14393.0}        5/6/2020 9:17:00 PM

Query remote computers and filter to only show those with PowerShell 2.0 installed.

.Notes
Learn more about PowerShell: http://jdhitsolutions.com/blog/essential-powershell-resources/
#>
    [cmdletbinding(DefaultParameterSetName = "computer")]
    Param(
        [Parameter(ParameterSetName = "computer", Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = "Enter the name of a computer to query.")]
        [ValidateNotNullOrEmpty()]
        [string[]]$Computername = $env:COMPUTERNAME,
        [Parameter(ParameterSetName = "computer", ValueFromPipelineByPropertyName, HelpMessage = "Enter a credential object or username.")]
        [PSCredential]$Credential,
        [Parameter(ParameterSetName = "computer")]
        [switch]$UseSSL,
        [Parameter(ParameterSetName = "session", ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.Runspaces.PSSession[]]$Session,
        [ValidateScript( {$_ -ge 0})]
        [int32]$ThrottleLimit = 32
    )

    Begin {
        #capture the start time. The Verbose messages can display a timespan.
        #this value can also be used as the audit date value
        $start = Get-Date
        #the first verbose message uses a pseudo timespan to reflect the idea we're just starting
        Write-Verbose "[00:00:00.0000000 BEGIN  ] Starting $($myinvocation.mycommand)"

        #a script block to be run remotely
        Write-Verbose "[$(New-TimeSpan -start $start) BEGIN  ] Defining scriptblock"
        $sb = {
            param([string]$VerbPref = "SilentlyContinue")

            $VerbosePreference = $VerbPref
            $regbase = "HKLM:\SOFTWARE\Microsoft"
            $versions = @()

            #windows powershell settings
            $pskey = Join-Path -Path $regbase -ChildPath PowerShell
            Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] Querying \$($env:computername)$pskey"
            Get-ChildItem $PSKey -Recurse -Include PowerShellEngine | ForEach-Object {
                $leaf = Convert-Path $_.pspath # | Split-path -leaf
                Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] ..$leaf"
                $versions += $_.pspath | Get-ItemPropertyValue -name PowerShellVersion
            }

            #check for PS Core and later
            $pskey = Join-Path -Path $regbase -ChildPath PowerShellCore\InstalledVersions

            if (Test-Path -path $Pskey) {
                Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] Querying \$($env:computername)$pskey"
                Get-ChildItem $PSKey -Recurse | ForEach-Object {
                    $leaf = Convert-Path $_.pspath | Split-path -leaf
                    Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] ..$leaf"
                    $versions += $_.pspath | Get-ItemPropertyValue -name SemanticVersion
                }
            }
            #send the result
            @{
                Computername = $env:COMPUTERNAME
                Versions     = $versions
            }

        } #scriptblock

        #parameters to splat to Invoke-Command
        Write-Verbose "[$(New-TimeSpan -start $start) BEGIN  ] Defining parameters for Invoke-Command"
        $icmParams = @{
            Scriptblock      = $sb
            Argumentlist     = $VerbosePreference
            HideComputerName = $True
            ThrottleLimit    = $ThrottleLimit
            ErrorAction      = "Stop"
            Session          = $null
        }

        #initialize an array to hold session objects
        [System.Management.Automation.Runspaces.PSSession[]]$All = @()
    } #begin

    Process {
        if ($PSCmdlet.ParameterSetName -eq 'computer') {
            foreach ($computer in $Computername ) {
                $PSBoundParameters["Computername"] = $Computer
                #create a session
                Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Creating a temporary PSSession to $($computer.toUpper())"
                If ($Credential.username) {
                    Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Using credential for $($credential.username)"
                }
                Try {
                    #save each created session to $tmp so it can be removed at the end
                    $all += New-PSSession @PSBoundParameters -ErrorAction Stop -OutVariable +tmp
                }
                Catch {
                    Write-Error $_
                }
            } #foreach computer
        } #if computer parameterset
        Else {
            #only add open sessions
            foreach ($sess in $session) {
                if ($sess.state -eq 'opened') {
                    Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Using session for $($sess.computername.toUpper())"
                    $all +=$sess
                }
            }
        }

    } #process

    End {

        $icmParams["session"] = $all

        Try {
            Write-Verbose "[$(New-TimeSpan -start $start) END    ] Querying $($all.count) computers"

            Invoke-Command @icmParams | Foreach-Object {
                Write-Verbose "[$(New-TimeSpan -start $start) END    ] Creating result for $($_.ComputerName)"
                [pscustomobject]@{
                Computername = $_.Computername
                PSVersions   = $_.versions
                Date         = $Start
            }
          } #foreach result
        } #try
        Catch {
            Write-Error $_
        } #catch

        if ($tmp) {
            Write-Verbose "[$(New-TimeSpan -start $start) END    ] Removing temporary PSSessions"
            $tmp | Remove-PSSession
        }
        Write-Verbose "[$(New-TimeSpan -start $start) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

该功能包括基于注释的帮助。

[玩转系统] 获取已安装的 PowerShell 版本

Process {
        if ($PSCmdlet.ParameterSetName -eq 'computer') {
            foreach ($computer in $Computername ) {
                $PSBoundParameters["Computername"] = $Computer
                #create a session
                Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Creating a temporary PSSession to $($computer.toUpper())"
                If ($Credential.username) {
                    Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Using credential for $($credential.username)"
                }
                Try {
                    #save each created session to $tmp so it can be removed at the end
                    $all += New-PSSession @PSBoundParameters -ErrorAction Stop -OutVariable +tmp
                }
                Catch {
                    Write-Error $_
                }
            } #foreach computer
        } #if computer parameterset

请注意,当我创建会话时,我使用 Out-Variable 将其保存为 $tmp。这样,在命令结束时我可以删除我创建的任何会话。如果传递了 PSSession,我会将其添加到数组中。

foreach ($sess in $session) {
                if ($sess.state -eq 'opened') {
                    Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Using session for $($sess.computername.toUpper())"
                    $all +=$sess
                }
            }

所有会话都添加到我将其添加到 Invoke-Command 的参数数组中。

 Invoke-Command @icmParams

与您预期不同的一件事是,该语句发生在函数的 End 脚本块中。这是因为我想依靠 PowerShell 来最好地管理所有连接。这就是为什么我包含 ThrottleLimit 参数,该参数会传递给 Invoke-Command。在早期版本中,我通过管道传输 PSSession 并在 Process 脚本块中运行 Invoke-Command。这意味着一次只能处理一个会话。通过将命令移至结束脚本块,总处理时间几乎减少了一半。总结一下这一点:Process 脚本块用于构建 PSSession 列表。就是这样。

开始脚本块是我定义要远程运行的脚本块的地方。我非常喜欢在命令中使用 Write-Verbose。但潜在的障碍是,如果我使用 -Verbose 运行该函数,当脚本块远程运行时,它不知道我的详细首选项。我是这样处理的。

在脚本块中,我添加了一个参数来指示我的详细首选项。

$sb = {
            param([string]$VerbPref = "SilentlyContinue")

            $VerbosePreference = $VerbPref

在我的函数中,我向 -Argumentlist 添加了一个值,该值显示我的端的详细首选项。

$icmParams = @{
            Scriptblock      = $sb
            Argumentlist     = $VerbosePreference
            HideComputerName = $True
            ThrottleLimit    = $ThrottleLimit
            ErrorAction      = "Stop"
            Session          = $null
        }

详细功能的最后一部分是我使用运行时间作为消息的一部分。我还指出了该语句在函数中发生的位置。在脚本块中,我的详细消息使用 $using 来获取在我的计算机上定义的 $start 变量。我还使用 REMOTE 来指示详细消息发生在其他地方。

Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] Querying \$($env:computername)$pskey"

这就是一切的样子。

[玩转系统] 获取已安装的 PowerShell 版本

现在我可以知道发生了什么以及在哪里发生。请注意,我对齐了详细消息的前导部分。我发现这使它更容易阅读。

至于结果:又好又简单。

[玩转系统] 获取已安装的 PowerShell 版本

或者通过指定计算机名称。

[玩转系统] 获取已安装的 PowerShell 版本

我不得不说我对结果很满意。我希望你能尝试一下并让我知道你的想法。它应该可以在 Windows PowerShell 和 PowerShell 7 中运行。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯