I would like to be able to define and use a custom type in some of my PowerShell scripts. For example, let\'s pretend I had a need for an object that had the following struc
Creating custom types can be done in PowerShell.
Kirk Munro actually has two great posts that detail the process thoroughly.
The book Windows PowerShell In Action by Manning also has a code sample for creating a domain specific language to create custom types. The book is excellent all around, so I really recommend it.
If you are just looking for a quick way to do the above, you could create a function to create the custom object like
function New-Person()
{
param ($FirstName, $LastName, $Phone)
$person = new-object PSObject
$person | add-member -type NoteProperty -Name First -Value $FirstName
$person | add-member -type NoteProperty -Name Last -Value $LastName
$person | add-member -type NoteProperty -Name Phone -Value $Phone
return $person
}
Here's one more option, which uses a similar idea to the PSTypeName solution mentioned by Jaykul (and thus also requires PSv3 or above).
Person.Types.ps1xml
:<?xml version="1.0" encoding="utf-8" ?>
<Types>
<Type>
<Name>StackOverflow.Example.Person</Name>
<Members>
<ScriptMethod>
<Name>Initialize</Name>
<Script>
Param (
[Parameter(Mandatory = $true)]
[string]$GivenName
,
[Parameter(Mandatory = $true)]
[string]$Surname
)
$this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName
$this | Add-Member -MemberType 'NoteProperty' -Name 'Surname' -Value $Surname
</Script>
</ScriptMethod>
<ScriptMethod>
<Name>SetGivenName</Name>
<Script>
Param (
[Parameter(Mandatory = $true)]
[string]$GivenName
)
$this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName -Force
</Script>
</ScriptMethod>
<ScriptProperty>
<Name>FullName</Name>
<GetScriptBlock>'{0} {1}' -f $this.GivenName, $this.Surname</GetScriptBlock>
</ScriptProperty>
<!-- include properties under here if we don't want them to be visible by default
<MemberSet>
<Name>PSStandardMembers</Name>
<Members>
</Members>
</MemberSet>
-->
</Members>
</Type>
</Types>
Update-TypeData -AppendPath .\Person.Types.ps1xml
$p = [PSCustomType]@{PSTypeName='StackOverflow.Example.Person'}
$p.Initialize('Anne', 'Droid')
$p | Format-Table -AutoSize
$p.SetGivenName('Dan')
$p | Format-Table -AutoSize
PS1XML
or Add-Member
are restricted to NoteProperty
, AliasProperty
, ScriptProperty
, CodeProperty
, ScriptMethod
, and CodeMethod
(or PropertySet
/MemberSet
; though those are subject to the same restrictions). All of these properties are read only.ScriptMethod
we can cheat the above restriction. E.g. We can define a method (e.g. Initialize
) which creates new properties, setting their values for us; thus ensuring our object has all the properties we need for our other scripts to work.SetGivenName
.This approach isn't ideal for all scenarios; but is useful for adding class-like behaviors to custom types / can be used in conjunction with other methods mentioned in the other answers. E.g. in the real world I'd probably only define the FullName
property in the PS1XML, then use a function to create the object with the required values, like so:
Take a look at the documentation, or the OOTB type file Get-Content
$PSHome\types.ps1xml
for inspiration.
# have something like this defined in my script so we only try to import the definition once.
# the surrounding if statement may be useful if we're dot sourcing the script in an existing
# session / running in ISE / something like that
if (!(Get-TypeData 'StackOverflow.Example.Person')) {
Update-TypeData '.\Person.Types.ps1xml'
}
# have a function to create my objects with all required parameters
# creating them from the hash table means they're PROPERties; i.e. updatable without calling a
# setter method (note: recall I said above that in this scenario I'd remove their definition
# from the PS1XML)
function New-SOPerson {
[CmdletBinding()]
[OutputType('StackOverflow.Example.Person')]
Param (
[Parameter(Mandatory)]
[string]$GivenName
,
[Parameter(Mandatory)]
[string]$Surname
)
([PSCustomObject][Ordered]@{
PSTypeName = 'StackOverflow.Example.Person'
GivenName = $GivenName
Surname = $Surname
})
}
# then use my new function to generate the new object
$p = New-SOPerson -GivenName 'Simon' -Surname 'Borg'
# and thanks to the type magic... FullName exists :)
Write-Information "$($p.FullName) was created successfully!" -InformationAction Continue