Quantcast
Channel: CIM – PowerShell for Windows Admins
Viewing all 117 articles
Browse latest View live

Cim session oddity

$
0
0

The CIM cmdlets were introduced with PowerShell 3.0.  You can use the –ComputerName parameter to access a remote machine or, if you need to run multiple commands to the remote machine, you can create a CIM session.

CIM sessions are analogous to PowerShell remoting sessions and use WSMAN by default to connect to the remote machine:

PS> $c12 = New-CimSession -ComputerName W12R2SUS

PS> Get-CimInstance -CimSession $c12 -ClassName Win32_OperatingSystem | fl
SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 9600
RegisteredUser  : Windows User
SerialNumber    : 00252-00107-57895-AA282
Version         : 6.3.9600
PSComputerName  : W12R2SUS

In this case I’m accessing a Windows 2012 R2 system

If you try to create a CIM session to a machine running PowerShell 2.0 it will appear to work but you’ll get an error when you try to access the session:

PS> $c8 = New-CimSession -ComputerName W8R2STD01
PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem | fl
Get-CimInstance : The WS-Management service cannot process the request. A DMTF resource URI was used to access a non-DMTF class. Try again using a non-DMTF resource URI.
At line:1 char:1
+ Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem | fl
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (root\cimv2:Win32_OperatingSystem:String) [Get-CimInstance], CimException
+ FullyQualifiedErrorId : HRESULT 0x80338139,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand
+ PSComputerName        : W8R2STD01

The reason is that the version of WSMAN installed with with PowerShell 2.0 (WSMAN 2.0) isn’t compatible with CIM sessions which expect WSMAN 3.0

One option is to use a DCOM based session:

PS> $opt = New-CimSessionOption -Protocol Dcom
PS> $c8D = New-CimSession -ComputerName W8R2STD01 -SessionOption $opt
PS> Get-CimInstance -CimSession $c8D -ClassName Win32_OperatingSystem | fl
SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 7601
RegisteredUser  : Windows User
SerialNumber    : 00477-179-0000007-84050
Version         : 6.1.7601
PSComputerName  : W8R2STD01

 

PowerShell MVP Jeff Hicks discovered that if you use a filter parameter with Get-CimInstance you can access PowerShell 2.0 machines using a WSMAN based CIM session

PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Filter “Caption LIKE ‘%'”  | fl
SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 7601
RegisteredUser  : Windows User
SerialNumber    : 00477-179-0000007-84050
Version         : 6.1.7601
PSComputerName  : W8R2STD01

In this case you’re filtering on the Caption being like any characters

I stood in for a speaker who was ill at the recent European PowerShell conference and part of the session was on using CIM sessions. This issue came up and I decided to investigate a bit closer

Without a filter:

PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Verbose
VERBOSE: Perform operation ‘Enumerate CimInstances’ with following parameters, ”namespaceName’ =
root\cimv2,’className’ = Win32_OperatingSystem’.
Get-CimInstance : The WS-Management service cannot process the request. A DMTF resource URI was used to access a
non-DMTF class. Try again using a non-DMTF resource URI.
At line:1 char:1
+ Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Ver …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (root\cimv2:Win32_OperatingSystem:String) [Get-CimInstance], CimException
+ FullyQualifiedErrorId : HRESULT 0x80338139,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand
+ PSComputerName        : W8R2STD01

VERBOSE: Operation ‘Enumerate CimInstances’ complete.

An attempt is made to enumerate the instances of the Win32_OperatingSystem class

If you use a filter

PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Filter “Caption LIKE ‘%'”  -Verbose | fl
VERBOSE: Perform operation ‘Query CimInstances’ with following parameters, ”queryExpression’ = SELECT * FROM
Win32_OperatingSystem WHERE Caption LIKE ‘%’,’queryDialect’ = WQL,’namespaceName’ = root\cimv2′.
SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 7601
RegisteredUser  : Windows User
SerialNumber    : 00477-179-0000007-84050
Version         : 6.1.7601
PSComputerName  : W8R2STD01

VERBOSE: Operation ‘Query CimInstances’ complete.

You’re sending a WQL  query to the remote machine.

My current theory is that Get-CimInstance is trying to enumerate the instances of a particular class (in a similar way to Get-WSmnaInstance does) and that fails due to the WSMAN version mismatch.  Using the Filter bypasses the enumeration allowing it to work.

This is a totally undocumented feature and there is no guarantee it will continue to work in future versions. Until PowerShell 2.0 is gone from you environment be aware that its an option but be careful

The post Cim session oddity appeared first on PowerShell for Windows Admins.


WMI Filters

$
0
0

A common mistake with WMI/CIM filters is:

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter “DeviceId=C:”
Get-WmiObject : Invalid query “select * from Win32_LogicalDisk where DeviceId=C:”
At line:1 char:1
+ Get-WmiObject -Class Win32_LogicalDisk -Filter “DeviceId=C:”
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCo
mmand

The clue is in the invalid query error message

When you use the –Filter parameter and are testing a property of type string the value you are testing against has to be in quotes

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter “DeviceId=’C:'”
DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 108999528448
Size         : 248480002048
VolumeName   : Windows

The filter is defined as a string so you need to use single quotes inside the double quotes. You could mess around with all single quotes but then you have to escape the inner set of quotes – good luck with that – its an unnecessary exercise in frustration

How do you know the property is a string?

PS>  (Get-CimClass -ClassName Win32_LogicalDisk).CimClassProperties[‘DeviceId’]
Name               : DeviceID
Value              :
CimType            : String
Flags              : Property, Key, ReadOnly, NullValue
Qualifiers         : {CIM_Key, read, key, MappingStrings…}
ReferenceClassName :

 

The same rules for –Filter apply to Get-CimInstance

Get-CimInstance -ClassName Win32_LogicalDisk -Filter “DeviceId=’C:'”

The post WMI Filters appeared first on PowerShell for Windows Admins.

WMI classes and Storage cmdlets

$
0
0

There is a hierarchy of objects to work through when dealing with disks

First you have the physical disk

PS>  Get-CimInstance -ClassName Win32_DiskDrive | fl

Partitions : 5
DeviceID   : \\.\PHYSICALDRIVE0
Model      : HFS256G3AMNB-2200A
Size       : 256052966400
Caption    : HFS256G3AMNB-2200A

A physical disk can have 1 or more partitions:

PS>  Get-CimInstance -ClassName Win32_DiskPartition | fl
NumberOfBlocks   : 716800
BootPartition    : False
Name             : Disk #0, Partition #0
PrimaryPartition : False
Size             : 367001600
Index            : 0

NumberOfBlocks   : 409600
BootPartition    : True
Name             : Disk #0, Partition #1
PrimaryPartition : True
Size             : 209715200
Index            : 1

NumberOfBlocks   : 485312512
BootPartition    : False
Name             : Disk #0, Partition #2
PrimaryPartition : True
Size             : 248480006144
Index            : 2

NumberOfBlocks   : 921600
BootPartition    : False
Name             : Disk #0, Partition #3
PrimaryPartition : False
Size             : 471859200
Index            : 3

NumberOfBlocks   : 12492800
BootPartition    : False
Name             : Disk #0, Partition #4
PrimaryPartition : False
Size             : 6396313600
Index            : 4

next step down is logical disks

PS>  Get-CimInstance -ClassName Win32_LogicalDisk | fl
DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 108900372480
Size         : 248480002048
VolumeName   : Windows

 

The classes Win32_DiskDriveToDiskPartition and Win32_LogicalDiskToPartition  link physical disks to partitions and partitions to logical disks respectively.

Then you’ve got volumes – which is where you actually work with disks for the most part

PS>  Get-CimInstance -ClassName Win32_Volume | fl Caption, Label
Caption : C:\
Label   : Windows

Caption : \\?\Volume{524b798f-a072-4ecc-8cfe-fb823e10a5e7}\
Label   : Windows RE tools

Caption : \\?\Volume{4ea44e2e-dd30-4cd9-bfd1-c991be836d97}\
Label   :

Caption : \\?\Volume{c671d23c-f5e5-473d-b6c4-fecb4a99e5b3}\
Label   : Recovery image

 

The Storage module introduced with Windows 8 has cmdlets for some of these tasks:

PS>  Get-PhysicalDisk | fl FriendlyName, SerialNumber, CanPool, OperationalStatus, HealthStatus, Usage
, Size
FriendlyName      : HFS256G3AMNB-2200A
SerialNumber      : EI3AN118813AM3740
CanPool           : False
OperationalStatus : OK
HealthStatus      : Healthy
Usage             : Auto-Select
Size              : 256060514304

 

Partitions:

PS>  Get-Partition | fl PartitionNumber, DriveLetter, Offset, Size, Type
PartitionNumber : 1
DriveLetter     :
Offset          : 1048576
Size            : 367001600
Type            : Recovery

PartitionNumber : 2
DriveLetter     :
Offset          : 368050176
Size            : 209715200
Type            : System

PartitionNumber : 3
DriveLetter     :
Offset          : 577765376
Size            : 134217728
Type            : Reserved

PartitionNumber : 4
DriveLetter     : C
Offset          : 711983104
Size            : 248480006144
Type            : Basic

PartitionNumber : 5
DriveLetter     :
Offset          : 249191989248
Size            : 471859200
Type            : Recovery

PartitionNumber : 6
DriveLetter     :
Offset          : 249663848448
Size            : 6396313600
Type            : Recovery

 

Get-Disk returns similar, but not identical, information to Get-PhysicalDisk

The Get-Disk cmdlet gets one or more Disk objects visible to the operating system, or optionally a filtered list.

The Get-Disk cmdlet gets one or more Disk objects visible to the operating system, or optionally a filtered list.

There isn’t a cmdlet to get logical disks

For volumes:

PS>  Get-Volume | fl DriveLetter, FileSystemLabel, FileSystem, DriveType, HealthStatus, OperationalSta
tus, SizeRemaining, Size
DriveLetter       : C
FileSystemLabel   : Windows
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 108473528320
Size              : 248480002048

DriveLetter       :
FileSystemLabel   :
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 122884096
Size              : 471855104

DriveLetter       :
FileSystemLabel   : Windows RE tools
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 61980672
Size              : 366997504

DriveLetter       :
FileSystemLabel   : Recovery image
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 476807168
Size              : 6396309504

As you can see from this quick comparison the same sorts of information is available from the storage cmdlets and WMI. In fact under the hood the storage cmdlets are using WMI – but a set of new classes defined in ROOT/Microsoft/Windows/Storage

The post WMI classes and Storage cmdlets appeared first on PowerShell for Windows Admins.

Optimising WMI calls–part 2

$
0
0

Last time we looked at using CIM sessions to make a set of WMI calls run quicker. This time we’ll reduce the number of calls.  I’m deliberately just reducing the number of calls to the Win32_Service class.  We’ll look at the disks another time

Our code becomes

Measure-Command -Expression {

$srvs = ‘W16TP5TGT01’, ‘W16TP5TGT02′

for ($i=1; $i -le 150; $i++){

foreach ($srv in $srvs) {
$cs = New-CimSession -ComputerName $srv
$bootupMemory = Get-CimInstance -Query “SELECT * FROM Win32_OperatingSystem” -CimSession $cs
$cpuLoad = Get-CimInstance -Query “SELECT * FROM Win32_Processor” -CimSession $cs

$tSessions = Get-CimInstance -Query “SELECT * FROM Win32_TerminalService” -CimSession $cs

$sfilt = “Name=’imaservice’ OR Name=’mfcom’ OR Name=’cpsvc’ OR Name=’msmq'”
$reqserv = Get-CimInstance -ClassName Win32_Service -Filter $sfilt -CimSession $cs

$ima = $reqserv | where Name -eq ‘imaservice’
$mfcom = $reqserv | where Name -eq ‘mfcom’
$ctxPrintMgr = $reqserv | where Name -eq ‘cpsvc’
$msmqstatus = $reqserv | where Name -eq ‘msmq’

$cDrive = Get-CimInstance -Query “SELECT * FROM Win32_Logicaldisk WHERE deviceid=’c:'” -CimSession $cs
$dDrive = Get-CimInstance -Query “SELECT * FROM Win32_Logicaldisk WHERE deviceid=’d:'” -CimSession $cs
Remove-CimSession -CimSession $cs
}
}
}

The change is to create a filter that pulls back JUST the services we want. Use that to create a collection of Win32_Service objects and then populate the variables with the required service data

Time drops dramatically

Days              : 0
Hours             : 0
Minutes           : 6
Seconds           : 50
Milliseconds      : 133
Ticks             : 4101339515
TotalDays         : 0.0047469207349537
TotalHours        : 0.113926097638889
TotalMinutes      : 6.83556585833333
TotalSeconds      : 410.1339515
TotalMilliseconds : 410133.9515

Total time goes from 539.42 seconds to 410.13 seconds.  That’s reduced the time by 23.96%

These are just simple coding changes remember- we’re not performing any clever parallel processing here

The post Optimising WMI calls–part 2 appeared first on PowerShell for Windows Admins.

Optimising WMI calls–part 3

$
0
0

The next change just uses 1 call to get the disk information instead of 2

Measure-Command -Expression {

$srvs = ‘W16TP5TGT01’, ‘W16TP5TGT02′

for ($i=1; $i -le 150; $i++){

foreach ($srv in $srvs) {
$cs = New-CimSession -ComputerName $srv
$bootupMemory = Get-CimInstance -Query “SELECT * FROM Win32_OperatingSystem” -CimSession $cs
$cpuLoad = Get-CimInstance -Query “SELECT * FROM Win32_Processor” -CimSession $cs

$tSessions = Get-CimInstance -Query “SELECT * FROM Win32_TerminalService” -CimSession $cs

$sfilt = “Name=’imaservice’ OR Name=’mfcom’ OR Name=’cpsvc’ OR Name=’msmq'”
$reqserv = Get-CimInstance -ClassName Win32_Service -Filter $sfilt -CimSession $cs

$ima = $reqserv | where Name -eq ‘imaservice’
$mfcom = $reqserv | where Name -eq ‘mfcom’
$ctxPrintMgr = $reqserv | where Name -eq ‘cpsvc’
$msmqstatus = $reqserv | where Name -eq ‘msmq’

$dfilt = “Deviceid=’c:’ OR Deviceid=’D:'”
$drives = Get-CimInstance -ClassName Win32_Logicaldisk -Filter $dfilt -CimSession $cs

$cDrive = $drives | where deviceid -eq ‘c:’
$dDrive = $drives | where deviceid -eq ‘d:’
Remove-CimSession -CimSession $cs
}
}
}

Time now becomes

Days              : 0
Hours             : 0
Minutes           : 6
Seconds           : 36
Milliseconds      : 923
Ticks             : 3969235528
TotalDays         : 0.00459402260185185
TotalHours        : 0.110256542444444
TotalMinutes      : 6.61539254666667
TotalSeconds      : 396.9235528
TotalMilliseconds : 396923.5528

Not such a dramatic change but overall we’re now taking 26.4% less time to run the code.

The post Optimising WMI calls–part 3 appeared first on PowerShell for Windows Admins.

Open source PowerShell and OMI

Dealing with CIM properties that are integer arrays

$
0
0

Saw a post about WmiMonitorID that intrigued me

If you use the WmiMonitorID:

PS> Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID | select -f 1

Active                 : True
InstanceName           : DISPLAY\GSM598F\4&19086f00&0&UID200195_0
ManufacturerName       : {71, 83, 77, 0…}
ProductCodeID          : {53, 57, 56, 70…}
SerialNumberID         : {51, 48, 52, 78…}
UserFriendlyName       : {50, 50, 69, 65…}
UserFriendlyNameLength : 13
WeekOfManufacture      : 4
YearOfManufacture      : 2013
PSComputerName         :

You get a number of properties returned as an array of numbers. if you look at the property with Get-CimClass they unsigned 16 bit integers

Name               : ManufacturerName
Value              :
CimType            : UInt16Array
Flags              : Property, ReadOnly, NullValue
Qualifiers         : {MAX, read, WmiDataId}
ReferenceClassName :

Probably the easiest way to deal with them is a very simple function and calculated fields

function Convert-ArrayToName {
param ($array)

($array | foreach { [char][byte]$_} ) -join ”

}

Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID |
select Active,
@{N=’Manufacturer’; E={Convert-ArrayToName -array $_.ManufacturerName }},
@{N=’ProductCode’; E={Convert-ArrayToName -array $_.ProductCodeID}},
@{N=’SerialNumber’; E={Convert-ArrayToName -array $_.SerialNumberID}},
@{N=’UserFriendlyName’; E={Convert-ArrayToName -array $_.UserFriendlyName}},
WeekOfManufacture,YearOfManufacture

 

The function Convert-ArrayToName accepts an array.  Using foreach-object the integers are converted to bytes and then to chars. Join the resultant array of chars and you get the string versions of the property

Call the function in a calculated field to convert the numeric array to a string – repeat for all relevant properties. You could create an object rather than just using select if you wish

Run the code and

Active                 : True
InstanceName           : DISPLAY\GSM598F\4&19086f00&0&UID200195_0
ManufacturerName       : {71, 83, 77, 0…}
ProductCodeID          : {53, 57, 56, 70…}
SerialNumberID         : {51, 48, 52, 78…}
UserFriendlyName       : {50, 50, 69, 65…}
UserFriendlyNameLength : 13
WeekOfManufacture      : 4
YearOfManufacture      : 2013
PSComputerName         :

Active                 : True
InstanceName           : DISPLAY\SEC3242\4&19086f00&0&UID265988_0
ManufacturerName       : {83, 69, 67, 0…}
ProductCodeID          : {51, 50, 52, 50…}
SerialNumberID         : {48, 0, 0, 0…}
UserFriendlyName       :
UserFriendlyNameLength : 0
WeekOfManufacture      : 0
YearOfManufacture      : 2012
PSComputerName         :

becomes

Active            : True
Manufacturer      : GSM
ProductCode       : 598F
SerialNumber      : 304NDJX51788
UserFriendlyName  : 22EA63
WeekOfManufacture : 4
YearOfManufacture : 2013

Active            : True
Manufacturer      : SEC
ProductCode       : 3242
SerialNumber      : 0
UserFriendlyName  :
WeekOfManufacture : 0
YearOfManufacture : 2012

 

The post Dealing with CIM properties that are integer arrays appeared first on PowerShell for Windows Admins.

Filter early and WQL

$
0
0

What’s wrong with this:

Get-CimInstance -ClassName Win32_Service |
where {$_.State -eq ‘Running’ -and $_.StartName -notlike ‘LocalSystem’ -and $_.StartName -notlike ‘NT Authority*’} |
select PSComputerName, Name, DisplayName, State, StartName

Nothing except that its inefficient. if you ran this against a remote machine the filtering would happen on the local machine AFTER you’d dragged everything across the network. May not matter for a few machines but when you get to hundreds or thousands of machines it will have an impact

You need to use a filter. First try would be something like this:

Get-CimInstance -ClassName Win32_Service  -Filter “State = ‘Running’ AND StartName != ‘LocalSystem’ AND NOT StartName LIKE ‘NT Authority%'”|
select PSComputerName, Name, DisplayName, State, StartName

Unfortunately any services with a NULL StartName will also be filtered out

This will work

Get-CimInstance -ClassName Win32_Service  -Filter “State = ‘Running’ AND Startname != ‘LocalSystem’ AND StartName != ‘NT AUTHORITY\\LocalService’ AND StartName != ‘NT AUTHORITY\\NetworkService'”|
select PSComputerName, Name, DisplayName, State, StartName

Same results are obtained with Get-WmiObject

 

The post Filter early and WQL appeared first on PowerShell for Windows Admins.


Server Uptime

$
0
0

Its easy to get the last boot time of a Windows machine but how do you get the uptime

function Get-Uptime {
[CmdletBinding()]
param (
[string]$ComputerName = $env:COMPUTERNAME
)

$os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $ComputerName

$uptime = (Get-Date) – $os.LastBootUpTime

$uptime

}

Use Get-CimInstance to get the Win32_OperatingSystem class. To calculate the uptime subtract the value of LastBootTime from the current time and date.

You’ll get a Timespan object returned.

PS> Get-Uptime
Days              : 1
Hours             : 10
Minutes           : 32
Seconds           : 26
Milliseconds      : 838
Ticks             : 1243468385381
TotalDays         : 1.4391995201169
TotalHours        : 34.5407884828056
TotalMinutes      : 2072.44730896833
TotalSeconds      : 124346.8385381
TotalMilliseconds : 124346838.5381

Pick out whichever properties you need for your report

The post Server Uptime appeared first on PowerShell for Windows Admins.

Working with multiple CIM objects

$
0
0

Many of the CIM objects we work with in our computers come in multiple instances – disks and network cards are a couple of examples. Many times when you see examples you’ll see something like this:

$disks = Get-WmiObject -Class Win32_LogicalDisk

foreach ($disk in $disks){
if ($disk.Size -gt 0) {
$disk | select DeviceId,
@{N=’Free’; E={[math]::Round($disk.FreeSpace/1GB, 2)}},
@{N=’Size’; E={[math]::Round($disk.Size/1GB, 2)}},
@{N=’PercUsed’; E={[math]::Round((($disk.Size – $disk.FreeSpace) / $disk.Size) * 100, 2)}}
}
}

Get a collection of objects. Iterate through them with foreach and do something.

You can, and should, do this as a pipeline operation. The code above is really a hangover from VBScript coding.

Converting the above to a pipeline gives

Get-WmiObject -Class Win32_LogicalDisk |
where Size -gt 0 |
foreach {
$_ | select DeviceId,
@{N=’Free’; E={[math]::Round($_.FreeSpace/1GB, 2)}},
@{N=’Size’; E={[math]::Round($_.Size/1GB, 2)}},
@{N=’PercUsed’; E={[math]::Round((($_.Size – $_.FreeSpace) / $_.Size) * 100, 2)}}
}

NOTE – before anyone starts complaining yes I know you can use a  filter on Get-WmiObject I’m explaining the principle! Also, I know that you could go straight into the select on the pipeline but if you want to add extra processing e.g. send an email if the disk is more than 80% used you need the foreach

You can do similar things with NICs for example

Get-WmiObject -ClassName Win32_PerfFormattedData_Tcpip_NetworkInterface |
where CurrentBandwidth -gt 0 |
foreach {

$props = [ordered]@{
Name = $psitem.Name
Computer = $psitem.PSComputerName
PercUtilisation = (($psitem.BytesTotalPersec * 8) / $psitem.CurrentBandWidth) * 100
}

New-Object -TypeName PSObject -Property $props

} |
where PercUtilisation -gt 0.5 |
foreach {
$text =  @”
$($_.Name) on $($_.Computer)

Bandwidth utilized:  $($_.PercUtilisation) %
“@

$text

}

Rather than displaying $text send an email if the utilisation is too big.

where PercUtilisation -gt 0.5

is used so I actually get to see results. You probably want 60% or more on your production machines.

The post Working with multiple CIM objects appeared first on PowerShell for Windows Admins.

ComputerName parameters for CIM and WMI cmdlets

$
0
0

Accessing a remote system and running

Get-WmiObject -ClassName Win32_LogicalDisk -ComputerName $computer

or

Get-CimInstance -ClassName Win32_LogicalDisk -ComputerName $computer

is a standard approach.

If you’re creating a function with that code in you may put the local machine as a default parameter:

$computer = $env:COMPUTERNAME

Running Get-WmiObject locally will work quite happily because you’re using COM to access the local machine.

Get-CimInstance may well fail with this error

PS> Get-CimInstance -ClassName Win32_LogicalDisk -ComputerName $computer
Get-CimInstance : The client cannot connect to the destination specified in the request. Verify that the service on the destination is running and is accepting requests. Consult the logs anddocumentation for the WS-Management service running on the destination, most commonly IIS or WinRM.
If the destination is the WinRM service, run the following command on the destination to analyze and configure the WinRM service: “winrm quickconfig”.
At line:1 char:1
+ Get-CimInstance -ClassName Win32_LogicalDisk -ComputerName $computer
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ConnectionError: (root\cimv2:Win32_LogicalDisk:String) [Get-CimInstanc
e], CimException
+ FullyQualifiedErrorId : HRESULT 0x80338012,Microsoft.Management.Infrastructure.CimCmdlets.GetC
imInstanceCommand
+ PSComputerName        : RSSURFACEPRO2

The CIM cmdlets use WSMAN to connect to remote machines. This is triggered by using the –ComputerName parameter. The error means you haven’t got the winrm service running on the local machine. On modern Windows remoting, and therefore winrm, are enable by default for servers but disable for client OS e.g. Windows 10. Easiest way to get this to work is run Enable-PSremoting from and elevated prompt.

The post ComputerName parameters for CIM and WMI cmdlets appeared first on PowerShell for Windows Admins.

Applying updates through WSUS

$
0
0

I like to keep the virtual machines in my test lab up to date so have a WSUS server to download and manage updates. The difficulty is applying the updates. With Windows 2012 R2 I used a module that would contact the WSUS server and apply the updates – the was especially useful on server core installations.

I found with Windows 2016 that this COM based module wasn’t reliable so after a bit of investigation discovered that there are some CIM classes that you can use to discover and apply available updates and see what updates have been applied.

All I need is a simple set of code so wrote a bare bones module that offers three functions:

#Scan for available updates
function Get-AvailableUpdate {
  [CmdletBinding()]
  param()
  $ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession  
  $result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true}
  $result.Updates
}

#Install all available updates
function Install-AvailableUpdate {
  [CmdletBinding()]
  param()
  $ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
  Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates
}

#list installed updates
function Get-InstalledUpdate {
  [CmdletBinding()]
  param()
  $ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
  $result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=1";OnlineScan=$true}
  $result.Updates
}

Testing so far seems to be good. As this is just for me I’m bothering with adding error testing or other production ready stuff. This works and I’ll fix problems as they occur

The post Applying updates through WSUS appeared first on PowerShell for Windows Admins.

wmic deprecated

$
0
0

I saw a forum post today where the question involved the use of the wmi command line tool wmic.

Wmic was deprecated in Windows Server 2012 – https://technet.microsoft.com/en-us/library/hh831568(v=ws.11).aspx. It will eventually be removed.

You should use the CIM cmdlets instead of wmic. The syntax is much easier and the resultant code is easier to understand.

A little known fact – the PowerShell project was originally started as a replacement for wmic.

The post wmic deprecated appeared first on PowerShell for Windows Admins.

Finding DNS static records

$
0
0

An interesting question from the forums about finding DNS static records

You can view the records in a DNS zone

Get-CimInstance -Namespace root\MicrosoftDNS -ClassName MicrosoftDNs_Atype -ComputerName W16DC01  -Filter “DomainName = ‘manticore.org'” |
select OwnerName, Timestamp, IPAddress

but how do you know which are static records.

There isn’t an obvious way to do it but if you dig into the records (hint create a static record and look at the differences) you’ll see that static records have a timestamp of 0

So to see just the static records

Get-CimInstance -Namespace root\MicrosoftDNS -ClassName MicrosoftDNs_Atype -ComputerName W16DC01 -Filter “DomainName = ‘manticore.org’ AND Timestamp = 0” |
select OwnerName, Timestamp, IPAddress

Rather than using the CIM class directly it’s simpler to use the cmdlets from the DNSserver module – install the remote admin tools to get access.

To view the A type records

Get-DnsServerResourceRecord -ComputerName W16DC01 -ZoneName ‘manticore.org’ -RRType A

To view the static records only

Get-DnsServerResourceRecord -ComputerName W16DC01 -ZoneName ‘manticore.org’ -RRType A |
where {-not $_.TimeStamp}

Notice that you’re checking for the absence of a timestamp

The post Finding DNS static records appeared first on PowerShell for Windows Admins.

Get-SupportedFileSystems

$
0
0

I stumbled on the Get-SupportedFileSystems cmdlet today. Its part of the Storage module and is defined as a function. Digging a bit further its from a CDXML module based on a CIM class. But which CDXML file contains the definition?

PS> Get-ChildItem -Path 'C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Storage' -File | Select-String -Pattern 'Get-SupportedFileSystems' -SimpleMatch

C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Storage\Storage.psd1:117:        'Get-SupportedFileSystems',
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Storage\Volume.cdxml:405:      // Get-SupportedFileSystems

Looking in Volume.cdxml shows we’re working with the ROOT/Microsoft/Windows/Storage/MSFT_Volume class. You can use this directly

PS> Get-CimInstance -Namespace ROOT/Microsoft/Windows/Storage -ClassName MSFT_Volume | select DriveLetter, FileSystem

DriveLetter FileSystem
----------- ----------
          D
            NTFS
            NTFS
          C NTFS

When you use Get-SupportedFileSystems all you get back is the filesystem

PS> Get-SupportedFileSystems -DriveLetter C
NTFS

The DriveLetter parameter can take an array of chars but if you supply a driveletter where there isn’t a defined filesystem you get an error

PS> Get-SupportedFileSystems -DriveLetter D
Get-SupportedFileSystems : Failed
Activity ID: {25bde807-4d9f-4216-8640-94268ff80624}
At line:1 char:1
+ Get-SupportedFileSystems -DriveLetter D
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (StorageWMI:ROOT/Microsoft/...age/MSFT_Volume) [Get-SupportedFileSystems],
    CimException
    + FullyQualifiedErrorId : StorageWMI 4,Get-SupportedFileSystems

or if the drive isn’t defined

PS> Get-SupportedFileSystems -DriveLetter E
Get-SupportedFileSystems : No MSFT_Volume objects found with property 'DriveLetter' equal to 'E'.  Verify the value of the property and retry.
At line:1 char:1
+ Get-SupportedFileSystems -DriveLetter E
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (E:Char) [Get-SupportedFileSystems], CimJobException
    + FullyQualifiedErrorId : CmdletizationQuery_NotFound_DriveLetter,Get-SupportedFileSystems

The DriveLetter parameter accepts pipeline input by propertyname BUT it has to be a Char not a string.

What would be useful would be to get the drives using Get-PSDrive and pass to Get-SupportedFileSystems

PS> Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize

Name Used (GB) Free (GB) Provider   Root CurrentLocation
---- --------- --------- --------   ---- ---------------
C       202.61    273.55 FileSystem C:\          Scripts
D                        FileSystem D:\

Get-PSDrive outputs the driveletter as the name property and just make life fun its a string.

Time for some PowerShell magic.

This looks good

Get-PSDrive -PSProvider FileSystem |
select @{N='DriveLetter'; E={[char]$_.Name}} |
Get-SupportedFileSystems

but it fails because D doesn’t have a filesystem defined.

So try this

Get-PSDrive -PSProvider FileSystem |
foreach {
  $props = [ordered]@{
    DriveLetter = $_.Name
    FileSystem = Get-SupportedFileSystems -DriveLetter $_.Name -ErrorAction SilentlyContinue
  }
  New-Object -TypeName PSObject -Property $props
}

DriveLetter FileSystem
----------- ----------
C           NTFS      
D

A simple way to check the filesystem used on your Windows machines

 

 

The post Get-SupportedFileSystems appeared first on PowerShell for Windows Admins.


Name mismatch

$
0
0

Ever wondered why you can’t do this:

Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' |
Get-CimInstance -ClassName Win32_OperatingSystem

The –ComputerName parameter on get-CimInstance accepts pipeline input BUT its by property name.

PS> Get-Help Get-CimInstance -Parameter ComputerName

-ComputerName [<String[]>]
    Specifies computer on which you want to run the CIM operation. You can specify a fully qualified domain name
    (FQDN), a NetBIOS name, or an IP address.

    If you do not specify this parameter, the cmdlet performs the operation on the local computer using Component
    Object Model (COM).

    If you specify this parameter, the cmdlet creates a temporary session to the specified computer using the WsMan
    protocol.

    If multiple operations are being performed on the same computer, using a CIM session gives better performance.

    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       True (ByPropertyName)
    Accept wildcard characters?  false

If you look at the output of Get-ADComputer it has a Name property.

PS>  Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org'

DistinguishedName : CN=W16PWA01,OU=Servers,DC=Manticore,DC=org
DNSHostName       : W16PWA01.Manticore.org
Enabled           : True
Name              : W16PWA01
ObjectClass       : computer
ObjectGUID        : 8d137004-1ced-4ff1-bcf4-f0671652fc8c
SamAccountName    : W16PWA01$
SID               : S-1-5-21-759617655-3516038109-1479587680-1322
UserPrincipalName :

So you have a Name mismatch between the property and the parameter.

There are a number of ways to deal with this.

First use foreach

Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' |
foreach {
  Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $psitem.Name | 
  select CSName, Caption
}

Use $psitem.Name (or $_.Name) as the input to –ComputerName. Simple coding and works very nicely.

If you have a lot of computers you may want to use a foreach loop instead

$computers = Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' | select -ExpandProperty Name
foreach ($computer in $computers) {
  Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computer|
  select CSName, Caption
}

Create an array of computer names and iterate through them.

Second use select

Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' |
select @{N='ComputerName';E={$_.Name}} |
Get-CimInstance -ClassName Win32_OperatingSystem |
select CSName, Caption

In this case use select-object to create a property with the name ComputerName (case DOESN’T matter) and pipe that into Get-CimInstance.

This option is a bit more advanced as you have understand how select-object works and how to create extra properties on the object you’re passing down the pipeline. It looks cooler and should get you a few extra “ace powerShell coder” points.

The third option takes advantage of the fact that _Computername accepts an array of computer names

Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName (Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' | select -ExpandProperty Name) |
select CSName, Caption

You run Get-ADComputer and use select –Expand to only return the VALUE of the computer name (a string). This gives you an array of computer names. Because its in () its treated as an input object to the parameter.

Very clever and gets you maximum points.

The post Name mismatch appeared first on PowerShell for Windows Admins.

DiskPart and PowerShell–part 1

$
0
0

An attendee at the Summit made the statement that the DiskPart utility didn’t have any equivalent in PowerShell. That’s not strictly true as the storage module provides a lot of functionality that maps to diskpart functionality.

The module contents include:

PS> Get-Command -Module Storage | select name

Name
—-
Disable-PhysicalDiskIndication
Disable-StorageDiagnosticLog
Enable-PhysicalDiskIndication
Enable-StorageDiagnosticLog
Flush-Volume
Get-DiskSNV
Get-PhysicalDiskSNV
Get-StorageEnclosureSNV
Initialize-Volume
Write-FileSystemCache
Add-InitiatorIdToMaskingSet
Add-PartitionAccessPath
Add-PhysicalDisk
Add-TargetPortToMaskingSet
Add-VirtualDiskToMaskingSet
Block-FileShareAccess
Clear-Disk
Clear-FileStorageTier
Clear-StorageDiagnosticInfo
Connect-VirtualDisk
Debug-FileShare
Debug-StorageSubSystem
Debug-Volume
Disable-PhysicalDiskIdentification
Disable-StorageEnclosureIdentification
Disable-StorageHighAvailability
Disable-StorageMaintenanceMode
Disconnect-VirtualDisk
Dismount-DiskImage
Enable-PhysicalDiskIdentification
Enable-StorageEnclosureIdentification
Enable-StorageHighAvailability
Enable-StorageMaintenanceMode
Format-Volume
Get-DedupProperties
Get-Disk
Get-DiskImage
Get-DiskStorageNodeView
Get-FileIntegrity
Get-FileShare
Get-FileShareAccessControlEntry
Get-FileStorageTier
Get-InitiatorId
Get-InitiatorPort
Get-MaskingSet
Get-OffloadDataTransferSetting
Get-Partition
Get-PartitionSupportedSize
Get-PhysicalDisk
Get-PhysicalDiskStorageNodeView
Get-PhysicalExtent
Get-PhysicalExtentAssociation
Get-ResiliencySetting
Get-StorageAdvancedProperty
Get-StorageDiagnosticInfo
Get-StorageEnclosure
Get-StorageEnclosureStorageNodeView
Get-StorageEnclosureVendorData
Get-StorageFaultDomain
Get-StorageFileServer
Get-StorageFirmwareInformation
Get-StorageHealthAction
Get-StorageHealthReport
Get-StorageHealthSetting
Get-StorageJob
Get-StorageNode
Get-StoragePool
Get-StorageProvider
Get-StorageReliabilityCounter
Get-StorageSetting
Get-StorageSubSystem
Get-StorageTier
Get-StorageTierSupportedSize
Get-SupportedClusterSizes
Get-SupportedFileSystems
Get-TargetPort
Get-TargetPortal
Get-VirtualDisk
Get-VirtualDiskSupportedSize
Get-Volume
Get-VolumeCorruptionCount
Get-VolumeScrubPolicy
Grant-FileShareAccess
Hide-VirtualDisk
Initialize-Disk
Mount-DiskImage
New-FileShare
New-MaskingSet
New-Partition
New-StorageFileServer
New-StoragePool
New-StorageSubsystemVirtualDisk
New-StorageTier
New-VirtualDisk
New-VirtualDiskClone
New-VirtualDiskSnapshot
New-Volume
Optimize-StoragePool
Optimize-Volume
Register-StorageSubsystem
Remove-FileShare
Remove-InitiatorId
Remove-InitiatorIdFromMaskingSet
Remove-MaskingSet
Remove-Partition
Remove-PartitionAccessPath
Remove-PhysicalDisk
Remove-StorageFileServer
Remove-StorageHealthSetting
Remove-StoragePool
Remove-StorageTier
Remove-TargetPortFromMaskingSet
Remove-VirtualDisk
Remove-VirtualDiskFromMaskingSet
Rename-MaskingSet
Repair-FileIntegrity
Repair-VirtualDisk
Repair-Volume
Reset-PhysicalDisk
Reset-StorageReliabilityCounter
Resize-Partition
Resize-StorageTier
Resize-VirtualDisk
Revoke-FileShareAccess
Set-Disk
Set-FileIntegrity
Set-FileShare
Set-FileStorageTier
Set-InitiatorPort
Set-Partition
Set-PhysicalDisk
Set-ResiliencySetting
Set-StorageFileServer
Set-StorageHealthSetting
Set-StoragePool
Set-StorageProvider
Set-StorageSetting
Set-StorageSubSystem
Set-StorageTier
Set-VirtualDisk
Set-Volume
Set-VolumeScrubPolicy
Show-VirtualDisk
Start-StorageDiagnosticLog
Stop-StorageDiagnosticLog
Stop-StorageJob
Unblock-FileShareAccess
Unregister-StorageSubsystem
Update-Disk
Update-HostStorageCache
Update-StorageFirmware
Update-StoragePool
Update-StorageProviderCache
Write-VolumeCache

In this mini series I’m going to go through a number of the diskpart options and show you how to do the same with the Storage module cmdlets.

I’m not sure if all diskpart options are available but it’ll be fun to find out.

The Storage module was introduced with Windows 8/Server 2012.

ls $pshome\modules\storage

or

Get-ChildItem $pshome\modules\storage

if you prefer shows a number of cdxml files. This means that the cmdlets are based on CIM classes which you can see at

Get-CimClass -Namespace ROOT/Microsoft/Windows/Storage

These classes aren’t available on versions of Windows prior to Windows 8/Server 2012.

I’ll also have a look at some of these classes to see if there’s anything we can do that isn’t covered directly by the storage module cmdlets.

The post DiskPart and PowerShell–part 1 appeared first on PowerShell for Windows Admins.

Are your domain controllers real?

$
0
0

A question on the forum asked about discovering if domain controllers are physical or virtual machines. In other words Are your domain controllers real?

This will do the job:

foreach ($domain in (Get-ADForest).domains) {
 Get-ADDomainController -filter * -server $domain |
 sort hostname |
 foreach {
 Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $psitem.Hostname |
 select PSComputerName, Manufacturer, Model
 }
 }

Get the domains in your forest and then for each domain get the domain controllers. Get-ADDomainController outputs an object with a property of hostname – but you need a computername for Get-CimInstance. So, use a foreach-object and use the Hostname property as shown (you could create a property ComputerName on the pipeline object but its more work) and get the results. A virtual machine will show under the Model. You can sort or whatever once you have the results.

The post Are your domain controllers real? appeared first on PowerShell for Windows Admins.

Find the logged on user

$
0
0

One method of finding the logged on users is to use CIM

$ComputerName = $env:COMPUTERNAME
Get-CimInstance -ClassName Win32_Process -ComputerName $ComputerName -Filter "Name = 'explorer.exe'" | 
foreach {
    
    $lguser = Invoke-CimMethod -InputObject $psitem -MethodName GetOwner
    
    $Properties = @{
       ComputerName = $ComputerName
       User = $lguser.User
       Domain = $lguser.Domain
       Time = $User.CreationDate 
    }
          
    New-Object -TypeName PSObject -Property $Properties
 }

Get the Win32_Process instances for explorer.exe and foreach of them use the GetOwner method  to get the owners names and domain. Create an object and ouput

The post Find the logged on user appeared first on PowerShell for Windows Admins.

Finding a CIM class

$
0
0

One of the problems you might find is finding a CIM class. You know its name but you don’t know which namespace its in.

The old WMI cmdlets allow you to search the namespaces recursively

PS> Get-WmiObject -Class Win32_Process -Namespace root -Recurse -List


 NameSpace: ROOT\CIMV2

Name Methods Properties
 ---- ------- ----------
 Win32_Process {Create, Terminat... {Caption, CommandLine, CreationClassName, CreationDate...}

But the CIM cmdlets don’t have this functionality. I’ve been meaning to do something about this for ages but finally got motivated by something I read while proof reading PowerShell in Action – yes its getting closer, much closer.

What I ended up with is these 2 functions

function get-namespace {
 [cmdletBinding()]
param ([string]$namespace = 'root') 
 Get-CimInstance -Namespace $namespace -ClassName '__NAMESPACE' |
 foreach {
 "$namespace\" + $_.Name
 get-namespace $("$namespace\" + $_.Name)
 }
 }

function find-cimclass {
 [cmdletBinding()]
param (
 [string]$namespace = 'root',
 [string]$classname
 )

$class = $null

## test namespace for class
 $class = Get-CimClass -Namespace $namespace -ClassName $classname -ErrorAction SilentlyContinue

if (-not $class) {
 $namespaces = get-namespace -namespace $namespace
 foreach ($name in $namespaces){
 $class = $null
 $class = Get-CimClass -Namespace $name -ClassName $classname -ErrorAction SilentlyContinue
 if ($class){break}
 }
 }

$class
 }

Find-Cimclass takes a namespace and class name as parameters. It tries to find the class in the given namespace. If it can’t find it then get-namespace is called to generate a list of namespaces to search. The function iterates over the collection of namespaces testing each one for the class. When it finds the class it returns the class information.

Get-namespace searches for all instances of the __Namespace class in the given namespace. it then recursively call itself to test each of those namespaces. That way you get the whole tree.

If you’re searching for a given class I recommend that you start at the root class to ensure that you test everywhere.

The post Finding a CIM class appeared first on PowerShell for Windows Admins.

Viewing all 117 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>