Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all 3333 articles
Browse latest View live

PowerTip: Use PowerShell to Display Shared Folders

$
0
0

Summary: Use Windows PowerShell to display shared folders.

Hey, Scripting Guy! Question How can I use Windows PowerShell to display shared folders that I set up on a local computer?

Hey, Scripting Guy! Answer Use the Get-SMBShare cmdlet and no parameters.


Weekend Scripter: Use PowerShell to Analyze Custom Objects

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to analyze custom objects.

Microsoft Scripting Guy, Ed Wilson, is here. Last week, I talked about using a Windows PowerShell script to collect the number of words and documents for each of several years. To this, I wrote a Windows PowerShell script that trolled a number of folders, opened the Word documents, and gathered the word count. (See Use PowerShell to Count Words and Display Progress Bar.)

The real power of the script comes not in simply emiting the objects to the Windows PowerShell console, but in collecting the objects, and then using Windows PowerShell to process the objects. In fact, with a script that takes a while to run, this is the only practical solution. All I need to do is add $objects = at the point in my script that creates the objects. The revised code is shown here:

   Note  Remember, the only thing I did was add $Objects = to my script in the section where I created the custom objects.

$path = "E:\Data\ScriptingGuys"

$year = $NumberOfDocs = $NumberOfWords = $null

$i = 1

$totalDocs = (Get-ChildItem E:\Data\ScriptingGuys -filter "*doc*" -Recurse -file |

  Where {$_.BaseName -match '^(HSG|WES|QHF)'}).count

$word = New-Object -ComObject word.application

$word.visible = $false

$objects = Get-ChildItem $path -filter "????" -Directory |

 ForEach-Object {

   $year = $_.name

   Get-ChildItem $_.FullName -filter "*doc*" -Recurse -file |

     Where-Object {$_.BaseName -match '^(HSG|WES|QHF)'} |

      ForEach-Object {

      $i++

      Write-Progress -Activity "Processing $($_.BaseName)" `

       -PercentComplete  (($i / $totalDocs)*100) -Status "Working on $year"

      $document = $word.documents.open($_.fullname)

      $NumberOfWords += $document.words.count

      $NumberOfDocs ++

      $document.close() | out-null

      [System.Runtime.Interopservices.Marshal]::ReleaseComObject($document) |

       Out-Null

      Remove-Variable Document }

    [PSCustomObject]@{

     "NumberOfDocuments" = $NumberOfDocs

     "NumberOfWords" = $NumberOfWords

     "Year" = $year}

     $NumberOfDocs = $NumberOfWords = $year = $null }

$word.quit()

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null

Remove-Variable Word

[gc]::collect()

[gc]::WaitForPendingFinalizers()

After I have run the script and created my collection of objects, I will be able to work with the objects until I close Windows PowerShell, change the value of $objects, or remove the variable.

The first thing I do is look at the variable to see what it contains. This is shown here:

PS C:\> $objects

                            NumberOfDocuments                                NumberOfWords Year                                       

                            -----------------                                ------------- ----                                       

                                            6                                         9083 2008                                       

                                          135                                       281606 2009                                       

                                          387                                       672847 2010                                       

                                          379                                       600970 2011                                       

                                          392                                       598339 2012                                       

                                          363                                       502704 2013                                       

                                          388                                       456485 2014                                       

                                          180                                       123584 2015                                        

The next thing I want to do is look at some stats related to the number of words created over the years:

PS C:\> $objects | measure -Property numberofwords -Sum

Count    : 8

Average  :

Sum      : 3245618

Maximum  :

Minimum  :

Property : NumberOfWords

It was over 3 million words! I want to know: What was the average number of words per year, the maximum number in one year, and the minimum number in one year? This code is shown here:

PS C:\> $objects | Measure-Object -Property numberofwords -Sum -Average -Maximum -Minimum 

Count    : 8

Average  : 405702.25

Sum      : 3245618

Maximum  : 672847

Minimum  : 9083

Property : NumberOfWords

But the first year, I only wrote six articles, and so that is skewing the results. I decide that I want to eliminate the first year. I can do that like this with the Select-Object cmdlet:

PS C:\> $objects | select -Last 7

                             NumberOfDocuments                                NumberOfWords Year                                       

                            -----------------                                ------------- ----                                        

                                          135                                       281606 2009                                       

                                          387                                       672847 2010                                        

                                          379                                       600970 2011                                       

                                          392                                       598339 2012                                       

                                          363                                       502704 2013                                       

                                          388                                       456485 2014                                       

                                          180                                       123584 2015           

Now that I have eliminated the first year, I add the Measure-Object cmdlet:

PS C:\> $objects | select -Last 7 |  Measure-Object -Property numberofwords -Sum -Average -Maximum -Minimum 

Count    : 7

Average  : 462362.142857143

Sum      : 3236535

Maximum  : 672847

Minimum  : 123584

Property : NumberOfWords

But what if I want to see the average size of each document? Well, I did not specifically collect that, did I? No problem, I have the information. All I need to do is to create a new object with the information I need. Once again, I use the Select-Object cmdlet as shown here:

PS C:\> $objects | select Year, @{L = "AverageSize"; E = {$_.NumberOfWords / $_.NumberOfDocuments}} | ft -AutoSize

Year      AverageSize

----      -----------

2008 1513.83333333333

2009 2085.97037037037

2010 1738.62273901809

2011   1585.672823219

2012         1526.375

2013 1384.85950413223

2014 1176.50773195876

2015 686.577777777778

What if I want a more in-depth look at the average size? Well, I bring the Measure-Object cmdlet back in to play. This is shown here:

PS C:\> $objects | select Year, @{L = "AverageSize"; E = {$_.NumberOfWords / $_.NumberOfDocuments}} | measure -Property AverageSize -Average -Maximum -Minimum

Count    : 8

Average  : 1462.3024099762

Sum      :

Maximum  : 2085.97037037037

Minimum  : 686.577777777778

Property : AverageSize

So, by creating a custom object and saving that object in a variable, it makes for great offline analysis.

That is all for now. Join me tomorrow for more way cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find Stats with PowerShell

$
0
0

Summary: Use Windows PowerShell to find stats of objects.

Hey, Scripting Guy! Question How can I use Windows PowerShell to see the minimum, maximum, and average values of a
           specific property in a series of objects?

Hey, Scripting Guy! Answer Pipe the objects to the Measure-Object cmdlet, specify the property you seek, and use
           the AverageMaximumSum, and Minimum switches, for example:

$objects | Measure-Object -Property numberofwords -Sum -Average -Maximum -Minimum 

Weekend Scripter: Use PowerShell to Work with Embedded Objects

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to work with embedded objects.

Microsoft Scripting Guy, Ed Wilson, is here. This morning I have my Zune 1.0 cranked up, and I am listening to some tunes. I have a nice cup of English Breakfast tea, and a warm croissant beside me. I also was able to score some homemade orange marmalade.

Anyway, a few years ago, the Scripting Wife and I were in Maui, Hawaii, and we took an early morning drive up a winding road. We entered clouds, and it seemed like it was raining. I thought about turning back because of the rain, but there was no good place to turn. Then we emerged from the clouds, and the view was way cool. Here is a picture:

Photo of landscape

It was like another world that was hidden by the clouds below. In some respect, that is the way embedded objects in Windows PowerShell objects appear.

Here is a basic example of embedded objects. When I use the Get-Process cmdlet to look at a specific process, such as Notepad, Windows PowerShell returns basic information. Often the information like that shown here is enough:

PS C:\> Get-Process notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName                           

-------  ------    -----      ----- -----   ------     -- -----------                           

     85       8     1636       9800   113     0.05   6220 notepad     

If I want to see more information, I pipe the resulting object to the Format-List cmdlet and select all of the properties:

PS C:\> Get-Process notepad | fl *

__NounName                 : Process

Name                       : notepad

Handles                    : 85

VM                         : 118321152

WS                         : 10035200

PM                         : 1675264

NPM                        : 8688

Path                       : C:\Windows\System32\notepad.exe

Company                    : Microsoft Corporation

CPU                        : 0.046875

FileVersion                : 6.3.9600.16384 (winblue_rtm.130821-1623)

ProductVersion             : 6.3.9600.16384

Description                : Notepad

Product                    : Microsoft® Windows® Operating System

Id                         : 6220

PriorityClass              : Normal

HandleCount                : 85

WorkingSet                 : 10035200

PagedMemorySize            : 1675264

PrivateMemorySize          : 1675264

VirtualMemorySize          : 118321152

TotalProcessorTime         : 00:00:00.0468750

BasePriority               : 8

ExitCode                   :

HasExited                  : False

ExitTime                   :

Handle                     : 3132

MachineName                : .

MainWindowHandle           : 2361006

MainWindowTitle            : Untitled - Notepad

MainModule                 : System.Diagnostics.ProcessModule (notepad.exe)

MaxWorkingSet              : 1413120

MinWorkingSet              : 204800

Modules                    : {System.Diagnostics.ProcessModule (notepad.exe),

                             System.Diagnostics.ProcessModule (ntdll.dll),

                             System.Diagnostics.ProcessModule (KERNEL32.DLL),

                             System.Diagnostics.ProcessModule (KERNELBASE.dll)...}

NonpagedSystemMemorySize   : 8688

NonpagedSystemMemorySize64 : 8688

PagedMemorySize64          : 1675264

PagedSystemMemorySize      : 231168

PagedSystemMemorySize64    : 231168

PeakPagedMemorySize        : 1675264

PeakPagedMemorySize64      : 1675264

PeakWorkingSet             : 10072064

PeakWorkingSet64           : 10072064

PeakVirtualMemorySize      : 118374400

PeakVirtualMemorySize64    : 2199141629952

PriorityBoostEnabled       : True

PrivateMemorySize64        : 1675264

PrivilegedProcessorTime    : 00:00:00.0468750

ProcessName                : notepad

ProcessorAffinity          : 255

Responding                 : True

SessionId                  : 1

StartInfo                  : System.Diagnostics.ProcessStartInfo

StartTime                  : 4/4/2015 1:39:29 PM

SynchronizingObject        :

Threads                    : {4012}

UserProcessorTime          : 00:00:00

VirtualMemorySize64        : 2199141576704

EnableRaisingEvents        : False

StandardInput              :

StandardOutput             :

StandardError              :

WorkingSet64               : 10035200

Site                       :

Container                  :

As you can see, the Modules, MainModule, and StartInfo objects contain other objects. As an example, here is the StartInfo object:

PS C:\> Get-Process notepad | select startinfo

StartInfo                                                                                       

---------                                                                                       

System.Diagnostics.ProcessStartInfo       

I can look up the object on MSDN, but I am not sure if it is worth the trouble. I can also use what I call “group and dot.”  All I need to do is put parentheses around the object, and select the StartInfo property. When I do this, I can see everything that is contained in the object. This is shown here:

PS C:\> (Get-Process notepad).startinfo

Verb                    :

Arguments               :

CreateNoWindow          : False

EnvironmentVariables    : {ProgramW6432, Path, PROCESSOR_IDENTIFIER, TEMP...}

RedirectStandardInput   : False

RedirectStandardOutput  : False

RedirectStandardError   : False

StandardErrorEncoding   :

StandardOutputEncoding  :

UseShellExecute         : True

Verbs                   : {}

UserName                :

Password                :

Domain                  :

LoadUserProfile         : False

FileName                :

WorkingDirectory        :

ErrorDialog             : False

ErrorDialogParentHandle : 0

WindowStyle             : Normal

One of the cool things I can do, is continue down the road. I can use a double dot (if you will), and select the WindowStyle property from the ProcessStartInfo object. Because I have already seen that (Get-Process notepad).StartInfo returns a ProcessStartInfo object, I know that I can return a single property from this object. This technique is shown here:

PS C:\> (Get-Process notepad).startinfo.WindowStyle

Normal

Windows PowerShell also has a built-in method of handling this situation. I can use the –ExpandProperty parameter from the Select-Object cmdlet. This is faster (for me) than using the “group and dot” technique because I do not have to back up on the command line. Here is the command:

PS C:\> Get-Process notepad | select -ExpandProperty startinfo

Verb                    :

Arguments               :

CreateNoWindow          : False

EnvironmentVariables    : {ProgramW6432, Path, PROCESSOR_IDENTIFIER, TEMP...}

RedirectStandardInput   : False

RedirectStandardOutput  : False

RedirectStandardError   : False

StandardErrorEncoding   :

StandardOutputEncoding  :

UseShellExecute         : True

Verbs                   : {}

UserName                :

Password                :

Domain                  :

LoadUserProfile         : False

FileName                :

WorkingDirectory        :

ErrorDialog             : False

ErrorDialogParentHandle : 0

WindowStyle             : Normal

This is good information, and it shows me what property contains, but it does not help me match it with the process name. What if I also want to see the process name? I specify the Property name in addition to the property I want to expand:

PS C:\> Get-Process notepad | Select -Property name -ExpandProperty startinfo

Name                    : notepad

Verb                    :

Arguments               :

CreateNoWindow          : False

EnvironmentVariables    : {ProgramW6432, Path, PROCESSOR_IDENTIFIER, TEMP...}

RedirectStandardInput   : False

RedirectStandardOutput  : False

RedirectStandardError   : False

StandardErrorEncoding   :

StandardOutputEncoding  :

UseShellExecute         : True

Verbs                   : {}

UserName                :

Password                :

Domain                  :

LoadUserProfile         : False

FileName                :

WorkingDirectory        :

ErrorDialog             : False

ErrorDialogParentHandle : 0

WindowStyle             : Normal

What if I am only interested in the WindowStyle property from the StartInfo object? I can create a custom property by using a hash table with the Select-Object cmdlet. This technique involves specifying a label and an expression. The label is a string, and it can be abbreviated as L. The expression is a script block, and can be abbreviated as E. Here is an example:

Get-Process notepad | select -Property name, @{L="Style";E={$_.startinfo.WindowStyle}}

When I run this, it returns the following custom object:

Image of command output

This will work for all processes, for a single process, and for other embedded objects.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Use PowerShell to Find Files in Wrong Folder

$
0
0

Summary: Use Windows PowerShell to easily find files that are in the wrong folder.

Hey, Scripting Guy! Question I have a folder for documents and another folder for images. But at times, I discover images in the
           documents folder. How can I use Windows PowerShell to easily detect this situation?

Hey, Scripting Guy! Answer Use the Test-Path cmdlet, and specify the file extension to exclude, for example:

PS C:\> test-path c:\fso -Exclude *.bmp

True

PS C:\> dir c:\fso -Include *.bmp

PS C:\> 

Working with Volumes in CIM

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about working with volumes with Windows PowerShell and CIM.

Hey, Scripting Guy! Question Hey, Scripting Guy! I don’t understand CIM at all. I mean, I thought it was basically WMI, but when I use WMI to look at volumes, I see different stuff. Can you help me?

—AB

Hey, Scripting Guy! Answer Hello AB,

Microsoft Scripting Guy, Ed Wilson, is here. There are some questions that seem to be rather easy to answer, and then become confusing. So I am going to jump in and introduce CIM Week.

The Windows Management Instrumentation (WMI) is Microsoft’s implementation of the Common Information Model (CIM). WMI has been around since NT 4.0. Although it does a great job, there are some issues with it.

For one thing, is relies on Distributed COM (DCOM) and Remote Procedure Calls (RPC)—both of which involve a bit of overhead. In addition, it is not very firewall friendly because it requires a large number of open ports.

In Windows 8, we introduced something that would be lighter, that would be firewall friendly, and that would work with the remoting features in Windows PowerShell. It becomes a bit confusing that we call the cmdlets "CIM cmdlets." Technically, WMI stuff also could be called CIM stuff because it implements CIM. The Windows PowerShell team blog has some great articles that explore the architecture and implantation details, and take a specific example to look at why things seem different.

There is a Get-Volume command. I say command because Get-Command lists it as a function from the Storage module. These are wrappers.

Where some of the confusion comes into play, is in comparing it to the Win32_Volume WMI class. The output is similar, but different.

When I use Get-Volume on my local machine, it returns a nicely formatted output. This is shown here:

PS C:\> Get-Volume

DriveLetter FileSystemLabel FileSystem  DriveType  HealthStatus SizeRemaining Size        

----------- ----------- ----------  ---------  ---------- ----------       ----

C                   NTFS              Fixed      Healthy     108.53 GB  126.48 GB

E           NEW VOLUME  FAT32       Fixed      Healthy       1.52 GB    1.99 GB

            Recovery    NTFS        Fixed      Healthy      59.83 MB     300 MB

D                                   CD-ROM     Healthy           0 B        0 B

One of the things that is a bit confusing with Get-Volume is that there is no –ComputerName parameter. It might seem to be a huge oversight. But there is a parameter named –CimSession. OK, fine. But does that mean I need to first create a CIM session to use the command? And if so, how do I do that?

One thing that is great about the CIM commands is that I can create a CIM session to multiple computers at the same time. I can then use that CIM session as an argument to the –CimSession parameter. Here is an example:

PS C:\> $cim = New-CimSession dc1, sgw

PS C:\> Get-Volume -CimSession $cim

DriveLetter FileSystem FileSystem DriveType HealthStatus SizeRemaining Size PSComputerName

--------- --------- --------- --------- --------- ---------      ---- ---------

C                   NTFS      Fixed     Healthy   116.91 GB 126.48 GB dc1     

          Recovery  NTFS      Fixed     Healthy    59.83 MB    300 MB dc1     

D                             CD-ROM    Healthy         0 B       0 B dc1     

          System... NTFS      Fixed     Healthy    89.18 MB    350 MB sgw     

C                   NTFS      Fixed     Healthy   116.86 GB 126.66 GB sgw     

A                             Removable Healthy         0 B       0 B sgw     

D         VMGUEST   CDFS      CD-ROM    Healthy         0 B  26.31 MB sgw     

I can use the Get-CimSession parameter to see if there are any open CIM sessions. This is shown here:

PS C:\> Get-CimSession

Id           : 3

Name         : CimSession3

InstanceId   : 84597c7e-f348-4f77-a320-3e36aec67a04

ComputerName : dc1

Protocol     : WSMAN

Id           : 4

Name         : CimSession4

InstanceId   : 15cc6611-2085-4c1d-8e71-56c0f64dd08b

ComputerName : sgw

Protocol     : WSMAN

Note that the CIM sessions use WSMan. This is the standard Windows PowerShell remoting protocol that was introduced in Windows PowerShell 2.0. It is cool because it uses a single port and is firewall friendly. In addition, introduced in Windows Server 2012, it is automatically enabled on server operating systems.

As shown here, if I want to remove the CIM sessions, I use the Remove-CimSession cmdlet:

PS C:\> Get-CimSession | Remove-CimSession

PS C:\> Get-CimSession

PS C:\> 

But, I do not have to do that. I can supply a computer name as an argument to the CimSession parameter. When I do this, a connection is made, the command runs, and the connection drops. This is shown here:

PS C:\> Get-Volume -CimSession dc1, sgw

DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size PSComputerName

--------- --------- --------- --------- --------- ---------      ---- ---------

C                   NTFS      Fixed     Healthy   116.91 GB 126.48 GB dc1     

          Recovery  NTFS      Fixed     Healthy    59.83 MB    300 MB dc1     

D                             CD-ROM    Healthy         0 B       0 B dc1     

          System... NTFS      Fixed     Healthy    89.18 MB    350 MB sgw     

C                   NTFS      Fixed     Healthy   116.86 GB 126.66 GB sgw     

A                             Removable Healthy         0 B       0 B sgw     

D         VMGUEST   CDFS      CD-ROM    Healthy         0 B  26.31 MB sgw      

I can verify that nothing is left behind by again running the Get-CimSession cmdlet:

PS C:\> Get-CimSession

PS C:\> 

AB, that is all there is to using CIM to make a connection and to return volume information. CIM Week will continue tomorrow when I will talk about more cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Find File or Folder

$
0
0

Summary: Learn how to use Windows PowerShell to find a file or a folder.

Hey, Scripting Guy! Question How can I use Windows PowerShell to determine if a variable contains a path that leads to a file or folder?

Hey, Scripting Guy! Answer Use the Test-Path cmdlet to test the path stored in the variable, and then specify the PathType parameter,
           for example:

PS C:\> $fso = "c:\fso"

PS C:\> $file = "C:\fso\backup.ps1"

PS C:\> Test-Path $fso -PathType leaf

False

PS C:\> Test-Path $file -PathType leaf

True

Comparing CIM and WMI in PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about comparing CIM and Windows PowerShell.

Hey, Scripting Guy! Question Hey, Scripting Guy! When I query using a CIM function, I get back different stuff than when I query WMI. I don’t get it. Can you help?

—BW

Hey, Scripting Guy! Answer Hello BW,

Microsoft Scripting Guy, Ed Wilson, is here. Yesterday in Working with Volumes in CIM, I began talking about WMI and CIM. Let’s continue by looking more in depth at what happens with Get-Volume and Win32_Volume.

Anyone who has done a lot of work with WMI and with Windows PowerShell knows about types and type data. For example, when I query the WIN32_BIOS WMI class, only a certain amount of information returns. This is shown here:

PS C:\> Get-WmiObject win32_Bios

SMBIOSBIOSVersion : Hyper-V UEFI Release v1.0

Manufacturer      : Microsoft Corporation

Name              : Hyper-V UEFI Release v1.0

SerialNumber      : 6591-2614-0518-3297-7423-5669-65

Version           : VRTUAL - 1

If I want to look at more in-depth information, I can pipe the output to the Format-List cmdlet, and it will return all of the information, as shown here:

PS C:\> Get-WmiObject win32_Bios | fl *

PSComputerName        : C1

Status                : OK

Name                  : Hyper-V UEFI Release v1.0

Caption               : Hyper-V UEFI Release v1.0

SMBIOSPresent         : True

__GENUS               : 2

__CLASS               : Win32_BIOS

__SUPERCLASS          : CIM_BIOSElement

__DYNASTY             : CIM_ManagedSystemElement

__RELPATH             : Win32_BIOS.Name="Hyper-V UEFI Release

                                   v1.0",SoftwareElementID="Hyper-V UEFI Release v1.0",
                                  SoftwareElementState=3,TargetOperatingSystem=0,Version="VRTUAL - 1"

__PROPERTY_COUNT      : 27

__DERIVATION          : {CIM_BIOSElement, CIM_SoftwareElement, CIM_LogicalElement, CIM_ManagedSystemElement}

__SERVER              : C1

__NAMESPACE           : root\cimv2

__PATH                : \\C1\root\cimv2:Win32_BIOS.Name="Hyper-V UEFI Release
      
                                v1.0",SoftwareElementID="Hyper-V UEFI Release v1.0",

                                 SoftwareElementState=3,TargetOperatingSystem=0,Version="VRTUAL - 1"

BiosCharacteristics   : {3, 9, 15, 16...}

BIOSVersion           : {VRTUAL - 1, Hyper-V UEFI Release v1.0, EDK II - 10000}

BuildNumber           :

CodeSet               :

CurrentLanguage       :

Description           : Hyper-V UEFI Release v1.0

IdentificationCode    :

InstallableLanguages  :

InstallDate           :

LanguageEdition       :

ListOfLanguages       :

Manufacturer          : Microsoft Corporation

OtherTargetOS         :

PrimaryBIOS           : True

ReleaseDate           : 20121126000000.000000+000

SerialNumber          : 6591-2614-0518-3297-7423-5669-65

SMBIOSBIOSVersion     : Hyper-V UEFI Release v1.0

SMBIOSMajorVersion    : 2

SMBIOSMinorVersion    : 4

SoftwareElementID     : Hyper-V UEFI Release v1.0

SoftwareElementState  : 3

TargetOperatingSystem : 0

Version               : VRTUAL - 1

Scope                 : System.Management.ManagementScope

Path                  : \\C1\root\cimv2:Win32_BIOS.Name="Hyper-V UEFI Release

                        v1.0",SoftwareElementID="Hyper-V UEFI Release v1.0",
                        SoftwareElementState=3,TargetOperatingSystem=0,Version="VRTUAL - 1"

Options               : System.Management.ObjectGetOptions

ClassPath             : \\C1\root\cimv2:Win32_BIOS

Properties            : {BiosCharacteristics, BIOSVersion, BuildNumber, Caption...}

SystemProperties      : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}

Qualifiers            : {dynamic, Locale, provider, UUID}

Site                  :

Container             :

The default properties that are displayed are the result of type data for the specific WMI class. I can find this by using the Get-TypeData cmdlet. Here is an example that tells me that I do, in fact, have specific type data for the WMI class:

PS C:\> "*win32_bios*" | Get-TypeData

TypeName                                Members                                

--------                                -------                               

System.Management.ManagementObject#r... {}                                    

Microsoft.Management.Infrastructure.... {}              

I can look at this in more detail, by piping the results to the Format-List cmdlet:

S C:\> "*win32_bios*" | Get-TypeData | fl * 

TypeName                        : System.Management.ManagementObject#root\cimv2 \Win32_BIOS

Members                         : {}

TypeConverter                   :

TypeAdapter                     :

IsOverride                      : False

SerializationMethod             :

TargetTypeForDeserialization    :

SerializationDepth              : 0

DefaultDisplayProperty          :

InheritPropertySerializationSet : False

StringSerializationSource       :

DefaultDisplayPropertySet       : System.Management.Automation.Runspaces.PropertySetData

DefaultKeyPropertySet           :

PropertySerializationSet        : 

TypeName                        : Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_BIOS

Members                         : {}

TypeConverter                   :

TypeAdapter                     :

IsOverride                      : False

SerializationMethod             :

TargetTypeForDeserialization    :

SerializationDepth              : 0

DefaultDisplayProperty          :

InheritPropertySerializationSet : False

StringSerializationSource       :

DefaultDisplayPropertySet       : System.Management.Automation.Runspaces.PropertySetData

DefaultKeyPropertySet           :

PropertySerializationSet        : 

But, what I really want to look at is DefaultDisplayPropertySet. This property is what governs the properties that return by default when I query the Win32_Bios cmdlet. So, I look at the property:

PS C:\> ("*win32_bios*" | Get-TypeData).defaultdisplaypropertyset

ReferencedProperties                                                          

--------------------                                                          

{SMBIOSBIOSVersion, Manufacturer, Name, SerialNumber...}                      

{SMBIOSBIOSVersion, Manufacturer, Name, SerialNumber...}     

It is one more level to get the ReferencedProperties. This is shown here:

PS C:\> ("*win32_bios*" | Get-TypeData).defaultdisplaypropertyset.referencedproperties

SMBIOSBIOSVersion

Manufacturer

Name

SerialNumber

Version

SMBIOSBIOSVersion

Manufacturer

Name

SerialNumber

Version

If you look closely, you will see that there are two sets of the same five properties. This is because I have two instances of type data. One is for WMI the other is for CIM.

PS C:\> ("*win32_bios*" | Get-TypeData).typename

System.Management.ManagementObject#root\cimv2\Win32_BIOS

Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_BIOS

At the beginning of this post, I used Get-WMIObject to query Win32_Bios. As shown here, if I use Get-CimInstance, the output is the same:

PS C:\> Get-CimInstance Win32_BIOS

SMBIOSBIOSVersion : Hyper-V UEFI Release v1.0

Manufacturer      : Microsoft Corporation

Name              : Hyper-V UEFI Release v1.0

SerialNumber      : 6591-2614-0518-3297-7423-5669-65

Version           : VRTUAL - 1

What about Win32_Volume and Get-Volume? Well, we have a completely different WMI namespace. I can discover this by looking at the TypeName from Get-Member:

PS C:\> Get-Volume | gm

   TypeName: Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Win

dows/Storage/MSFT_Volume

Name                      MemberType     Definition                            

----                      ----------     ----------                           

Clone                     Method         System.Object ICloneable.Clone()     

Dispose                   Method         void Dispose(), void IDisposable.Di...

Equals                    Method         bool Equals(System.Object obj)       

GetCimSessionComputerName Method         string GetCimSessionComputerName()   

GetCimSessionInstanceId   Method         guid GetCimSessionInstanceId()       

GetHashCode               Method         int GetHashCode()                    

GetObjectData             Method         void GetObjectData(System.Runtime.S...

GetType                   Method         type GetType()                       

ToString                  Method         string ToString()                    

DriveLetter               Property       char DriveLetter {get;}              

FileSystem                Property       string FileSystem {get;}             

FileSystemLabel           Property       string FileSystemLabel {get;set;}    

ObjectId                  Property       string ObjectId {get;}               

Path                      Property       string Path {get;}                   

PSComputerName            Property       string PSComputerName {get;}         

Size                      Property       uint64 Size {get;}                   

SizeRemaining             Property       uint64 SizeRemaining {get;}          

DriveType                 ScriptProperty System.Object DriveType {get=switch...

HealthStatus              ScriptProperty System.Object HealthStatus {get=swi...

Notice, that the TypeName is a CimInstance, like one of the types for the Win32_Bios class. But notice that this also is in Root/Microsoft/Windows/Storage WMI namespace. This namespace is why Win32_Volume does not work on a computer running Windows 7, even though it may have Windows PowerShell 4.0 installed.

If I look for type data related to *volume*, I find four different types—but only one that is related to MSFT_Volume and none related to Win32_Volume. This is shown here:

PS C:\> ("*volume*" | Get-TypeData).TypeName

System.Management.ManagementObject#root\cimv2\Win32_VolumeQuotaSetting

Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_VolumeQuotaSetting

Microsoft.Management.Infrastructure.CimInstance#MSFT_Volume

Microsoft.FailoverClusters.PowerShell.ClusterSharedVolume

When I query Get-Volume, I get a nice clean output:

PS C:\> Get-Volume

DriveLetter FileSystemLabel FileSystem  DriveType  HealthStatus SizeRemaining  Size

----------- ----------- ----------  ---------  ---------- ----------       ----

C                       NTFS        Fixed      Healthy     108.53 GB  126.48 GB

E           NEW VOLUME  FAT32       Fixed      Healthy       1.52 GB    1.99 GB

            Recovery    NTFS        Fixed      Healthy      59.83 MB     300 MB

D                                   CD-ROM     Healthy           0 B        0 B

But, if I query Win32_Volume, I get back the WMI class, but not any formatted data. This is true even if I use Get-CimInstance. This is shown here:

PS C:\> Get-CimInstance win32_volume

Caption                      : C:\

Description                  :

InstallDate                  :

Name                         : C:\

Status                       :

Availability                 :

ConfigManagerErrorCode       :

ConfigManagerUserConfig      :

CreationClassName            :

DeviceID                     : \\?\Volume{a6325fa1-0f39-432c-a6db-e6388000463a}\

ErrorCleared                 :

ErrorDescription             :

LastErrorCode                :

PNPDeviceID                  :

PowerManagementCapabilities  :

PowerManagementSupported     :

StatusInfo                   :

SystemCreationClassName      :

SystemName                   : C1

Access                       :

BlockSize                    : 4096

ErrorMethodology             :

NumberOfBlocks               :

Purpose                      :

Automount                    : True

BootVolume                   : True

Capacity                     : 135810510848

Compressed                   : False

DirtyBitSet                  : False

DriveLetter                  : C:

DriveType                    : 3

FileSystem                   : NTFS

FreeSpace                    : 116536430592

IndexingEnabled              : True

Label                        :

MaximumFileNameLength        : 255

PageFilePresent              : True

QuotasEnabled                : False

QuotasIncomplete             : False

QuotasRebuilding             : False

SerialNumber                 : 3092510724

SupportsDiskQuotas           : True

SupportsFileBasedCompression : True

SystemVolume                 : False

PSComputerName               :

Caption                      : E:\

Description                  :

InstallDate                  :

Name                         : E:\

Status                       :

Availability                 :

ConfigManagerErrorCode       :

ConfigManagerUserConfig      :

CreationClassName            :

DeviceID                     : \\?\Volume{33ce26cc-3d92-11e3-8252-806e6f6e6963}\

ErrorCleared                 :

ErrorDescription             :

LastErrorCode                :

PNPDeviceID                  :

PowerManagementCapabilities  :

PowerManagementSupported     :

StatusInfo                   :

SystemCreationClassName      :

SystemName                   : C1

Access                       :

BlockSize                    : 4096

ErrorMethodology             :

NumberOfBlocks               :

Purpose                      :

Automount                    : True

BootVolume                   : False

Capacity                     : 2135949312

Compressed                   :

DirtyBitSet                  : False

DriveLetter                  : E:

DriveType                    : 3

FileSystem                   : FAT32

FreeSpace                    : 1628012544

IndexingEnabled              :

Label                        : NEW VOLUME

MaximumFileNameLength        : 255

PageFilePresent              : False

QuotasEnabled                :

QuotasIncomplete             :

QuotasRebuilding             :

SerialNumber                 : 3926397947

SupportsDiskQuotas           : False

SupportsFileBasedCompression : False

SystemVolume                 : False

PSComputerName               :

Caption                      : \\?\Volume{7a82aab2-29a6-4a13-9ca9-98e67bb153ef}\

Description                  :

InstallDate                  :

Name                         : \\?\Volume{7a82aab2-29a6-4a13-9ca9-98e67bb153ef}\

Status                       :

Availability                 :

ConfigManagerErrorCode       :

ConfigManagerUserConfig      :

CreationClassName            :

DeviceID                     : \\?\Volume{7a82aab2-29a6-4a13-9ca9-98e67bb153ef}\

ErrorCleared                 :

ErrorDescription             :

LastErrorCode                :

PNPDeviceID                  :

PowerManagementCapabilities  :

PowerManagementSupported     :

StatusInfo                   :

SystemCreationClassName      :

SystemName                   : C1

Access                       :

BlockSize                    : 4096

ErrorMethodology             :

NumberOfBlocks               :

Purpose                      :

Automount                    : True

BootVolume                   : False

Capacity                     : 314568704

Compressed                   : False

DirtyBitSet                  : False

DriveLetter                  :

DriveType                    : 3

FileSystem                   : NTFS

FreeSpace                    : 62734336

IndexingEnabled              : True

Label                        : Recovery

MaximumFileNameLength        : 255

PageFilePresent              : False

QuotasEnabled                : False

QuotasIncomplete             : False

QuotasRebuilding             : False

SerialNumber                 : 2387537837

SupportsDiskQuotas           : True

SupportsFileBasedCompression : True

SystemVolume                 : False

PSComputerName               :

Caption                      : D:\

Description                  :

InstallDate                  :

Name                         : D:\

Status                       :

Availability                 :

ConfigManagerErrorCode       :

ConfigManagerUserConfig      :

CreationClassName            :

DeviceID                     : \\?\Volume{04281f07-3c5a-11e3-824c-806e6f6e6963}\

ErrorCleared                 :

ErrorDescription             :

LastErrorCode                :

PNPDeviceID                  :

PowerManagementCapabilities  :

PowerManagementSupported     :

StatusInfo                   :

SystemCreationClassName      :

SystemName                   : C1

Access                       :

BlockSize                    :

ErrorMethodology             :

NumberOfBlocks               :

Purpose                      :

Automount                    : True

BootVolume                   :

Capacity                     :

Compressed                   :

DirtyBitSet                  :

DriveLetter                  : D:

DriveType                    : 5

FileSystem                   :

FreeSpace                    :

IndexingEnabled              :

Label                        :

MaximumFileNameLength        :

PageFilePresent              :

QuotasEnabled                :

QuotasIncomplete             :

QuotasRebuilding             :

SerialNumber                 :

SupportsDiskQuotas           :

SupportsFileBasedCompression :

SystemVolume                 :

PSComputerName               :

If, I want to use the Win32_Volume WMI class, and if I want the nice clean output I get from Get-Volume, I need to create TypeData for the Win32_Volume class. But that is another story...

BW, there's a good start to understanding how to use CIM. CIM Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


PowerTip: Use PowerShell to Find Cmdlets that Use CIM

$
0
0

Summary: Use Windows PowerShell to find cmdlets that use CIM.

Hey, Scripting Guy! Question I want to see what Windows PowerShell cmdlets will use a CIM session, but when I use Get-Command,
           only a couple things return. How can I fix this?

Hey, Scripting Guy! Answer Windows PowerShell automatically loads modules, but when looking for parameters, it does not load modules.
           Therefore, if you want to see all the commands that use a CIM session, first load the modules, then search for 
           commands that accept the CIMSession parameter, for example:

Get-Module -ListAvailable | Import-Module

Get-Command -ParameterName cimsession

Create Custom Type Data in PowerShell to Format WMI Display

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about creating custom type data to control the way a WMI class displays.

Hey, Scripting Guy! Question Hey, Scripting Guy! Yesterday, you said you could create custom type data to control the way the Win32_Volume WMI class reports back. How would one go about doing that? I looked on the Internet, and to be honest, it looks like it is basically impossible.

—AT

Hey, Scripting Guy! Answer Hello AT,

Microsoft Scripting Guy, Ed Wilson, is here. I love this time of the year. All the windows are open, the azaleas are in bloom, and I am upstairs with my Surface Pro 3, a cup of orange-cream green tea, and a slice of artisan cheese. I also scored some organic grapes, and they are so sweet that I needed to add nothing to my tea this morning.

AT, you are right. Yesterday in Comparing CIM and WMI in PowerShell, I began talking about comparing the results of Get-Volume with the Win32_Volume WMI class.

Obviously, if all of my systems are running Windows 8.1, there is no reason to mess around with using the WMI class. But if that is not the case, it makes sense to make things a bit easier to use.

Create custom type data

I want to be able to update the type data for the Win32_Volume WMI class. There is no custom type data, so I need to create something. Luckily, beginning with Windows PowerShell 3.0, this is super easy. I simply use the Update-TypeData cmdlet. The hard parts are figuring out the type name that I am going to use and figuring out how to accomplish the task.

From yesterday’s article, we know that the Win32_Volume WMI class does not have any custom type data. However, the Win32_Bios WMI class does. So I need to see how the Win32_Bios WMI class type data is named:

PS C:\> gcim Win32_BIOS | Get-TypeName

CimInstance#root/cimv2/Win32_BIOS

PS C:\> gwmi win32_bios | Get-TypeName

ManagementObject#root\cimv2\Win32_BIOS

But I want the full name, so I use a wildcard character to retrieve it. This is shown here:

PS C:\> "*win32_bios*" | Get-TypeData | select typename

TypeName

--------

System.Management.ManagementObject#root\cimv2\Win32_BIOS

Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_BIOS

It appears that all I need to do is change the name of the WMI class on the end of the string. I decide my new type data name will be the following:

Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume

This type data will control the way data returns from the Get-CimInstance cmdlet. I am not going to bother to create type data for the Get-WmiObject cmdlet, because this methodology requires Windows PowerShell 3.0 or later; therefore, the Get-CimInstance cmdlet would be available anyway.

As a test, I decide to create default properties. I use the Update-TypeData cmdlet, and I specify the type name and the default properties. I store the type name in a variable named $dataType. The second command is one long command. The command is shown here:

$dataType = "Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume"

Update-TypeData -TypeName $DataType `

 -DefaultDisplayPropertySet DriveLetter, DriveType, Label, FileSystem, FreeSpace, ` Capacity

As shown here, when I use Get-CimInstance to query Win32_Volume, only the selected properties return:

PS C:\> Get-CimInstance win32_volume

DriveLetter : C:

DriveType   : 3

Label       : SSD

FileSystem  : NTFS

FreeSpace   : 53269737472

Capacity    : 159486308352

DriveLetter : E:

DriveType   : 3

Label       : HybridTerrabyte

FileSystem  : NTFS

FreeSpace   : 392849973248

Capacity    : 1000068870144

DriveLetter :

DriveType   : 3

Label       : Recovery

FileSystem  : NTFS

FreeSpace   : 24080384

Capacity    : 314568704

If I attempt to update the type data, an error occurs. Here is the error message I receive:

PS C:\> E:\Data\ScriptingGuys\2015\HSG_4_13_15\CreateTypeDataForVolumeClass.ps1

Update-TypeData : Error in TypeData

"Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume": The member DefaultDisplayPropertySet is already present.

At E:\Data\ScriptingGuys\2015\HSG_4_13_15\CreateTypeDataForVolumeClass.ps1:11 char:1

+ Update-TypeData -TypeName $DataType `

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [Update-TypeData], RuntimeException

    + FullyQualifiedErrorId : TypesDynamicUpdateException,Microsoft.PowerShell.Commands.UpdateTypeDataCommand

I first need to remove the type data, and then I can update it. This command is shown here:

"Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume" |

Get-TypeData | Remove-TypeData

Add a script property

I see that the output from the drive type is coded. So, I look up the WMI class on MSDN, and add a script property to decode the numeric output. To do this, I use a simple Switch statement:

Switch ($this.psbase.CimInstanceProperties["DriveType"].value)

  {

   0 {"Unknown"}

   1 {"No Root"}

   2 {"Removable"}

   3 {"Local Disk"}

   4 {"Network Disk"}

   5 {"Compact Disk"}

   6 {"RAM Disk"}

  }

I keep my default property set, and I add a ScriptProperty named DriveType. The script block for the Switch statement and to translate the value goes in the –Value parameter. I use $this to gain access to the value from the WMI class property. The revised command is shown here:

$dataType = "Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume"

Update-TypeData -TypeName $DataType `

 -DefaultDisplayPropertySet DriveLetter, DriveType, Label, FileSystem, FreeSpace, Capacity `

 -MemberType ScriptProperty -MemberName DriveType -Value {

  Switch ($this.psbase.CimInstanceProperties["DriveType"].value)

  {

   0 {"Unknown"}

   1 {"No Root"}

   2 {"Removable"}

   3 {"Local Disk"}

   4 {"Network Disk"}

   5 {"Compact Disk"}

   6 {"RAM Disk"}

  }

 }

Now when I use Get-CimInstance to query the Win32_Volume WMI class, it comes back with nicely formatted output. This is shown in the following image:

Image of command output

But it gets better! Because the custom type system works on all instances of the data type, it even works on remote systems. So I update the type data on my local computer, and when I query a remote computer (that has not been updated), the data is properly formatted. It is way cool. This technique is shown here:

PS C:\> $dataType = "Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume"

PS C:\> Update-TypeData -TypeName $DataType `

>>  -DefaultDisplayPropertySet DriveLetter, DriveType, Label, FileSystem, FreeSpace, Capacity `

>>  -MemberType ScriptProperty -MemberName DriveType -Value {

>>   Switch ($this.psbase.CimInstanceProperties["DriveType"].value)

>>   {

>>    0 {"Unknown"}

>>    1 {"No Root"}

>>    2 {"Removable"}

>>    3 {"Local Disk"}

>>    4 {"Network Disk"}

>>    5 {"Compact Disk"}

>>    6 {"RAM Disk"}

>>   }

>>  }

>> 

PS C:\> gcim Win32_Volume

DriveLetter :

DriveType   : Local Disk

Label       : System Reserved

FileSystem  : NTFS

FreeSpace   : 331784192

Capacity    : 366997504

DriveLetter : C:

DriveType   : Local Disk

Label       :

FileSystem  : NTFS

FreeSpace   : 124714565632

Capacity    : 135996108800

DriveLetter : D:

DriveType   : Compact Disk

Label       :

FileSystem  :

FreeSpace   :

Capacity    :

PS C:\> gcim Win32_Volume -CimSession dc1

DriveLetter    : C:

DriveType      : Local Disk

Label          :

FileSystem     : NTFS

FreeSpace      : 125529444352

Capacity       : 135810510848

PSComputerName : dc1

DriveLetter    :

DriveType      : Local Disk

Label          : Recovery

FileSystem     : NTFS

FreeSpace      : 62734336

Capacity       : 314568704

PSComputerName : dc1

DriveLetter    : D:

DriveType      : Compact Disk

Label          :

FileSystem     :

FreeSpace      :

Capacity       :

PSComputerName : dc1

AT, that is all there is to using Windows PowerShell to create custom type data to control the display of a WMI class. CIM Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Remove Type Data with PowerShell

$
0
0

Summary: Learn how to remove type data by using Windows PowerShell.

Hey, Scripting Guy! Question How can I remove a custom type data that is producing an error message when I try to update it
           in my Windows PowerShell session?

Hey, Scripting Guy! Answer Use the Get-TypeData cmdlet to retrieve an instance of the type data, and pipe it to the 
           Remove-TypeData cmdlet, for example:

"Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume" |
Get-TypeData | Remove-TypeData

Note  This is a long single-line command that is broken at the pipeline character for readability.

Look at Schema of WMI Class with PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to look at the schema of a WMI class.

Microsoft Scripting Guy, Ed Wilson, is here. One of the cool things about WMI is that it is largely self-describing. This means that I can use WMI and look at WMI. With the CIM cmdlets, this goes even a step further. For example, if I want to look at the Win32_Desktop WMI class, I can use the Get-CimClass cmdlet, and examine it:

PS C:\> Get-CimClass win32_desktop

   NameSpace: ROOT/cimv2

CimClassName         CimClassMethods      CimClassProperties      

------------                        ---------------          ------------------      

Win32_Desktop                       {}                   {Caption, Description,...

From this output, I can see that the class appears in the Root/cimv2 WMI namespace. Not really a surprise because that is the default namespace. I can also see that there are no methods. This means that I will not be able to do much more than view information. I can drill down further to see the properties. To do this, I select and expand the cimClassProperties property. This is shown here:

Get-CimClass win32_desktop | select -ExpandProperty cimclassproperties

The output, however, scrolls and scrolls because each property is more than a simple name. It contains qualifiers, flags, and more. Here is the output for the first couple of properties:

PS C:\> Get-CimClass win32_desktop | select -ExpandProperty cimclassproperties

Name               : Caption

Value              :

CimType            : String

Flags              : Property, ReadOnly, NullValue

Qualifiers         : {MaxLen, read}

ReferenceClassName :

Name               : Description

Value              :

CimType            : String

Flags              : Property, ReadOnly, NullValue

Qualifiers         : {read}

ReferenceClassName :

One of the things that is interesting is that the qualifiers are Read Only. Some properties are actually Read/Write; therefore, the class may not expose a method, but it still has updatable properties. I first look for all Readproperties. This command and partial output are shown here:

PS C:\> (Get-CimClass win32_desktop | select -ExpandProperty cimclassproperties).where({$psitem.qualifiers -match 'read'})

Name               : Caption

Value              :

CimType            : String

Flags              : Property, ReadOnly, NullValue

Qualifiers         : {MaxLen, read}

ReferenceClassName :

Name               : Description

Value              :

CimType            : String

Flags              : Property, ReadOnly, NullValue

Qualifiers         : {read}

ReferenceClassName : 

Now, I want to see if there are any writable properties exposed via this class. Here is the command I use:

PS C:\> (Get-CimClass win32_desktop | select -ExpandProperty cimclassproperties).where({$psitem.qualifiers -match 'write'})

PS C:\> 

Hmm…nothing comes back.

Well, that was to be expected. I happen to know of a WMI class, Win32_ComputerSystem, that contains a few writable properties. So I test my command on that class. Here is the command and the results:

PS C:\> (Get-CimClass win32_computersystem | select -ExpandProperty cimclassproperties).where({$psitem.qualifiers -match 'write'})

Name               : Roles

Value              :

CimType            : StringArray

Flags              : Property, NullValue

Qualifiers         : {read, write}

ReferenceClassName :

Name               : AutomaticManagedPagefile

Value              :

CimType            : Boolean

Flags              : Property, NullValue

Qualifiers         : {MappingStrings, read, write}

ReferenceClassName :

Name               : AutomaticResetBootOption

Value              :

CimType            : Boolean

Flags              : Property, NullValue

Qualifiers         : {MappingStrings, read, write}

ReferenceClassName :

Name               : CurrentTimeZone

Value              :

CimType            : SInt16

Flags              : Property, NullValue

Qualifiers         : {MappingStrings, read, write}

ReferenceClassName :

Name               : EnableDaylightSavingsTime

Value              :

CimType            : Boolean

Flags              : Property, NullValue

Qualifiers         : {write}

ReferenceClassName :

Name               : SystemStartupDelay

Value              :

CimType            : UInt16

Flags              : Property, NullValue

Qualifiers         : {DEPRECATED, MappingStrings, Privileges, read...}

ReferenceClassName :

Name               : SystemStartupOptions

Value              :

CimType            : StringArray

Flags              : Property, NullValue

Qualifiers         : {DEPRECATED, MappingStrings, Privileges, read...}

ReferenceClassName :

Name               : SystemStartupSetting

Value              :

CimType            : UInt8

Flags              : Property, NullValue

Qualifiers         : {DEPRECATED, MappingStrings, Privileges, read...}

ReferenceClassName :

Name               : Workgroup

Value              :

CimType            : String

Flags              : Property, NullValue

Qualifiers         : {MappingStrings, read, write}

ReferenceClassName :

It seems that some of the properties are deprecated. This means that I am not supposed to use them. I wonder if I can find an easier way to see this information. I modify my command. This command is pretty long, and it is a single-line command that wrapped on the blog:

PS C:\> (Get-CimClass win32_computersystem | select -ExpandProperty cimclassproperties).where({$psitem.qualifiers -match 'write' -and $psitem.qualifiers -match 'deprecated'})

Name               : SystemStartupDelay

Value              :

CimType            : UInt16

Flags              : Property, NullValue

Qualifiers         : {DEPRECATED, MappingStrings, Privileges, read...}

ReferenceClassName :

Name               : SystemStartupOptions

Value              :

CimType            : StringArray

Flags              : Property, NullValue

Qualifiers         : {DEPRECATED, MappingStrings, Privileges, read...}

ReferenceClassName :

Name               : SystemStartupSetting

Value              :

CimType            : UInt8

Flags              : Property, NullValue

Qualifiers         : {DEPRECATED, MappingStrings, Privileges, read...}

ReferenceClassName :

It looks like the startup stuff is all deprecated.

If I only want to look at the property names, I can choose it directly from my query. This is shown here:

PS C:\> (Get-CimClass win32_desktop | select -ExpandProperty cimclassproperties).name

Caption

Description

SettingID

BorderWidth

CoolSwitch

CursorBlinkRate

DragFullWindows

GridGranularity

IconSpacing

IconTitleFaceName

IconTitleSize

IconTitleWrap

Name

Pattern

ScreenSaverActive

ScreenSaverExecutable

ScreenSaverSecure

ScreenSaverTimeout

Wallpaper

WallpaperStretched

WallpaperTiled

Because I may not know what qualifiers a WMI class supports, I can easily use the Get-CimClass cmdlet to return those qualifiers:

PS C:\> (Get-CimClass win32_process).CimClassQualifiers.name

Locale

UUID

CreateBy

DeleteBy

dynamic

provider

SupportsCreate

SupportsDelete

PS C:\> (Get-CimClass win32_desktop).CimClassQualifiers.name

Locale

UUID

dynamic

Privileges

provider

PS C:\> (Get-CimClass win32_Computersystem).CimClassQualifiers.name

Locale

UUID

dynamic

provider

SupportsUpdate

Keep in mind that these are qualifiers on the WMI class itself, not qualifiers on the properties. I can obtain a list of all the qualifiers on all of the properties, sort them, and then get the unique qualifier names. This is shown here:

PS C:\> (Get-CimClass win32_process | select -ExpandProperty cimclassproperties).qualifiers.name | sort | get-unique

CIM_Key

Fixed

key

MappingStrings

MaxLen

Override

Privileges

Propagated

read

ValueMap

That is all there is to using Windows PowerShell to look at a WMI class schema. CIM Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find WMI Parent Class with PowerShell

$
0
0

Summary: Use Windows PowerShell to easily find a WMI parent class.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find what parent class a particular WMI class derives from?

Hey, Scripting Guy! Answer Use the Get-CimClass cmdlet, and select the CimSuperClassName property, for example:

(Get-Cimclass win32_bios).CimSuperClassName

Combine PowerShell Remoting and CIM

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about combining Windows PowerShell remoting and CIM.

Microsoft Scripting Guy, Ed Wilson, is here. One of the cool things about Windows PowerShell is remoting. It is not a new feature. In fact, it was introduced in Windows PowerShell 2.0—a time that seems like eons ago. Really. That was back in Windows Server 2008 R2, so that was in 2009. Anyway, it is turned on by default in Windows Server 2012 and later.

Windows PowerShell remoting lets me connect to remote servers or workstations, over a single port, and in a secure manner. In addition, it will take advantage of domain rights and integration. I can work on one remote server, or a whole bunch all at the same time.

I like being able to connect to a specific server. When I do, it is like working in a Windows PowerShell console on my laptop. To do this, all I need to do is to use the Enter-PSSession cmdlet and specify the remote server. When I am there, I can use the Get-CimInstance cmdlet to retrieve WMI information, or I could use Get-WmiObject cmdlet. Because I know that I have the CIM cmdlets on the remove server, I will use Get-CimInstance. Here is an example:

PS C:\> Enter-PSSession -ComputerName dc1

[dc1]: PS C:\Users\Administrator\Documents> Get-CimInstance win32_bios

SMBIOSBIOSVersion : Hyper-V UEFI Release v1.0

Manufacturer      : Microsoft Corporation

Name              : Hyper-V UEFI Release v1.0

SerialNumber      : 3601-6926-9922-0181-5225-8175-58

Version           : VRTUAL - 1

[dc1]: PS C:\Users\Administrator\Documents> exit

PS C:\>

Now why would I really do something like this? I mean, if I have Windows PowerShell remoting turned on, and I have the CIM cmdlets, why wouldn’t I run my CIM commands remotely? For one thing, I may want to perform more than one command at a time. In addition, I may want to have more direct access to resources on the remote computer. For example, I can create folders and files on the remote server as if I was on my own laptop.

In the following example, I enter a remote Windows PowerShell session on a server named DC1. I change my working directory to the root of drive C, and then I create a folder off the root named FSO. I then use the Get-CimInstance cmdlet to retrieve WMI information from the Win32_BIOS WMI class, and I redirect the output to a text file in the FSO folder. I then use Get-Content to retrieve that content. Here are the commands and the output:

PS C:\> Enter-PSSession -ComputerName dc1

[dc1]: PS C:\Users\Administrator\Documents> sl c:\

[dc1]: PS C:\> md fso

    Directory: C:\

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

d----         4/15/2015  11:52 PM            fso

[dc1]: PS C:\> Get-CimInstance win32_bios >> c:\fso\mybiosINFO.txt

[dc1]: PS C:\> get-content C:\fso\mybiosINFO.txt

SMBIOSBIOSVersion : Hyper-V UEFI Release v1.0

Manufacturer      : Microsoft Corporation

Name              : Hyper-V UEFI Release v1.0

SerialNumber      : 3601-6926-9922-0181-5225-8175-58

Version           : VRTUAL - 1

[dc1]: PS C:\> exit

PS C:\>

Now, certainly, I could have done all of this without entering a direct remote Windows PowerShell session on DC1, but it would have been more complicated. One other thing that is cool to do is create multiple Windows PowerShell remote sessions, save the sessions, and then enter them as required.

First I use the New-PSSession cmdlet to create two new PSSessions, and I store the resultant sessions in variables:

PS C:\> $dc1 = New-PSSession -ComputerName dc1

PS C:\> $sgw = New-PSSession -ComputerName sgw

Next, when I use the Enter-PSSession cmdlet, I specify the session that is contained in the variable:

PS C:\> Enter-PSSession -Session $dc1

[dc1]: PS C:\Users\Administrator\Documents> gcim Win32_LocalTime

Day            : 15

DayOfWeek      : 3

Hour           : 23

Milliseconds   :

Minute         : 58

Month          : 4

Quarter        : 2

Second         : 10

WeekInMonth    : 3

Year           : 2015

PSComputerName :

[dc1]: PS C:\Users\Administrator\Documents> exit

What is really cool is that  I can now use the Up arrow to recall the commands that I ran on the other server. The commands and output are shown here:

PS C:\> Enter-PSSession -Session $sgw

[sgw]: PS C:\Users\administrator.NWTRADERS\Documents> gcim Win32_LocalTime

Day            : 15

DayOfWeek      : 3

Hour           : 23

Milliseconds   :

Minute         : 58

Month          : 4

Quarter        : 2

Second         : 30

WeekInMonth    : 3

Year           : 2015

PSComputerName :

[sgw]: PS C:\Users\administrator.NWTRADERS\Documents> exit

Now you know how to use Windows PowerShell remoting and CIM. Join me tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Remove All CIM Sessions

$
0
0

Summary: Learn how to use Windows PowerShell to remove all CIM sessions.

Hey, Scripting Guy! Question How can I use Windows PowerShell to delete all the CIM sessions I have created?

Hey, Scripting Guy! Answer Use the Get-CIMSession cmdlet to retrieve all CIM sessions, and pipe the results to the 
           Remove-CIMSession cmdlet:

Get-CimSession | Remove-CimSession


Weekend Scripter: Oh No! Oh Wait...PowerShell for the Win!

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to clean up an impossible document.

Microsoft Scripting Guy, Ed Wilson, is here. Sometimes I just cringe. It is a reflex reaction born from many long (and at times, torturous) years in the IT field. I know, I should be able to get over it, and sometimes I actually think I am getting better. And then things like the following crop up in my email...

I recently received a panic email from an IT admin. He was tasked with creating over 1,200 new user accounts. Hey, no problem. Give me a properly formatted CSV file or Excel spreadsheet, and it is a piece of cake.

Dude, that was the problem. Some *&^%$#@!!! in HR typed all of the new names in a Word doc. Here is a screenshot of the Word document:

Image of name list

My initial reaction ran something along the lines of,  “You have GOT to be kidding me!”

My secondary reaction went along the lines of, “OK. Reply to the email, attach an Excel spreadsheet, and tell them to copy the names into the proper columns. When they do the job right, I will be glad to help create the new user accounts.”

But then the email said that the person's boss said, “Just fix it.” So that ended that idea.

I closed my laptop and mumbled something along the lines of, “That’s ridiculous,” and I decided to go make a pot of tea. After having a great cup of Darjeeling tea, I was sufficiently calmed down and ready to try something.

First of all, I did not want to deal with automating Word, so I decided to copy the three columns of user names into a text file. I figured Windows PowerShell could easily clean up the text file, and I would be using it as an intermediate stage on the way to a CSV file.

I was pleasantly surprised in that when I copied and pasted all of the user names into the text file, everything came out as a single column. Here is a screenshot of the text file:

Image of name list

The second issue I was worried about was that some of the names had Unicode characters in them. Luckily, Notepad prompted me about that, and I was able to save the file as Unicode. It is a simple control next to the Save button that permits me to choose the encoding type:

Image of menu

The first thing I need to do is to remove all of the single letter labels. The A, B, C kind of things that luckily appear on single lines. To do this, I read the contents of the text file, store it in a variable, and then check the length of each line. After I check the length, I title case the letters, trim any leading or trailing spaces from the words, and split on a comma. I then create a custom object. Here is my code so far:

$rawtext = Get-Content C:\DataIn\Names_IN.txt

Foreach ($n in $rawtext)

{

  If($n.Length -gt 2)

   {

    $name = (Get-Culture).TextInfo.ToTitleCase($n).split(',').trim()

    [PSCustomObject]@{

    Lname = $name[0]

    Fname = $name[1]} }

}

When I run the script, I can see from the output that I have come a long way towards my goal. Here is the script and output:

Image of command output

But I now see that I have another problem. Some of the user names have a middle initial, and some do not. I need to fix that. It should be another simple if/else sort of thing. The tricky part (so to speak) is that I need to add my additional logic at the correct point in my code.

This point is where my custom object emits. Right now, it emits to the default output location, which is the Windows PowerShell ISE output pane. I want to send it down a pipeline, check to see if there is a space in the Fname property, and if there is, split that into two—an Fname property and an Mname property. If there is not a middle, I will leave the property blank as the default value.

Here is my code now:

$rawtext = Get-Content C:\DataIn\Names_IN.txt

Foreach ($n in $rawtext)

{

  If($n.Length -gt 2)

   {

    $name = (Get-Culture).TextInfo.ToTitleCase($n).split(',').trim()

    [PSCustomObject]@{

    Lname = $name[0]

    Fname = $name[1]}  |

    ForEach-Object {

     If($_.Fname -match ' ')

      {

       $fn = $_.Fname -split ' '

       [PSCustomObject]@{

        Lname = $name[0]

        Fname = $fn[0]

        Mname = $fn[1]}

        }

      ELSE {

       [PSCustomObject]@{

        Lname = $name[0]

        Fname = $name[1]

        Mname = ''}}

      }

    }

}

Here is the script and the output as it stand at this point:

Image of command output

Now all I need to do is to save my output as a CSV file so I will be able to import it into Active Directory. Luckily, there is an Export-CSV cmdlet that should be able to handle this. Because I emit my custom objects in two places, I need to pick up the objects in two different places and use that to append to my CSV file. Remember, I have Unicode characters in my text file, so I need to specify the encoding as Unicode. Here is my completed script:

$rawtext = Get-Content C:\DataIn\Names_IN.txt

Foreach ($n in $rawtext)

{

  If($n.Length -gt 2)

   {

    $name = (Get-Culture).TextInfo.ToTitleCase($n).split(',').trim()

    [PSCustomObject]@{

    Lname = $name[0]

    Fname = $name[1]}  |

    ForEach-Object {

     If($_.Fname -match ' ')

      {

       $fn = $_.Fname -split ' '

       [PSCustomObject]@{

        Lname = $name[0]

        Fname = $fn[0]

        Mname = $fn[1]} |

         Export-Csv -Path C:\DataOut\Names_Out.CSV -Encoding Unicode `

          -NoTypeInformation -Append

        }

      ELSE {

       [PSCustomObject]@{

        Lname = $name[0]

        Fname = $name[1]

        Mname = ''} |

         Export-Csv -Path C:\DataOut\Names_Out.CSV -Encoding Unicode `

          -NoTypeInformation -append }

      }

    }

My CSV file is shown here:

Image of name list

OK...I will admit it. I spent more time whining about cleaning up this data than I spent writing the script. Windows PowerShell makes performing these sorts of data-grooming tasks easy. I knew this, but dude, I did not think it would be this easy. The script appears long, but it is repetitive. There may be an easier way to do this, but this did not take very long at all.

Join me tomorrow when I will create a simple script to use my newly cleaned CSV file to create over 1200 users in Active Directory. I KNOW this task will be easy.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Save CSV Data and Maintain Unicode Encoding

$
0
0

Summary: Use Windows PowerShell to save data as a CSV file and maintain the Unicode encoding.

Hey, Scripting Guy! Question How can I use Windows PowerShell to save my data as a CSV file but ensure that it is saved as Unicode?

Hey, Scripting Guy! Answer Make sure you specify the –Encoding parameter when you call the Export-CSV cmdlet, for example:

Export-Csv -Path C:\Data\Names.CSV -Encoding Unicode -NoTypeInformation

Weekend Scripter: Use PowerShell to Create Users in Active Directory

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to read a CSV file and create users in Active Directory.

Microsoft Scripting Guy, Ed Wilson, is here. Yesterday in Oh No! Oh Wait...PowerShell for the Win!, I created a CSV file from a Word document that I had been given. Today, I take the next logical step and create the users.

When I have a working CSV file, I can begin the process of writing a script that will create the users in Active Directory.

Note  The CSV file that I was given had more than 1,200 lines in it. Although I gave it a quick “once over glance,” I did not check every single line in the file. So, I fully expect that there will be some potential problems with the data.

The first thing I want to do is to import the Active Directory module. I do this because I know that I am going to be using a few cmdlets from that module, so I may as well install it. This is actually a bit faster.

Note  I am working on a domain-joined computer running Windows 8.1. I have access to the Active Directory module because I installed the Remote Server Admin Tools (RSAT). For more information about RSAT, including how to obtain the correct version and install the tools, see Install Free PowerShell Remote Server Admin Tools.

The next thing I do is check to see if the organizational unit (OU) I intend to install is present. If the OU (named DataImport) is not present, then I create the OU. When I create it, I do so without setting the ProtectedFromAccidentalDeletion flag. (That is because I want to be able to delete this OU without having to do anything special.) Here are the first couple of lines of code:

Import-Module ActiveDirectory

If(!(Get-ADObject -Filter "name -eq 'DataImport'"))

 {

  New-ADOrganizationalUnit -Name dataImport -Description "for data import" `

   -ProtectedFromAccidentalDeletion:$false}

The next thing I do is import my CSV file:

$names = Import-Csv -Encoding Unicode -Path C:\DataIn\Names_Out.CSV 

Now, I walk through the CSV file and create all of the parameters I will use when I create my users. The DisplayName property is composed of the first name, the middle name (if it exists), and the last name. I pick up these values from my CSV file. The column headers are Fname (for first name), Mname (for middle name) and Lname (for last name). This is shown here:

foreach($name in $names)

{

 $Params = @{

  DisplayName = "$($name.Fname) $($name.Mname) $($name.Lname)"

I then specify the GivenName property. The GivenName property corresponds to the Fname field in my CSV file. Here is that assignment:

GivenName = $name.Fname 

I use the first letter from the middle name to assign to the Initials property. To do this, I index into the array of letters that make up the middle name and choose the first one (it begins counting at 0). This is shown here:

Initials = $name.Mname[0] 

The Surname property receives the Lname field from my CSV file. As shown here, this is a straightforward value assignment:

Surname = $name.Lname

When I ran the script the first time, I received 21 error messages. This is because some of the names were too long. So I decided to cheat a bit and select only the first couple of letters from the first name and combine it with the last name. Here is the code I use to do this:

Name = "$($name.Fname.Substring(0,2)) $($name.Lname)"

I used the same logic for the SamAccountName property. The code appears here:

SamAccountName = "$($name.Fname.Substring(0,2)).$($name.Lname)"

I then assign the path to the organizational unit I will use to hold my newly created user accounts:

Path = "ou=DataImport,DC=Nwtraders,Dc=Com"

 }

Because I expect the script to run into a few issues (for example, I do not check for duplicate account names), I use Try/Catch/Finally. The first thing I do is set the Error Action Preference to Stop. This will catch non-terminating errors. Then I create the new users, and if an error occurs, I display the display name. Finally, I set the value of the $ErrorActionPreference variable to SilentlyContinue.

The reason for setting the error action to SilentlyContinue, is that I do not check for the length of first names when I use SubString to select only the first two letters. In my data, I discovered that some user names did not include a complete first name, but rather, only had a first initial. This was causing a few of my initial errors. So because I knew about the issue, I decided to hide the error messages.

The code is shown here:

$ErrorActionPreference = "Stop"

 Try {New-ADUser @Params}

 Catch { "Error creating ... " ;$params.DisplayName }

 Finally {$ErrorActionPreference = "silentlycontinue"}

}

Here is the complete script:

Import-Module ActiveDirectory

If(!(Get-ADObject -Filter "name -eq 'DataImport'"))

 {

  New-ADOrganizationalUnit -Name dataImport -Description "for data import" `

   -ProtectedFromAccidentalDeletion:$false}

$names = Import-Csv -Encoding Unicode -Path C:\DataIn\Names_Out.CSV

foreach($name in $names)

{

 $Params = @{

  DisplayName = "$($name.Fname) $($name.Mname) $($name.Lname)"

  GivenName = $name.Fname

  Initials = $name.Mname[0]

  Surname = $name.Lname

  Name = "$($name.Fname.Substring(0,2)) $($name.Lname)"

  SamAccountName = "$($name.Fname.Substring(0,2)).$($name.Lname)"

  Path = "ou=DataImport,DC=Nwtraders,Dc=Com"

 }

 $ErrorActionPreference = "Stop"

 Try {New-ADUser @Params}

 Catch { "Error creating ... " ;$params.DisplayName }

 Finally {$ErrorActionPreference = "silentlycontinue"}

}

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Return Specific Number of Letters from String

$
0
0

Summary: Use Windows PowerShell to return a specific number of letters from a string.

Hey, Scripting Guy! Question How can I use Windows PowerShell to easily to retrieve the first two letters from a string?

Hey, Scripting Guy! Answer Use the SubString method from a string, and then specify the starting the position and the number
           of letters to return, for example:

PS C:\> ("string").Substring(0,2)

st

Find and Use Windows PowerShell Providers

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about finding and using Windows PowerShell providers.

Hey, Scripting Guy! Question Hey, Scripting Guy! I recently heard a Windows PowerShell MVP say that understanding the Windows PowerShell provider system is fundamental to understanding Windows PowerShell. I think I am in trouble because I have no idea what he is talking about. Can you throw a fella a bone?

—DP

Hey, Scripting Guy! Answer Hello DP,

Microsoft Scripting Guy, Ed Wilson, is here. I am sipping a rather bland cup of tea, but I don’t care because it is a great day. In fact, the weather in Charlotte, North Carolina was rather cold and rainy last week, and today, is also turning out to be nice. What makes it a great day?

Well, I am at the Microsoft Office in Charlotte for the PowerShell Summit. That is right. It kicks off this morning, and it is a veritable Who’s Who of Windows PowerShell greats, including several way cool people from the Windows PowerShell team and nearly half of all the Windows PowerShell MVPs. I mean, this is Posh Geek Paradise (hmmmm...PGP…I wonder if that acronym is available). So who cares if the tea is weak because the day is awesome.

DP, welcome to Provider Week on the Hey, Scripting Guy! Blog. And in fact, Windows PowerShell MVP, Jim Christopher, is presenting a session this afternoon called “Simplex: Stupid Simple Providers.”

Note  This year, all Windows PowerShell Summit sessions are being recorded. They will be posted on the PowerShell.org channel on YouTube. 

What is a Windows PowerShell provider?

A Windows PowerShell provider is basically a sort of abstraction layer that hides the complexities of different types of information stores. This means that by using a Windows PowerShell provider, I can access different types of data in exactly the same way. Because the Windows PowerShell team opened Windows PowerShell providers to developers, anyone can create a new provider to permit one to access data easily by using a standard set of Windows PowerShell cmdlets.

In the past, creating a new Windows PowerShell provider was rather complicated, and there was not really easy documentation to show how to do the job properly. But with Jim Christopher’s provider project, this is no longer the case—it is actually pretty easy (even for non-developers) to create new providers.

So what kinds of data do Windows PowerShell providers provide access to? One way to get an idea is to use the Get-PSProvider cmdlet and look at what returns. Here is an example:

PS C:\Windows\System32\WindowsPowerShell\v1.0> Get-PSProvider

Name                 Capabilities                       Drives                           

----                 ------------                       ------                           

Alias                ShouldProcess                      {Alias}                          

Environment          ShouldProcess                      {Env}                            

FileSystem           Filter, ShouldProcess, Credentials {C, E, D}                        

Function             ShouldProcess                      {Function}                       

Registry             ShouldProcess, Transactions        {HKLM, HKCU}                     

Variable             ShouldProcess                      {Variable}                       

Each Windows PowerShell provider exposes a drive called a PSDrive. The PSDrive is then used to display the data that is exposed via the Windows PowerShell provider. So by default, I have access to the Alias, Environment, FileSystem, Function, Registry, and Variable providers.

But there are more. Two providers, the WSMan and the Certificate providers do not load by default. This is for performance reasons. When I access the drive, the provider loads. In the following example, I access the WSMAN and the Cert drives and force the respective providers to load.

PS C:\Windows\System32\WindowsPowerShell\v1.0> sl wsman:

PS WSMan:\> Get-PSProvider

Name                 Capabilities                       Drives                           

----                 ------------                       ------                           

Alias                ShouldProcess                      {Alias}                          

Environment          ShouldProcess                      {Env}                            

FileSystem           Filter, ShouldProcess, Credentials {C, E, D}                        

Function             ShouldProcess                      {Function}                       

Registry             ShouldProcess, Transactions        {HKLM, HKCU}                     

Variable             ShouldProcess                      {Variable}                       

WSMan                Credentials                        {WSMan}                          

 

PS WSMan:\> sl cert:

PS Cert:\> Get-PSProvider 

Name                 Capabilities                       Drives                           

----                 ------------                       ------                           

Alias                ShouldProcess                      {Alias}                          

Environment          ShouldProcess                      {Env}                            

FileSystem           Filter, ShouldProcess, Credentials {C, E, D}                        

Function             ShouldProcess                      {Function}                       

Registry             ShouldProcess, Transactions        {HKLM, HKCU}                     

Variable             ShouldProcess                      {Variable}                       

WSMan                Credentials                        {WSMan}                           

Certificate          ShouldProcess                      {Cert}                           

The name of the Windows PowerShell provider provides clues as to the type of information that it exposes. For example, the Certificate provider exposes certificate data from the certificate store, the Registry provider exposes the HKLM and the HKCU registry hives, and the WSMan provider exposes the WSMan configuration data.

The Alias, Environment, Function, and Variable providers are all single layers. That is, there are no subdirectories in the stores of information. All of the other providers, however, are multiple layers.

The file system is used as the model for all Windows PowerShell providers. I navigate the Registry in the same way that I would navigate the file system on my laptop. For example, I change my working directory to the C:\FSO folder. I then create new files, rename other files, and delete some additional files. I then go to the C:\Program Files folder to check on some other things or to copy folders and subfolders to a different location.

I can use the same methodology with the other providers. For example, I can set my working location to the HKCU:\Software folder, obtain a directory list of all the subfolders, and copy stuff to a different location for backup purposes. I create new registry keys the same way that I create a new file or folder.

Because the Windows PowerShell provider system is extensible (meaning others can create additional providers), I do not need to learn new techniques to work with the data exposed via their providers. For example, in addition to Jim Christopher’s project, the Microsoft SQL Server and Active Directory teams created providers (see Introduction to the SQL Server 2012 PowerShell Provider and Playing with the AD: Drive for Fun and Profit).

So what cmdlets should I become familiar with if I want to work with Windows PowerShell providers? Here is a list:

Name

Synopsis                                                           

Get-PSProvider

Gets information about the specified Windows PowerShell provider.    

Drive cmdlets

 Work with Windows PowerShell drives

Get-PSDrive

Gets drives in the current session.                                  

New-PSDrive

Creates temporary and persistent mapped network drives.              

Remove-PSDrive

Deletes temporary Windows PowerShell drives and disconnects mapped network drives.                                                          

Location cmdlets

Work with current location

Get-Location

Gets information about the current working location or a location stack.                                                               

Pop-Location

Changes the current location to the location most recently pushed to the stack. You can "pop" the location from the default stack or from a stack that you create by using the Push-Location cmdlet.      

Push-Location

Adds the current location to the top of a location stack.

Set-Location

Sets the current working location to a specified location.           

Item cmdlets

Work with items exposed via providers

Clear-Item

Deletes the content of an item, but does not delete the item.       

Clear-ItemProperty

Deletes the value of a property, but does not delete the property.    

Copy-Item

Copies an item from one location to another.                         

Copy-ItemProperty

Copies a property and value from a specified location to another location.                                                            

Get-Item

Gets files and folders.                                              

Get-ItemProperty

Gets the properties of a specified item.                             

Invoke-Item

Performs the default action on the specified item.                   

Move-Item

Moves an item from one location to another.                          

Move-ItemProperty

Moves a property from one location to another.                       

New-Item

Creates a new item.                                                  

New-ItemProperty

Creates a new property for an item and sets its value.

Remove-Item

Deletes files and folders.                                           

Remove-ItemProperty

Deletes the property and its value from an item.                     

Rename-Item

Renames an item in a Windows PowerShell provider namespace.          

Rename-ItemProperty

Renames a property of an item.                                       

Set-Item

Changes the value of an item to the value specified in the command.  

Set-ItemProperty

Creates or changes the value of a property of an item.               

DP, that is all there is to using Windows PowerShell providers. Provider Week will continue tomorrow when I will talk about way more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Viewing all 3333 articles
Browse latest View live




Latest Images