Capitolo 7 - Uso di WMI

WMI e CIM

Windows PowerShell viene fornito per impostazione predefinita con i cmdlet per l'uso di altre tecnologie, ad esempio Strumentazione gestione Windows (WMI). I cmdlet WMI sono deprecati e non sono disponibili in PowerShell 6+, ma sono descritti qui come possono verificarsi in script meno recenti in esecuzione in Windows PowerShell. Per il nuovo sviluppo, usare invece i cmdlet CIM.

In PowerShell sono disponibili diversi cmdlet WMI nativi, senza la necessità di installare software o moduli aggiuntivi. Get-Command può essere usato per determinare quali cmdlet WMI esistono in Windows PowerShell. I risultati seguenti derivano dal mio computer dell'ambiente lab di Windows 10 che esegue la versione 5.1 di PowerShell. I risultati effettivi possono variare a seconda della versione di PowerShell in esecuzione.

Get-Command -Noun WMI*
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Get-WmiObject                                      3.1.0.0    Microsof...
Cmdlet          Invoke-WmiMethod                                   3.1.0.0    Microsof...
Cmdlet          Register-WmiEvent                                  3.1.0.0    Microsof...
Cmdlet          Remove-WmiObject                                   3.1.0.0    Microsof...
Cmdlet          Set-WmiInstance                                    3.1.0.0    Microsof...

I cmdlet CIM (Common Information Model) sono stati introdotti nella versione 3.0 di PowerShell. Sono progettati per essere usati sia in computer Windows che non Windows.

I cmdlet CIM sono tutti contenuti in un modulo. Per ottenere un elenco dei cmdlet CIM, usare Get-Command con il parametro Module, come illustrato nell'esempio seguente.

Get-Command -Module CimCmdlets
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Export-BinaryMiLog                                 1.0.0.0    CimCmdlets
Cmdlet          Get-CimAssociatedInstance                          1.0.0.0    CimCmdlets
Cmdlet          Get-CimClass                                       1.0.0.0    CimCmdlets
Cmdlet          Get-CimInstance                                    1.0.0.0    CimCmdlets
Cmdlet          Get-CimSession                                     1.0.0.0    CimCmdlets
Cmdlet          Import-BinaryMiLog                                 1.0.0.0    CimCmdlets
Cmdlet          Invoke-CimMethod                                   1.0.0.0    CimCmdlets
Cmdlet          New-CimInstance                                    1.0.0.0    CimCmdlets
Cmdlet          New-CimSession                                     1.0.0.0    CimCmdlets
Cmdlet          New-CimSessionOption                               1.0.0.0    CimCmdlets
Cmdlet          Register-CimIndicationEvent                        1.0.0.0    CimCmdlets
Cmdlet          Remove-CimInstance                                 1.0.0.0    CimCmdlets
Cmdlet          Remove-CimSession                                  1.0.0.0    CimCmdlets
Cmdlet          Set-CimInstance                                    1.0.0.0    CimCmdlets

I cmdlet CIM consentono comunque di usare WMI, quindi si possono eseguire query su WMI con i cmdlet CIM di PowerShell.

Come accennato in precedenza, WMI è una tecnologia separata da PowerShell e i cmdlet CIM si usano solo per accedere a WMI. È possibile che uno script VBScript meno recente usi WQL (WMI Query Language) per eseguire query su WMI, come nell'esempio seguente.

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colBIOS = objWMIService.ExecQuery _
    ("Select * from Win32_BIOS")

For each objBIOS in colBIOS
    Wscript.Echo "Manufacturer: " & objBIOS.Manufacturer
    Wscript.Echo "Name: " & objBIOS.Name
    Wscript.Echo "Serial Number: " & objBIOS.SerialNumber
    Wscript.Echo "SMBIOS Version: " & objBIOS.SMBIOSBIOSVersion
    Wscript.Echo "Version: " & objBIOS.Version
Next

È possibile usare la query WQL di tale script VBScript con il cmdlet Get-CimInstance senza alcuna modifica.

Get-CimInstance -Query 'Select * from Win32_BIOS'
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 3810-1995-1654-4615-2295-2755-89
Version           : VRTUAL - 4001628

Questo non è il modo in cui io in genere eseguo query su WMI con PowerShell. Ma funziona e consente di eseguire facilmente la migrazione di script VBScript esistenti a PowerShell. Quando inizio a scrivere una riga per eseguire query su WMI, uso la sintassi seguente.

Get-CimInstance -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 3810-1995-1654-4615-2295-2755-89
Version           : VRTUAL - 4001628

Se voglio solo il numero di serie, posso inviare tramite pipe l'output a Select-Object e specificare solo la proprietà SerialNumber.

Get-CimInstance -ClassName Win32_BIOS | Select-Object -Property SerialNumber
SerialNumber
------------
3810-1995-1654-4615-2295-2755-89

Per impostazione predefinita, esistono diverse proprietà che vengono recuperate dietro le quinte e non vengono mai usate. Potrebbe non essere importante quando si eseguono query su WMI nel computer locale. Tuttavia, una volta avviata l'esecuzione di query su computer remoti, non solo è necessario un tempo di elaborazione aggiuntivo per restituire tali informazioni, ma vengono anche recuperate informazioni aggiuntive superflue attraverso la rete. Get-CimInstance include un parametro Property che limita la quantità di informazioni recuperate. In questo modo la query su WMI risulta più efficiente.

Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber |
Select-Object -Property SerialNumber
SerialNumber
------------
3810-1995-1654-4615-2295-2755-89

I risultati precedenti restituiscono un oggetto. Per restituire una semplice stringa, usare il parametro ExpandProperty.

Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber |
Select-Object -ExpandProperty SerialNumber
3810-1995-1654-4615-2295-2755-89

È anche possibile usare lo stile punteggiato della sintassi per restituire una stringa semplice. In questo modo si elimina la necessità dell'invio tramite pipe a Select-Object.

(Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber).SerialNumber
3810-1995-1654-4615-2295-2755-89

Eseguire query su computer remoti con i cmdlet CIM

Continuo a eseguire PowerShell come amministratore locale e utente di dominio. Quando provo a eseguire query per recuperare informazioni da un computer remoto con il cmdlet Get-CimInstance, ricevo un messaggio di errore di accesso negato.

Get-CimInstance -ComputerName dc01 -ClassName Win32_BIOS
Get-CimInstance : Access is denied.
At line:1 char:1
+ Get-CimInstance -ComputerName dc01 -ClassName Win32_BIOS
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (root\cimv2:Win32_BIOS:String) [Get-CimI
   nstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80070005,Microsoft.Management.Infrastructure.Cim
   Cmdlets.GetCimInstanceCommand
    + PSComputerName        : dc01

Molte persone sono preoccupate per la sicurezza riguardo a PowerShell, ma la verità è che in PowerShell le autorizzazioni sono esattamente identiche a quelle che si hanno nell'interfaccia utente grafica. Non più e non meno. Il problema nell'esempio precedente è che l'utente che esegue PowerShell non ha i diritti per eseguire query sulle informazioni di WMI dal server DC01. Potrei riavviare PowerShell come amministratore di dominio perché Get-CimInstance non include un parametro Credential. Ma, fidatevi di me, non è una buona idea perché qualsiasi cosa eseguita da PowerShell sarebbe in esecuzione come amministratore di dominio. Questo potrebbe essere pericoloso dal punto di vista della sicurezza a seconda della situazione.

Usando il principio dei privilegi minimi, elevo il mio account di amministratore di dominio per ogni singolo comando con il parametro Credential, se è presente in un comando. Get-CimInstance non include un parametro Credential, quindi la soluzione in questo scenario consiste nel creare prima una sessione CimSession. Quindi, uso la sessione CimSession invece di un nome computer per eseguire query su WMI nel computer remoto.

$CimSession = New-CimSession -ComputerName dc01 -Credential (Get-Credential)
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential

La sessione CIM è stata archiviata in una variabile denominata $CimSession. Si noti che ho anche specificato anche il cmdlet Get-Credential tra parentesi, in modo che venga eseguito per primo, richiedendomi credenziali alternative prima di creare la nuova sessione. Più avanti in questo capitolo illustrerò un altro modo più efficiente per specificare credenziali alternative, ma è importante capire questo concetto di base prima di renderlo più complicato.

È ora possibile usare la sessione CIM creata nell'esempio precedente con il cmdlet Get-CimInstance per eseguire query sulle informazioni del BIOS da WMI nel computer remoto.

Get-CimInstance -CimSession $CimSession -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 0986-6980-3916-0512-6608-8243-13
Version           : VRTUAL - 4001628
PSComputerName    : dc01

L'uso di sessioni CIM invece di limitarsi a specificare un nome computer presenta diversi vantaggi. Quando si eseguono più query nello stesso computer, l'uso di una sessione CIM è più efficiente rispetto all'uso del nome computer per ogni query. La creazione di una sessione CIM configura la connessione una sola volta. Quindi, più query usano questa stessa sessione per recuperare le informazioni. L'uso del nome computer richiede ai cmdlet di configurare e rimuovere la connessione con ogni singola query.

Per impostazione predefinita, il cmdlet Get-CimInstance usa il protocollo WSMan, il che significa che il computer remoto richiede PowerShell versione 3.0 o successiva per connettersi. In realtà non è la versione di PowerShell che conta, ma quella dello stack. È possibile determinare la versione dello stack con il cmdlet Test-WSMan. Deve essere la versione 3.0. Questa è la versione inclusa in PowerShell versione 3.0 e successive.

Test-WSMan -ComputerName dc01
wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 0.0.0 SP: 0.0 Stack: 3.0

I cmdlet WMI precedenti usano il protocollo DCOM, che è compatibile con le versioni meno recenti di Windows. Tuttavia, DCOM viene in genere bloccato dal firewall nelle versioni più recenti di Windows. Il cmdlet New-CimSessionOption consente di creare una connessione del protocollo DCOM da usare con New-CimSession. In questo modo è possibile usare il cmdlet Get-CimInstance per comunicare con le versioni di Windows precedenti fino a Windows Server 2000. Questo significa anche che, quando si usa il cmdlet Get-CimInstance con una sessione CimSession configurata per l'uso del protocollo DCOM, PowerShell non è necessario nel computer remoto.

Creare l'opzione del protocollo DCOM usando il cmdlet New-CimSessionOption e archiviarla in una variabile.

$DCOM = New-CimSessionOption -Protocol Dcom

Per una maggiore efficienza, è possibile archiviare le credenziali di amministratore di dominio o quelle elevate in una variabile, in modo che non sia necessario immetterle continuamente per ogni comando.

$Cred = Get-Credential
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential

Ho un server denominato SQL03 che esegue Windows Server 2008 (non R2). Si tratta del sistema operativo Windows Server più recente in cui PowerShell non è installato per impostazione predefinita.

Creare una sessione CimSession in SQL03 usando il protocollo DCOM.

$CimSession = New-CimSession -ComputerName sql03 -SessionOption $DCOM -Credential $Cred

Si noti che nel comando precedente questa volta ho specificato la variabile denominata $Cred come valore per il parametro Credential, invece di immettere di nuovo le credenziali manualmente.

L'output della query è lo stesso indipendentemente dal protocollo sottostante usato.

Get-CimInstance -CimSession $CimSession -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 7237-7483-8873-8926-7271-5004-86
Version           : VRTUAL - 4001628
PSComputerName    : sql03

Il cmdlet Get-CimSession viene usato per vedere quali sessioni CimSession sono attualmente connesse e quali protocolli usano.

Get-CimSession
Id           : 1
Name         : CimSession1
InstanceId   : 80742787-e38e-41b1-a7d7-fa1369cf1402
ComputerName : dc01
Protocol     : WSMAN

Id           : 2
Name         : CimSession2
InstanceId   : 8fcabd81-43cf-4682-bd53-ccce1e24aecb
ComputerName : sql03
Protocol     : DCOM

Recuperare e archiviare entrambe le sessioni CimSession create in precedenza in una variabile denominata $CimSession.

$CimSession = Get-CimSession

Eseguire una query con un solo comando su entrambi i computer, uno con il protocollo WSMan e l'altro con DCOM.

Get-CimInstance -CimSession $CimSession -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 0986-6980-3916-0512-6608-8243-13
Version           : VRTUAL - 4001628
PSComputerName    : dc01

SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 7237-7483-8873-8926-7271-5004-86
Version           : VRTUAL - 4001628
PSComputerName    : sql03

Ho scritto numerosi articoli di blog sui cmdlet WMI e CIM. Uno dei più utili riguarda una funzione che ho creato per determinare automaticamente se usare WSMan o DCOM per configurare automaticamente la sessione CIM senza la necessità di farlo manualmente. Questo articolo di blog è intitolato Funzione di PowerShell per creare sessioni CimSession nei computer remoti con fallback a DCOM.

Al termine delle sessioni CIM, è necessario rimuoverle con il cmdlet Remove-CimSession. Per rimuovere tutte le sessioni CIM, è sufficiente inviare tramite pipe Get-CimSession a Remove-CimSession.

Get-CimSession | Remove-CimSession

Riepilogo

In questo capitolo si è appreso come usare PowerShell per lavorare con WMI in computer locali e remoti. Si è inoltre appreso come usare i cmdlet CIM per lavorare con i computer remoti con il protocollo WSMan o DCOM.

Revisione

  1. Qual è la differenza tra i cmdlet WMI e CIM?
  2. Per impostazione predefinita, quale protocollo viene usato dal cmdlet Get-CimInstance?
  3. Quali vantaggi si ottengono usando una sessione CIM invece di specificare un nome di computer con Get-CimInstance?
  4. Come è possibile specificare un protocollo alternativo diverso da quello predefinito per l'uso con Get-CimInstance?
  5. Come si chiudono o si rimuovono le sessioni CIM?