{"id":4883,"date":"2012-10-02T00:01:00","date_gmt":"2012-10-02T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2012\/10\/02\/build-your-own-powershell-cmdlet-part-4-of-9\/"},"modified":"2012-10-02T00:01:00","modified_gmt":"2012-10-02T00:01:00","slug":"build-your-own-powershell-cmdlet-part-4-of-9","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/build-your-own-powershell-cmdlet-part-4-of-9\/","title":{"rendered":"Build Your Own PowerShell Cmdlet: Part 4 of 9"},"content":{"rendered":"<p><b>Summary<\/b>: Microsoft Windows PowerShell MVP, Sean Kearney, continues a series of guest blogs that detail how to build your own cmdlet.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Guest blogger and Windows PowerShell MVP, Sean Kearney, has written a series about building cmdlets. For more about Sean, see <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/sean+kearney\/\" target=\"_blank\">his previous guest blog posts<\/a>.<\/p>\n<p style=\"padding-left: 30px\"><b>Note<\/b> This is Part 4 of a nine-part series about building your own Windows PowerShell cmdlet. Read the <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/windows+powershell\/guest+blogger\/sean+kearney\/build+your+own+cmdlet\/\">entire series<\/a> as it unfolds.<\/p>\n<p>Here&rsquo;s Sean&hellip;<\/p>\n<h2>Polishing the advanced function<\/h2>\n<p>So now what we have is an advanced function that sure feels like a cmdlet, doesn&rsquo;t it? It almost is, but it needs a lot more polishing. Here are the steps we still need to complete.<\/p>\n<ul>\n<li>\n<p>Change parameters to accept data as an array<\/p>\n<\/li>\n<li>\n<p>Add cmdlet binding to emulate compiled cmdlet<\/p>\n<\/li>\n<li>\n<p>Set properties on parameters and possible validation<\/p>\n<\/li>\n<li>\n<p>Insert <b>Begin<\/b>, <b>Process<\/b>, and <b>End<\/b> scriptblocks<\/p>\n<\/li>\n<li>\n<p>Add Help content<\/p>\n<\/li>\n<\/ul>\n<h2>Change those parameters to an array<\/h2>\n<p>When you&rsquo;re working with a cmdlet, you&rsquo;re working with the pipeline. Within the pipeline you may receive one object or a large array of them. In both cases you are dealing with an array of information, even if that array only contains one element.<\/p>\n<p>To adjust the parameters as an array, we only need insert a pair of brackets into the variable. This identifies it as an array instead of as a single object.<\/p>\n<p>In the case of our three parameters, they will switch from looking like this&hellip;<\/p>\n<p style=\"padding-left: 30px\">function global:ADD-LOGFILE{<\/p>\n<p style=\"padding-left: 30px\">PARAM(<\/p>\n<p style=\"padding-left: 30px\">[STRING]$Folder=&#8221;C:\\PowerShell&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING]$Preface=&#8221;Logfile&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING]$Extension=&#8221;.log&#8221;<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<p>&hellip;to the following, which allows them to receive data as an array instead of as single parameters.<\/p>\n<p style=\"padding-left: 30px\">function global:ADD-LOGFILE{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PARAM(<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Folder=&#8221;C:\\PowerShell&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Preface=&#8221;Logfile&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Extension=&#8221;.log&#8221;<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<h2>Enabling cmdlet binding and building on parameters<\/h2>\n<p>Cmdlet binding is enabled by adding a new piece before the Param block for your parameters. Cmdlet binding defines how your cmdlet and its parameters will work in the Windows PowerShell world. To enable the cmdlet binding, we simply make this change to our function.<\/p>\n<p style=\"padding-left: 30px\">function global:ADD-LOGFILE{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding()]<\/p>\n<p style=\"padding-left: 30px\">PARAM(<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Folder=&#8221;C:\\PowerShell&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Preface=&#8221;Logfile&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Extension=&#8221;.log&#8221;<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<p>Cmdlet binding will bind the parameters in the same fashion as compiled cmdlets. This means parameters that by default are available to all cmdlets are now available to your cmdlet.<\/p>\n<p>With the cmdlet binding, you can also specify additional parameters, such as:<\/p>\n<ul>\n<li>\n<p>DefaultParameterSetName<\/p>\n<\/li>\n<li>\n<p>SupportsShouldProcess<\/p>\n<\/li>\n<li>\n<p>ConfirmImpact<\/p>\n<\/li>\n<\/ul>\n<h3>DefaultParameterSetName<i> <\/i><\/h3>\n<p>This is what it implies to be. It states which of your parameters should be assumed to be the default. For example, within <b>Add-LogFile<\/b>, we might want to have the folder name as the default parameter. To specify that we would like the folder to be the default parameter for our <b>Add-LogFile<\/b> cmdlet, we would make this change to the top of our advanced function.<\/p>\n<p style=\"padding-left: 30px\">function global:ADD-LOGFILE{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding(<\/p>\n<p style=\"padding-left: 30px\">DefaultParameterSetName=&rdquo;Folder&rdquo;<\/p>\n<p style=\"padding-left: 30px\">)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PARAM(<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Folder=&#8221;C:\\PowerShell&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Preface=&#8221;Logfile&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Extension=&#8221;.log&#8221;<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<h3>SupportsShouldProcess<\/h3>\n<p>This will allow you to leverage the <b>WhatIf<\/b> parameter. This is my favorite parameter. With this enabled, I can create a cmdlet that has the option to &ldquo;say without doing&rdquo; for the user.<\/p>\n<p>Typically, &ldquo;destructive cmdlets&rdquo; (those that are intended to remove and cause actions that would be difficult to impossible to undo, such as removing a user in Active Directory) will support this. You can leverage this feature however you would like. It can even be used as part of the troubleshooting for your cmdlet.<\/p>\n<p>We could enable this as part of our cmdlet by adding <b>SupportsShouldProcess<\/b> to <b>CmdletBinding<\/b>.<\/p>\n<p style=\"padding-left: 30px\">function global:ADDLOGFILE{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding(<\/p>\n<p style=\"padding-left: 30px\">DefaultParameterSetName=&rdquo;Folder&rdquo;,<\/p>\n<p style=\"padding-left: 30px\">SupportsShouldProcess=$True<\/p>\n<p style=\"padding-left: 30px\">)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PARAM(<\/p>\n<p style=\"padding-left: 30px\">[STRING]$Folder=&#8221;C:\\PowerShell&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING]$Preface=&#8221;Logfile&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING]$Extension=&#8221;.log&#8221;<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<p>To be able to leverage the <b>SupportsShouldProcess<\/b> parameter, you need to use the following block of script.<\/p>\n<p style=\"padding-left: 30px\">If ($PSCmdlet.ShouldProcess(&#8220;<i>Message<\/i>&#8220;)) { <i>BlockofCode<\/i> }<\/p>\n<p>If the <b>WhatIf<\/b> parameter is supplied with the cmdlet, the contents of &ldquo;Message&rdquo; will be displayed, which should indicate what the code block would do. Otherwise, all the script within the parentheses will execute.<\/p>\n<p>If we were to modify <b>Add-LogFile<\/b> to use <b>SupportsShouldProcess<\/b> with our line to create the log file, it would look like this:<\/p>\n<p style=\"padding-left: 30px\">If ($PSCmdlet.ShouldProcess(&#8220;Creation of Logfile $Logfilename Successful&#8221;)) { NEW-ITEM &ndash;Type File -path $Logfilename -Force | OUT-NULL }<\/p>\n<h3>Enable whatif<\/h3>\n<p>Now with the feature enabled, we can leverage the <b>WhatIf<\/b> parameter.<\/p>\n<p style=\"padding-left: 30px\">ADD-LOGFILE C:\\NewFolder -whatif<\/p>\n<p>The output to the <b>Add-LogFile<\/b> cmdlet that we are creating would now look like this with a controlling <b>WhatIf<\/b> parameter added.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5444.hsg-10-2-12-1.png\"><img decoding=\"async\" title=\"Image of command output\" alt=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5444.hsg-10-2-12-1.png\" \/><\/a><\/p>\n<h3>ConfirmImpact<i> <\/i><\/h3>\n<p>This enables your cmdlet to use the <b>&ndash;Confirm<\/b> parameter. This allows you to employ the ability to confirm before actions occur. The script that is required to leverage this feature is identical to <b>SupportsShouldProcess<\/b>.<\/p>\n<p><b>ConfirmImpact<\/b> is set to one of three values: Low, Medium, or High. It will leverage the value of the <b>$ConfirmPreference<\/b> variable. It will launch a confirmation before the action if it is equal to or greater than the variable or if the <b>&ndash;Confirm<\/b> parameter is supplied.<\/p>\n<p>Our same cmdlet using the <b>&ndash;Confirm<\/b> parameter would look like this if we added this feature.<\/p>\n<p style=\"padding-left: 30px\">function global:ADDLOGFILE{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding(<\/p>\n<p style=\"padding-left: 30px\">DefaultParameterSetName=&rdquo;Folder&rdquo;,<\/p>\n<p style=\"padding-left: 30px\">SupportsShouldProcess=$True,<\/p>\n<p style=\"padding-left: 30px\">ConfirmImpact=&rsquo;High&rsquo;<\/p>\n<p style=\"padding-left: 30px\">)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PARAM(<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Folder=&#8221;C:\\PowerShell&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Preface=&#8221;Logfile&#8221;,<\/p>\n<p style=\"padding-left: 30px\">[STRING[]]$Extension=&#8221;.log&#8221;<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<p>Running <b>Add-LogFile &ndash;Confirm<\/b> with the same changes in the script would look like this.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4034.hsg-10-2-12-2.png\"><img decoding=\"async\" title=\"Image of command output\" alt=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4034.hsg-10-2-12-2.png\" \/><\/a><\/p>\n<p>Let&rsquo;s save the script as addlogcmdlet.ps1 with our change of <b>[CmdletBinding()]<\/b> placed into it. We&rsquo;ll run the new script and then execute <b>Get-Help<\/b> on the <b>Add-LogFile <\/b>cmdlet. See if you can catch the difference.<\/p>\n<p>Here it was before the change:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0726.hsg-10-2-12-3.png\"><img decoding=\"async\" title=\"Image of command output\" alt=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0726.hsg-10-2-12-3.png\" \/><\/a><\/p>\n<p>And now after <b>[CmdletBinding()]<\/b> was added:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3377.hsg-10-2-12-4.png\"><img decoding=\"async\" title=\"Image of command output\" alt=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3377.hsg-10-2-12-4.png\" \/><\/a><\/p>\n<p>The difference is that now the common parameters are available. These are parameters that are available to all cmdlets. The common parameters are:<\/p>\n<ul>\n<li>\n<p>-Verbose<\/p>\n<\/li>\n<li>\n<p>-Debug<\/p>\n<\/li>\n<li>\n<p>-WarningAction<\/p>\n<\/li>\n<li>\n<p>-WarningVariable<\/p>\n<\/li>\n<li>\n<p>-ErrorAction<\/p>\n<\/li>\n<li>\n<p>-ErrorVariable<\/p>\n<\/li>\n<li>\n<p>-OutVariable<\/p>\n<\/li>\n<li>\n<p>-OutBuffer<\/p>\n<\/li>\n<\/ul>\n<p>All of these options can be leveraged by your cmdlet, depending on how you would like to control the output. In all cases, if your cmdlet receives the parameter but has no script to recognize what it is meant for, it will do nothing.<\/p>\n<p>With the <b>Verbose<\/b> parameter you can set a Boolean True or False as the property. When this parameter is active, data sent by the <b>Write-Verbose<\/b> cmdlet is viewable.<\/p>\n<p>With the <b>Debug<\/b> parameter, you can set a Boolean True or False as the property. When this parameter is active, data sent by the <b>Write-Debug<\/b> cmdlet is viewable.<\/p>\n<p>With the <b>WarningAction<\/b> parameter, you can override the settings contained within <b>$WarningAction<\/b>. When you send data with the <b>Write-Warning<\/b> cmdlet, you have the choice to be prompted for an action.<\/p>\n<p>The <b>WarningVariable<\/b> allows you to store the data sent by Write-Warning into a variable for later retrieval.<\/p>\n<p>With the <b>ErrorAction<\/b> parameter, you can override the settings contained within <b>$ErrorAction<\/b>. The actions of this parameter are similar to <b>WarningAction<\/b> except it is for the <b>Write-Error<\/b> cmdlet. When you send data with the <b>Write-Error<\/b> cmdlet, you can be prompted for an action. Again, the choice is yours.<\/p>\n<p>Like its cousin, the <b>ErrorVariable<\/b> parameter allows you to store errors within a variable.<\/p>\n<p>~Sean<\/p>\n<p>Thanks Sean! This series is turning out great. I appreciate you hanging in there. Good stuff. Guest Blogger Week will continue tomorrow when Sean will talk more about building a cmdlet.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft Windows PowerShell MVP, Sean Kearney, continues a series of guest blogs that detail how to build your own cmdlet. Microsoft Scripting Guy, Ed Wilson, is here. Guest blogger and Windows PowerShell MVP, Sean Kearney, has written a series about building cmdlets. For more about Sean, see his previous guest blog posts. Note This [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[372,56,3,154,45],"class_list":["post-4883","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-build-your-own-cmdlet","tag-guest-blogger","tag-scripting-guy","tag-sean-kearney","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft Windows PowerShell MVP, Sean Kearney, continues a series of guest blogs that detail how to build your own cmdlet. Microsoft Scripting Guy, Ed Wilson, is here. Guest blogger and Windows PowerShell MVP, Sean Kearney, has written a series about building cmdlets. For more about Sean, see his previous guest blog posts. Note This [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4883","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=4883"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4883\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=4883"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=4883"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=4883"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}