<p>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 structure:</p>

<pre><code>Contact
{
    string First
    string Last
    string Phone
}
</code></pre>

<p>How would I go about creating this so that I could use it in function like the following:</p>

<pre><code>function PrintContact
{
    param( [Contact]$contact )
    "Customer Name is " + $contact.First + " " + $contact.Last
    "Customer Phone is " + $contact.Phone 
}
</code></pre>

<p>Is something like this possible, or even recommended in PowerShell?</p>

[{'answer_id': 59887, 'author': 'David Mohundro', 'author_id': 4570, 'author_profile': 'https://Stackoverflow.com/users/4570', 'pm_score': 2, 'selected': False, 'text': '<p>There is the concept of PSObject and Add-Member that you could use.</p>\n\n<pre><code>$contact = New-Object PSObject\n\n$contact | Add-Member -memberType NoteProperty -name "First" -value "John"\n$contact | Add-Member -memberType NoteProperty -name "Last" -value "Doe"\n$contact | Add-Member -memberType NoteProperty -name "Phone" -value "123-4567"\n</code></pre>\n\n<p>This outputs like:</p>\n\n<pre><code>[8] » $contact\n\nFirst                                       Last                                       Phone\n-----                                       ----                                       -----\nJohn                                        Doe                                        123-4567\n</code></pre>\n\n<p>The other alternative (that I\'m aware of) is to define a type in C#/VB.NET and load that assembly into PowerShell for use directly.</p>\n\n<p>This behavior is definitely encouraged because it allows other scripts or sections of your script work with an actual object.</p>\n'}, {'answer_id': 59980, 'author': 'Steven Murawski', 'author_id': 1233, 'author_profile': 'https://Stackoverflow.com/users/1233', 'pm_score': 6, 'selected': False, 'text': '<p>Creating custom types can be done in PowerShell.<br>\nKirk Munro actually has two great posts that detail the process thoroughly.</p>\n\n<ul>\n<li><a href="http://poshoholic.com/2008/07/03/essential-powershell-name-your-custom-object-types/" rel="noreferrer">Naming Custom Objects</a></li>\n<li><a href="http://poshoholic.com/2008/07/05/essential-powershell-define-default-properties-for-custom-objects/" rel="noreferrer">Defining Default Properties for Custom Objects</a></li>\n</ul>\n\n<p>The book <a href="http://www.manning.com/payette/" rel="noreferrer">Windows PowerShell In Action by Manning</a> 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.</p>\n\n<p>If you are just looking for a quick way to do the above, you could create a function to create the custom object like</p>\n\n<pre><code>function New-Person()\n{\n  param ($FirstName, $LastName, $Phone)\n\n  $person = new-object PSObject\n\n  $person | add-member -type NoteProperty -Name First -Value $FirstName\n  $person | add-member -type NoteProperty -Name Last -Value $LastName\n  $person | add-member -type NoteProperty -Name Phone -Value $Phone\n\n  return $person\n}\n</code></pre>\n'}, {'answer_id': 61315, 'author': 'EBGreen', 'author_id': 1358, 'author_profile': 'https://Stackoverflow.com/users/1358', 'pm_score': 4, 'selected': False, 'text': '<p>This is the shortcut method:</p>\n\n<pre><code>$myPerson = "" | Select-Object First,Last,Phone\n</code></pre>\n'}, {'answer_id': 66693, 'author': 'Jaykul', 'author_id': 8718, 'author_profile': 'https://Stackoverflow.com/users/8718', 'pm_score': 8, 'selected': True, 'text': '<h2>Prior to PowerShell 3</h2>\n\n<p>PowerShell\'s Extensible Type System didn\'t originally let you create concrete types you can test against the way you did in your parameter. If you don\'t need that test, you\'re fine with any of the other methods mentioned above. </p>\n\n<p>If you want an actual type that you can cast to or type-check with, as in your example script ... it <strong>cannot</strong> be done without writing it in C# or VB.net and compiling. In PowerShell 2, you can use the "Add-Type" command to do it quite simmple:</p>\n\n<pre><code>add-type @"\npublic struct contact {\n   public string First;\n   public string Last;\n   public string Phone;\n}\n"@\n</code></pre>\n\n<p><strong><em>Historical Note</em></strong>: In PowerShell 1 it was even harder. You had to manually use CodeDom, there is a very old function <a href="http://poshcode.org/scripts/190" rel="noreferrer">new-struct</a> script on PoshCode.org which will help. Your example becomes:</p>\n\n<pre><code>New-Struct Contact @{\n    First=[string];\n    Last=[string];\n    Phone=[string];\n}\n</code></pre>\n\n<p>Using <code>Add-Type</code> or <code>New-Struct</code> will let you actually test the class in your <code>param([Contact]$contact)</code> and make new ones using <code>$contact = new-object Contact</code> and so on...</p>\n\n<h1>In PowerShell 3</h1>\n\n<p>If you don\'t need a "real" class that you can cast to, you don\'t have to use the Add-Member way that <a href="https://stackoverflow.com/a/59980/8718">Steven and others have demonstrated</a> above.</p>\n\n<p>Since PowerShell 2 you could use the -Property parameter for New-Object:</p>\n\n<pre><code>$Contact = New-Object PSObject -Property @{ First=""; Last=""; Phone="" }\n</code></pre>\n\n<p>And in PowerShell 3, we got the ability to use the <code>PSCustomObject</code> accelerator to add a TypeName:</p>\n\n<pre><code>[PSCustomObject]@{\n    PSTypeName = "Contact"\n    First = $First\n    Last = $Last\n    Phone = $Phone\n}\n</code></pre>\n\n<p>You\'re still only getting a single object, so you should make a <code>New-Contact</code> function to make sure that every object comes out the same, but you can now easily verify a parameter "is" one of those type by decorating a parameter with the <code>PSTypeName</code> attribute:</p>\n\n<pre><code>function PrintContact\n{\n    param( [PSTypeName("Contact")]$contact )\n    "Customer Name is " + $contact.First + " " + $contact.Last\n    "Customer Phone is " + $contact.Phone \n}\n</code></pre>\n\n<h1>In PowerShell 5</h1>\n\n<p>In PowerShell 5 everything changes, and we finally got <code>class</code> and <code>enum</code> as language keywords for defining types (there\'s no <code>struct</code> but that\'s ok):</p>\n\n<pre><code>class Contact\n{\n    # Optionally, add attributes to prevent invalid values\n    [ValidateNotNullOrEmpty()][string]$First\n    [ValidateNotNullOrEmpty()][string]$Last\n    [ValidateNotNullOrEmpty()][string]$Phone\n\n    # optionally, have a constructor to \n    # force properties to be set:\n    Contact($First, $Last, $Phone) {\n       $this.First = $First\n       $this.Last = $Last\n       $this.Phone = $Phone\n    }\n}\n</code></pre>\n\n<p>We also got a new way to create objects without using <code>New-Object</code>: <code>[Contact]::new()</code> --  in fact, if you kept your class simple and don\'t define a constructor, you can create objects by casting a hashtable (although without a constructor, there would be no way to enforce that all properties must be set):</p>\n\n<pre><code>class Contact\n{\n    # Optionally, add attributes to prevent invalid values\n    [ValidateNotNullOrEmpty()][string]$First\n    [ValidateNotNullOrEmpty()][string]$Last\n    [ValidateNotNullOrEmpty()][string]$Phone\n}\n\n$C = [Contact]@{\n   First = "Joel"\n   Last = "Bennett"\n}\n</code></pre>\n'}, {'answer_id': 4667545, 'author': 'Nick Meldrum', 'author_id': 32739, 'author_profile': 'https://Stackoverflow.com/users/32739', 'pm_score': 3, 'selected': False, 'text': "<p>Steven Murawski's answer is great, however I like the shorter (or rather just the neater select-object instead of using add-member syntax):</p>\n\n<pre><code>function New-Person() {\n  param ($FirstName, $LastName, $Phone)\n\n  $person = new-object PSObject | select-object First, Last, Phone\n\n  $person.First = $FirstName\n  $person.Last = $LastName\n  $person.Phone = $Phone\n\n  return $person\n}\n</code></pre>\n"}, {'answer_id': 27492180, 'author': 'Florian JUDITH', 'author_id': 4363832, 'author_profile': 'https://Stackoverflow.com/users/4363832', 'pm_score': 2, 'selected': False, 'text': '<p>Here is the hard path to create custom types and store them in a collection.</p>\n\n<pre><code>$Collection = @()\n\n$Object = New-Object -TypeName PSObject\n$Object.PsObject.TypeNames.Add(\'MyCustomType.Contact.Detail\')\nAdd-Member -InputObject $Object -memberType NoteProperty -name "First" -value "John"\nAdd-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe"\nAdd-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "123-4567"\n$Collection += $Object\n\n$Object = New-Object -TypeName PSObject\n$Object.PsObject.TypeNames.Add(\'MyCustomType.Contact.Detail\')\nAdd-Member -InputObject $Object -memberType NoteProperty -name "First" -value "Jeanne"\nAdd-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe"\nAdd-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "765-4321"\n$Collection += $Object\n\nWrite-Ouput -InputObject $Collection\n</code></pre>\n'}, {'answer_id': 32506564, 'author': 'Benjamin Hubbard', 'author_id': 2562189, 'author_profile': 'https://Stackoverflow.com/users/2562189', 'pm_score': 3, 'selected': False, 'text': '<p>Surprised no one mentioned this simple option (vs 3 or later) for creating custom objects:</p>\n\n<pre><code>[PSCustomObject]@{\n    First = $First\n    Last = $Last\n    Phone = $Phone\n}\n</code></pre>\n\n<p>The type will be PSCustomObject, not an actual custom type though. But it is probably the easiest way to create a custom object.</p>\n'}, {'answer_id': 57481684, 'author': 'JohnLBevan', 'author_id': 361842, 'author_profile': 'https://Stackoverflow.com/users/361842', 'pm_score': 1, 'selected': False, 'text': '<p>Here\'s one more option, which uses a similar idea to the PSTypeName solution mentioned by <a href="https://stackoverflow.com/a/66693/361842">Jaykul</a> (and thus also requires PSv3 or above).</p>\n\n<h1>Example</h1>\n\n<ol>\n<li>Create a <strong><em>TypeName</em>.Types.ps1xml</strong> file defining your type.  E.g. <code>Person.Types.ps1xml</code>:</li>\n</ol>\n\n<pre class="lang-xml prettyprint-override"><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt;\n&lt;Types&gt;\n  &lt;Type&gt;\n    &lt;Name&gt;StackOverflow.Example.Person&lt;/Name&gt;\n    &lt;Members&gt;\n      &lt;ScriptMethod&gt;\n        &lt;Name&gt;Initialize&lt;/Name&gt;\n        &lt;Script&gt;\n            Param (\n                [Parameter(Mandatory = $true)]\n                [string]$GivenName\n                ,\n                [Parameter(Mandatory = $true)]\n                [string]$Surname\n            )\n            $this | Add-Member -MemberType \'NoteProperty\' -Name \'GivenName\' -Value $GivenName\n            $this | Add-Member -MemberType \'NoteProperty\' -Name \'Surname\' -Value $Surname\n        &lt;/Script&gt;\n      &lt;/ScriptMethod&gt;\n      &lt;ScriptMethod&gt;\n        &lt;Name&gt;SetGivenName&lt;/Name&gt;\n        &lt;Script&gt;\n            Param (\n                [Parameter(Mandatory = $true)]\n                [string]$GivenName\n            )\n            $this | Add-Member -MemberType \'NoteProperty\' -Name \'GivenName\' -Value $GivenName -Force\n        &lt;/Script&gt;\n      &lt;/ScriptMethod&gt;\n      &lt;ScriptProperty&gt;\n        &lt;Name&gt;FullName&lt;/Name&gt;\n        &lt;GetScriptBlock&gt;\'{0} {1}\' -f $this.GivenName, $this.Surname&lt;/GetScriptBlock&gt;\n      &lt;/ScriptProperty&gt;\n      &lt;!-- include properties under here if we don\'t want them to be visible by default\n      &lt;MemberSet&gt;\n        &lt;Name&gt;PSStandardMembers&lt;/Name&gt;\n        &lt;Members&gt;\n        &lt;/Members&gt;\n      &lt;/MemberSet&gt;\n      --&gt;\n    &lt;/Members&gt;\n  &lt;/Type&gt;\n&lt;/Types&gt;\n</code></pre>\n\n<ol start="2">\n<li>Import your type: <code>Update-TypeData -AppendPath .\\Person.Types.ps1xml</code></li>\n<li>Create an object of your custom type: <code>$p = [PSCustomType]@{PSTypeName=\'StackOverflow.Example.Person\'}</code></li>\n<li>Initialise your type using the script method you defined in the XML: <code>$p.Initialize(\'Anne\', \'Droid\')</code></li>\n<li>Look at it; you\'ll see all properties defined: <code>$p | Format-Table -AutoSize</code></li>\n<li>Type calling a mutator to update a property\'s value: <code>$p.SetGivenName(\'Dan\')</code></li>\n<li>Look at it again to see the updated value: <code>$p | Format-Table -AutoSize</code></li>\n</ol>\n\n<h1>Explanation</h1>\n\n<ul>\n<li>The PS1XML file allows you to define custom properties on types.</li>\n<li>It is not restricted to .net types as the documentation implies; so you can put what you like in \'/Types/Type/Name\' any object created with a matching \'PSTypeName\' will inherit the members defined for this type.</li>\n<li>Members added through <code>PS1XML</code> or <code>Add-Member</code> are restricted to <code>NoteProperty</code>, <code>AliasProperty</code>, <code>ScriptProperty</code>, <code>CodeProperty</code>, <code>ScriptMethod</code>, and <code>CodeMethod</code> (or <code>PropertySet</code>/<code>MemberSet</code>; though those are subject to the same restrictions).  All of these properties are read only.</li>\n<li>By defining a <code>ScriptMethod</code> we can cheat the above restriction.  E.g. We can define a method (e.g. <code>Initialize</code>) 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.</li>\n<li>We can use this same trick to allow the properties to be updatable (albeit via method rather than direct assignment), as shown in the example\'s <code>SetGivenName</code>.</li>\n</ul>\n\n<p>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 <code>FullName</code> property in the PS1XML, then use a function to create the object with the required values, like so:</p>\n\n<h1>More Info</h1>\n\n<p>Take a look at the <a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_types.ps1xml?view=powershell-6" rel="nofollow noreferrer">documentation</a>, or the OOTB type file <code>Get-Content \n$PSHome\\types.ps1xml</code> for inspiration.</p>\n\n<pre><code># have something like this defined in my script so we only try to import the definition once.\n# the surrounding if statement may be useful if we\'re dot sourcing the script in an existing \n# session / running in ISE / something like that\nif (!(Get-TypeData \'StackOverflow.Example.Person\')) {\n    Update-TypeData \'.\\Person.Types.ps1xml\'\n}\n\n# have a function to create my objects with all required parameters\n# creating them from the hash table means they\'re PROPERties; i.e. updatable without calling a \n# setter method (note: recall I said above that in this scenario I\'d remove their definition \n# from the PS1XML)\nfunction New-SOPerson {\n    [CmdletBinding()]\n    [OutputType(\'StackOverflow.Example.Person\')]\n    Param (\n        [Parameter(Mandatory)]\n        [string]$GivenName\n        ,\n        [Parameter(Mandatory)]\n        [string]$Surname\n    )\n    ([PSCustomObject][Ordered]@{\n        PSTypeName = \'StackOverflow.Example.Person\'\n        GivenName = $GivenName\n        Surname = $Surname\n    })\n}\n\n# then use my new function to generate the new object\n$p = New-SOPerson -GivenName \'Simon\' -Surname \'Borg\'\n\n# and thanks to the type magic... FullName exists :)\nWrite-Information "$($p.FullName) was created successfully!" -InformationAction Continue\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/59819', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/4916/']