Monthly Archive

Categories

PowerShell and CIM

Change a computer’s description

The Win32_OperatingSystem class exposes the machines Description. This is how you can easily change a computer’s description.

PS> Get-CimInstance -ClassName Win32_OperatingSystem | select Description

Description 
-----------

PS> Get-CimInstance -ClassName Win32_OperatingSystem | Set-CimInstance -Property @{Description = 'Richards Laptop'} 
PS> Get-CimInstance -ClassName Win32_OperatingSystem | select Description

Description 
----------- 
Richards Laptop

You can see that the description is originally blank. Get the CimInstance of Win32_OperatingSystem and pipe it to Set-CimInstance. The property to change and its new value are held in the hash table that’s the value given to the –Property parameter. You can modify multiple properties at once – just add them as property-value pairs to the hash table

More diskinfo

Yesterday I showed how to get the disk, partition and logical disk information using CIM. Today I want to show more diskinfo techniques.

This time we’ll use the Storage module which was introduced with Windows 8. Underneath the covers it uses CIM – just different classes. The storage module doesn’t differentiate between volumes  and logical disks – it just uses volumes.

To start at the physical disk and get the partition and volumes:

$diskinfo = Get-Disk | foreach {

  $parts = Get-Partition -DiskNumber $psitem.DiskNumber | where DriveLetter

  $disk = $psitem

  foreach ($part in $parts) { 
    
    Get-Volume -Partition $part | 
    foreach { 
      $props = $null

      $props = [ordered]@{ 
        Disk = $disk.Number 
         Model = $disk.Model 
        Firmware = $disk.FirmwareVersion 
        SerialNUmber = $disk.SerialNumber 
        'DiskSize(GB)' = [math]::Round(($disk.AllocatedSize / 1GB ), 2) 
        Partitions = $disk.NumberOfPartitions 
        Partition = $part.PartitionNumber 
        BootPartition = $part.IsBoot 
        'PartitionSize(GB)' = [math]::Round(($part.Size / 1GB ), 2) 
        VolumeBlockSize = $psitem.AllocationUnitSize 
        LDiskName = $psitem.DriveLetter 
        FileSystem = $psitem.FileSystem 
         LDiskSize =  [math]::Round(($psitem.Size / 1GB ), 2) 
        LDiskFree =  [math]::Round(($psitem.SizeRemaining / 1GB ), 2) 
       }

      New-Object -TypeName PSObject -Property $props 
    } 
  } 
 } 
 $diskinfo

And to go the other way

$diskinfo =  Get-Volume | 
 where {$_.DriveLetter -AND $_.DriveType -eq 'Fixed'} | 
foreach {

      $part = Get-Partition -DriveLetter $psitem.DriveLetter 
       
      $disk = Get-Disk -Partition $part

      $props = $null

      $props = [ordered]@{ 
        Disk = $disk.Number 
         Model = $disk.Model 
        Firmware = $disk.FirmwareVersion 
        SerialNUmber = $disk.SerialNumber 
        'DiskSize(GB)' = [math]::Round(($disk.AllocatedSize / 1GB ), 2) 
        Partitions = $disk.NumberOfPartitions 
        Partition = $part.PartitionNumber 
        BootPartition = $part.IsBoot 
        'PartitionSize(GB)' = [math]::Round(($part.Size / 1GB ), 2) 
         VolumeBlockSize = $psitem.AllocationUnitSize 
        LDiskName = $psitem.DriveLetter 
         FileSystem = $psitem.FileSystem 
        LDiskSize =  [math]::Round(($psitem.Size / 1GB ), 2) 
        LDiskFree =  [math]::Round(($psitem.SizeRemaining / 1GB ), 2) 
      }

      New-Object -TypeName PSObject -Property $props 
 } 
 $diskinfo

The number of blocks doesn’t seem to be available – suppose you could calculate it – otherwise the information is the same as with the CIM classes you saw last time. Some of the property names are different.

Linking disks, partitions and logical drives

A question of the forums was asking about discovering disk information. They were trying to pipe the output of Get-WmiObject into another Get-WmiObject. that won’t work. There is another way. On Windows machines physical drives are divided into 1 or more partitions which are each divided into one or more logical disks. Linking disks, partitions and logical drives is a relatively simple process.

You can start at the physical disk and work down to the logical disks or start at the logical disk and work back to the physical disk. Lets start with the logical disk.

$diskinfo = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" | 
foreach { 
  $props = $null 
  
  $part = Get-CimAssociatedInstance -InputObject $psitem -ResultClass Win32_DiskPartition 
  $disk = Get-CimAssociatedInstance -InputObject $part -ResultClassName Win32_DiskDrive 
  
  $props = [ordered]@{ 
     Disk = $disk.Index 
     Model = $disk.Model 
     Firmware = $disk.FirmwareRevision 
     SerialNUmber = $disk.SerialNumber 
     'DiskSize(GB)' = [math]::Round(($disk.Size / 1GB ), 2) 
     Partitions = $disk.Partitions 
     Partition = $part.index 
     BootPartition = $part.BootPartition 
     'PartitionSize(GB)' = [math]::Round(($part.Size / 1GB ), 2) 
     Blocks = $part.NumberOfBlocks 
     BlockSize = $part.BlockSize 
     LDiskName = $psitem.Caption 
     FileSystem = $psitem.FileSystem 
      LDiskSize =  [math]::Round(($psitem.Size / 1GB ), 2) 
     LDiskFree =  [math]::Round(($psitem.FreeSpace / 1GB ), 2) 
  }

  New-Object -TypeName PSObject -Property $props

}

$diskinfo

Use Get-CimInstance to retrieve the instances of the Win32_LogicalDisk class. Use a filter for DriveType = 3 – which is local disks (as far as the server is concerned – they could be on a SAN or NAS).

Foreach of the disks get the associated partition and use that object to get the associated physical drive.

CIM (WMI) has the concept of associators and references.

A reference is a pointer showing you which instance is associated with another instance. For example:

PS> Get-CimInstance -ClassName Win32_LogicalDiskToPartition


Antecedent      : Win32_DiskPartition (DeviceID = "Disk #0, Partition #1") 
Dependent       : Win32_LogicalDisk (DeviceID = "C:") 
EndingAddress   : 511578663935 
StartingAddress : 368050176 
PSComputerName  :

Logical disk C: is associated with partition #1 on disk #0

If you want to actually get the associated class then you do this

PS> $ld = Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DeviceID = "C:"' 
 PS> Get-CimAssociatedInstance -InputObject $ld -ResultClass Win32_DiskPartition

Name             NumberOfBlocks       BootPartition        PrimaryPartition     Size                Index 
 ----             --------------       -------------        ----------------     ----                ----- 
 Disk #0, Part... 998458230            False                True                 511210613760        1

or

PS> Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DeviceID = "C:"' |   Get-CimAssociatedInstance -ResultClass Win32_DiskPartition

Name             NumberOfBlocks       BootPartition        PrimaryPartition     Size                Index 
 ----             --------------       -------------        ----------------     ----                ----- 
 Disk #0, Part... 998458230            False                True                 511210613760        1

Once you’ve go the partition and physical disk instances. Populate your output object and loop. Notice that the pipeline is output directly to the variable $diskinfo. You don’t need to build arrays – get the pipeline to do it for you.

Each logical disk gets an output like this

Disk              : 0 
 Model             : Samsung SSD 840 PRO Series 
 Firmware          : DXM06B0Q 
SerialNUmber      : S1AXNSAF329511V 
DiskSize(GB)      : 476.93 
 Partitions        : 3 
 Partition         : 1 
BootPartition     : False 
PartitionSize(GB) : 476.1 
 Blocks            : 998458230 
BlockSize         : 512 
LDiskName         : C: 
FileSystem        : NTFS 
LDiskSize         : 476.1 
LDiskFree         : 212.33

That’s working up the stack. What about working down. That’s a similar process:

$diskinfo = Get-CimInstance -ClassName Win32_DiskDrive | 
foreach { 
  $disk = $psitem 
  
  $parts = Get-CimAssociatedInstance -InputObject $psitem -ResultClass Win32_DiskPartition

  foreach ($part in $parts) { 
    
    Get-CimAssociatedInstance -InputObject $part -ResultClassName Win32_LogicalDisk | 
    foreach { 
       $props = $null

      $props = [ordered]@{ 
        Disk = $disk.Index 
        Model = $disk.Model 
        Firmware = $disk.FirmwareRevision 
        SerialNUmber = $disk.SerialNumber 
        'DiskSize(GB)' = [math]::Round(($disk.Size / 1GB ), 2) 
        Partitions = $disk.Partitions 
        Partition = $part.index 
        BootPartition = $part.BootPartition 
         'PartitionSize(GB)' = [math]::Round(($part.Size / 1GB ), 2) 
        Blocks = $part.NumberOfBlocks 
        BlockSize = $part.BlockSize 
        LDiskName = $psitem.Caption 
        FileSystem = $psitem.FileSystem 
        LDiskSize =  [math]::Round(($psitem.Size / 1GB ), 2) 
        LDiskFree =  [math]::Round(($psitem.FreeSpace / 1GB ), 2) 
      }

      New-Object -TypeName PSObject -Property $props 
    } 
  } 
 } 
 $diskinfo

Start with getting the instances of Win32_Diskdrive. Foreach instance get the associated partitions - Win32_DiskPartition.

Iterate through the partitions and get the associated logical disk. Create your object and output.

NOTE: neither of these techniques will show the partitions that don’t contain logical drives so you won’t see the boot partition and other “hidden partitions” on modern Windows machines. if you need those look at Win32_DiskPartition directly.

Finding a CIM class

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.

Find the logged on user

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

 

Are your domain controllers real?

A question on the forum asked about discovering if domain controllers are physical or virtual machines.

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.

DiskPart and PowerShell–part 1

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.

Name mismatch

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.

Get-SupportedFileSystems

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

Finding DNS static records

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