Summary: Use Windows PowerShell to display shared folders.
How can I use Windows PowerShell to display shared folders that I set up on a local computer?
Use the Get-SMBShare cmdlet and no parameters.
Summary: Use Windows PowerShell to display shared folders.
How can I use Windows PowerShell to display shared folders that I set up on a local computer?
Use the Get-SMBShare cmdlet and no parameters.
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
Summary: Use Windows PowerShell to find stats of objects.
How can I use Windows PowerShell to see the minimum, maximum, and average values of a
specific property in a series of objects?
Pipe the objects to the Measure-Object cmdlet, specify the property you seek, and use
the Average, Maximum, Sum, and Minimum switches, for example:
$objects | Measure-Object -Property numberofwords -Sum -Average -Maximum -Minimum
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:
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:
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
Summary: Use Windows PowerShell to easily find files that are in the wrong folder.
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?
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:\>
Summary: Microsoft Scripting Guy, Ed Wilson, talks about working with volumes with Windows PowerShell and CIM.
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
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
Summary: Learn how to use Windows PowerShell to find a file or a folder.
How can I use Windows PowerShell to determine if a variable contains a path that leads to a file or folder?
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
Summary: Microsoft Scripting Guy, Ed Wilson, talks about comparing CIM and Windows PowerShell.
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
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
Summary: Use Windows PowerShell to find cmdlets that use CIM.
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?
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
Summary: Microsoft Scripting Guy, Ed Wilson, talks about creating custom type data to control the way a WMI class displays.
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
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.
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
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:
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
Summary: Learn how to remove type data by using Windows PowerShell.
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?
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.
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
Summary: Use Windows PowerShell to easily find a WMI parent class.
How can I use Windows PowerShell to find what parent class a particular WMI class derives from?
Use the Get-CimClass cmdlet, and select the CimSuperClassName property, for example:
(Get-Cimclass win32_bios).CimSuperClassName
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
Summary: Learn how to use Windows PowerShell to remove all CIM sessions.
How can I use Windows PowerShell to delete all the CIM sessions I have created?
Use the Get-CIMSession cmdlet to retrieve all CIM sessions, and pipe the results to the
Remove-CIMSession cmdlet:
Get-CimSession | Remove-CimSession
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:
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:
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:
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:
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:
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:
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
Summary: Use Windows PowerShell to save data as a CSV file and maintain the Unicode encoding.
How can I use Windows PowerShell to save my data as a CSV file but ensure that it is saved as Unicode?
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
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
Summary: Use Windows PowerShell to return a specific number of letters from a string.
How can I use Windows PowerShell to easily to retrieve the first two letters from a string?
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
Summary: Microsoft Scripting Guy, Ed Wilson, talks about finding and using Windows PowerShell providers.
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
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.
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