Búsqueda de sitios web

Buscando PowerShell con CIM


Ayer compartí un script que puede usar para inventariar sistemas para instalaciones de Windows PowerShell y PowerShell 7. Esto debería funcionar para la mayoría de las personas que instalan PowerShell 7 con el instalador proporcionado. Pero, como me han señalado más de una vez, esto no detectará ninguna instalación con carga lateral o fuera de banda. Hice referencia a esto en el artículo anterior. Creo que lo mejor que puedes hacer es buscar en las instancias del disco duro de pwsh.exe. Pero no queremos hacer una búsqueda de directorio recursiva por fuerza bruta. En lugar de eso, usaré Get-CimInstance.

Get-CimInstance -ClassName CIM_Logicalfile -Filter "filename='pwsh' AND extension = 'exe' AND drive='C:'"

Estoy limitando la búsqueda al controlador C para acelerar las cosas. Este tipo de búsqueda encuentra instalaciones estables y de vista previa.

Esto me da la misma información que tenía antes y un poco más. Todavía puedo buscar Windows PowerShell de la misma manera usando Get-Command.

La secuencia de comandos

Aquí hay un guión revisado.

#requires -version 5.1

[cmdletbinding()]
Param(
    [Parameter(HelpMessage = "Specify the drive to search for instances of pwsh.exe")]
    [ValidatePattern("^[c-zC-Z]$")]
    [string]$Drive = "C"
)

#a private function to shorten filenames
Function _shortName {
    Param($path)

    $parts = $path.split("\")
    $out = foreach ($part in $parts) {
        if ($part -match "\s") {
            "{0}~1" -f $part.Substring(0, 6)
        }
        else {
            $part
        }
    }

    $out -join "\"
}

Write-Verbose "Searching for PowerShell installations on $([System.Environment]::MachineName)"
$os = (Get-CimInstance -ClassName Win32_OperatingSystem -Property Caption).caption

#region Windows Powershell
Try {
    Write-Verbose "Testing for Windows PowerShell"
    $cmd = Get-Command -Name powershell.exe -ErrorAction stop
    if ($cmd) {
        Write-Verbose "Using $($cmd.path)"
        #need to run Powershell.exe to get $PSVersion
        $psh = &$cmd.path -nologo -noprofile -command { Get-Host }

        #test for PowerShell 2.0 engine or feature

        Write-Verbose "Testing for Windows PowerShell 2.0 engine or feature"

        #get computersystem roles to determine if running on a server or client
        #assuming operating system caption uses 'Server'

        if ($os.caption -match "server") {
            Write-Verbose "Running Get-WindowsFeature"
            $f = Get-WindowsFeature PowerShell-V2
        }
        else {
            Write-Verbose "Running Get-WindowsOptionalFeature"
            $f = Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root
        }

        if ($f.installed -OR $f.State -eq 'Enabled') {
            $comment = "Windows PowerShell 2.0 feature enabled or installed"
        }
        else {
            $comment = ""
        }
        #the install date will reflect the last time the OS was updated or installed
        [pscustomobject]@{
            PSTypeName      = "PSInstallInfo"
            Name            = $cmd.Name
            Path            = $cmd.source
            PSVersion       = $psh.Version
            Installed       = (Get-Item $cmd.source).CreationTime
            Comments        = $comment
            Computername    = [System.Environment]::MachineName
            OperatingSystem = $os
        }
        Remove-Variable cmd

    } #$cmd found
} #try
Catch {
    #this should never happen
    Write-Verbose "Windows PowerShell not installed on $([Environment]::MachineName)."
}
#endregion

#region PowerShell 7
Write-Verbose "Testing for PowerShell 7 including preview builds"
$pwsh = Get-CimInstance -ClassName CIM_Logicalfile -Filter "filename='pwsh' AND extension = 'exe' AND drive='$($drive):'"
if ($pwsh) {
    Foreach ($item in $pwsh) {

        if ($item.path -match 'preview') {
            $comment = "Preview"
        }
        else {
            $comment = ""
        }

        #test for SSH
        if (Test-Path $env:programdata\ssh\sshd_config ) {
            #get short name
            $short = _shortName $item.EightDotThreeFileName
            Write-Verbose "Testing for an SSH subsystem using $short"
            $ssh = Get-Content $env:programdata\ssh\sshd_config | Select-String $($short -replace "\\", "\\")
            #'(?<=powershell\s).*pwsh.exe'
            if ($ssh.matches.value -eq $short) {
                $comment += " SSH configured"
            }
        }

        [pscustomobject]@{
            PSTypeName      = "PSInstallInfo"
            Name            = Split-Path $item.name -Leaf
            Path            = Split-Path $item.name
            PSVersion       = $item.version
            Installed       = $item.InstallDate
            Comments        = $comment.Trim()
            Computername    = [System.Environment]::MachineName
            OperatingSystem = $os
        }

    }
}
#endregion

#end of script

Cambios

Hice algunos cambios en esta versión. Primero, decidí no tratar el motor PowerShell 2.0 como una versión separada. Utiliza la misma versión de powershell.exe tanto para 5.1 como para 2.0. En cambio, reflejo el estado del motor 2.0 en el comentario. También adopté un enfoque diferente para SSH. En lugar de probar si SSH está instalado y ejecutándose, estoy revisando el archivo sshd_config para detectar la existencia de un subsistema PowerShell. Esto es lo que le permite conectarse mediante comunicación remota SSH en PowerShell.

Esto requiere un poco de manipulación porque el nombre de archivo largo de Windows está truncado en el archivo sshd_config. Entonces, en lugar de buscar C:\Program Files\PowerShell\7\pwsh.exe, estoy buscando "c:\progra~1\powershell\7\pwsh.exe". Escribí una breve función auxiliar para convertir la ruta. Me doy cuenta de que esto no es infalible pero funciona para mis instalaciones SSH.

Aquí está la salida local predeterminada.

Como antes, puede utilizar la comunicación remota para inventariar otros servidores y escritorios.

$c = Invoke-Command -FilePath C:\scripts\Get-PSExeFile.ps1 -ComputerName win10,srv1,srv2 -HideComputerName | sort computername

¿Te gusta eso? Revisé el archivo de formato ps1xml.

<!--
Format type data generated 07/13/2021 12:14:45 by PROSPERO\Jeff

This file was created using the New-PSFormatXML command that is part
of the PSScriptTools module.

https://github.com/jdhitsolutions/PSScriptTools
-->
<Configuration>
  <ViewDefinitions>
    <View>
      <!--Created 07/13/2021 12:14:45 by PROSPERO\Jeff-->
      <Name>default</Name>
      <ViewSelectedBy>
        <TypeName>PSInstallInfo</TypeName>
      </ViewSelectedBy>
      <GroupBy>
        <ScriptBlock>
        if ($host.name -match "console|code|serverremotehost") {
          "{0} [$([char]27)[3m{1}$([char]27)[0m]" -f $_.Computername,$_.OperatingSystem
        }
        else {
          "{0} [{1}]" -f $_.Computername,$_.OperatingSystem
        }
        </ScriptBlock>
        <Label>Computername</Label>
      </GroupBy>
      <TableControl>
        <!--Delete the AutoSize node if you want to use the defined widths
        <AutoSize />.-->
        <TableHeaders>
          <TableColumnHeader>
            <Label>Name</Label>
            <Width>19</Width>
            <Alignment>left</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>PSVersion</Label>
            <Width>16</Width>
            <Alignment>left</Alignment>
          </TableColumnHeader>
           <TableColumnHeader>
            <Label>Installed</Label>
            <Width>12</Width>
            <Alignment>left</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>Comments</Label>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Name</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>
                if (($host.name -match "console|code|serverremotehost") -AND ($_.path -match "preview")) {
                  "$([char]27)[38;5;219m$($_.PSVersion)$([char]27)[0m"
                }
                else {
                  $_.PSVersion
                }
                </ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>$_.Installed.ToShortDateString()</ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
               <ScriptBlock>
                if (($host.name -match "console|code|serverremotehost") -AND ($_.comments-match "SSH configured")) {
                  <!-- replace SSH Detected with an ANSI sequence-->
                  $_.comments -replace "SSH configured","$([char]27)[1;38;5;155mSSH configured$([char]27)[0m"
                }
                else {
                  $_.comments
                }
                </ScriptBlock>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>

Resumen

En este punto, deberías tener varias opciones. Esperemos que alguno de ellos satisfaga sus necesidades. O siéntete libre de cortar y pegar las piezas que quieras en tu propia herramienta. Si adopta este enfoque, espero que comparta su trabajo. Preguntas y comentarios siempre bienvenidos. Disfrutar.

Artículos relacionados