Prof. Powershell
'Casting' an Object Spell in PowerShell
One handy PowerShell trick is to tell the engine to treat something as a particular type of object.
- By Jeffery Hicks
- 07/23/2013
What makes PowerShell such a powerful management tool is that it is based on objects and not text parsing. Once you have an object it is very easy to work with its properties and methods. Most of the time we don't worry too much about object types because PowerShell does it for us automatically. You can always verify by piping an object to Get-Member.
PS C:\> $s = get-service bits
PS C:\> $s | get-member
If you try these commands you'll see that $S is an object of the type System.ServiceProcess.ServiceController. One handy PowerShell trick is to tell the engine to treat something as a particular type of object. The fancy terminology is "casting". Here are some common ways you can cast objects from the command prompt. Note that in almost every case you are only dealing with a single instance of an object.
PS C:\> [wmi]"root\cimv2:win32_service.name='bits'"
ExitCode : 0
Name : bits
ProcessId : 1436
StartMode : Auto
State : Running
Status : OK
If you know the key to a WMI class you can quickly get a WMI object without having to run Get-WMIObject. This is the same result I would get running
PS C:\> get-wmiobject win32_service -filter "name='bits'"
By the way, to cast a WMI object on a remote computer prepend the string with the server name.
PS C:\> [wmi]\\server01\root\cimv2:win32_service.name='bits'
Another useful cast is [ADSI]. You can use this get create an ADSI COM object for a local user or group.
PS C:\> $admin=[ADSI]"WinNT://chi-win8-01/administrator,user"
PS C:\> $admin.passwordage.value/86400
353.635717592593
Or you can use LDAP and Active Directory assuming you know the distinguished name.
PS C:\> $admin=[ADSI]"LDAP://CN=Administrator,CN=Users,DC=globomantics,DC=local"
PS C:\> $admin.whenchanged
Monday, July 8, 2013 4:00:00 PM
Please note that when using the [ADSI] type accelerator, the monikers WinNT and LDAP are case-sensitive.
Or perhaps you need to quickly create an XML document.
PS C:\> [xml]$results = get-content C:\work\testresults.xml
PS C:\> $results
xml-stylesheet opcr
-------------- ----
type="text/xsl" href="Result.xslt" opcr
Normally, Get-Content would return a simple array of strings. But instead I can cast $results as an XML document.
You can also do some odd things with casting.
PS C:\> [int]$x="5"
PS C:\> $x
5
PS C:\> $x*$x
25
Normally, PowerShell will treat anything in quotes as a string. But in this strange use case, I cast $x as an integer so PowerShell turned the string into a number. But be careful.
PS C:\> $x="jeff"
If you try this you'll get an error like "Cannot convert value "jeff" to type "System.Int32". Error: "Input string was not in a correct format." Once you cast an object as a specific type it is always that type until you completely remove the variable of re-cast it.
PS C:\> [string]$x="jeff"
But you can avoid problems by not re-using variables.
Finally, you can also take most common object types and use them as type accelerators.
PS C:\scripts> [datetime]"12/25/2013"
Wednesday, December 25, 2013 12:00:00 AM
PS C:\> [System.ServiceProcess.ServiceController]"bits"
Status Name DisplayName
------ ---- -----------
Running bits Background Intelligent Transfer Ser...
PS C:\> [system.io.fileinfo]"C:\work\a.xml"
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 10/22/2012 10:26 AM 1791 a.xml
Not sure I would normally use these in a script without documentation but definitely makes you more efficient at the command prompt. Although one scenario where you might want to use type definitions is with Read-Host.
When you use Read-Host it writes a string which can be problematic if you aren't paying attention.
PS C:\> $r = read-host "Enter a number"
Enter a number: 7
PS C:\> $r*3
777
PowerShell failed to read your mind. Instead do this:
PS C:\> [int]$r = read-host "Enter a number"
Enter a number: 7
PS C:\> $r*3
21
Or you might want to prompt for something and have it be the correct type.
PS C:\> [datetime]$cutoff = Read-Host "Enter a cutoff date"
Enter a cutoff date: 1/1/2013
PS C:\> $oldfiles = dir c:\work -file -Recurse | where {$_.lastmodifiedtime -le $cutoff}
There are a number of type accelerators you can use in PowerShell which are great time savers. Of course I didn't cover all of them. What are some of the ones you find the most valuable? Next time we'll look at other ways of working with object types.
About the Author
Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.