Naming and Designing Advanced PowerShell Functions

ScriptingGuy1


Summary
: Microsoft Scripting Guy Ed Wilson discusses naming and designing advanced Windows PowerShell functions in relation to his Local User Management Module. Microsoft Scripting Guy Ed Wilson here. The cool thing about today is that it is Canada Day. This means that my boss back in Redmond is off today. Of course, Monday is July 4th, and the Scripting Wife and I will be off celebrating. The cool thing about this dual country celebration is that we effectively get a four-day weekend. In fact, the Scripting Wife and I will be heading to Canada in October for three weeks. We will be in Ottawa, Quebec City, and in Montreal. I am working with my friend, Georges Maheu, on a new workshop, and we will be conducting Train the Trainer sessions up there. I am also hoping to meet up with at least one users group while I am north of the border. It will be an awful lot of fun, and if we are lucky, maybe we can score some poutine while we are in Quebec City. In yesterday’s post, I talked a little bit about writing the Local User Management Module and how I used parameter sets to simplify some of the code syntax. On the page for the Local User Management Module in the Scripting Guys Script Repository, a couple of questions have already appeared. The first one is, “Does it work on domain users and groups?” To be honest I did not test that scenario. I do know that the WinNT provider (which I used in the module) can be used to make some modifications to Active Directory. I have always advocated that newbies stay away from that approach as it leads to serious confusion early on. After scripters have learned the difference between using the WinNT provider and the LDAP provider, there are some nice shortcuts that can be used with the WinNT provider that will simplify a few scenarios. The reason I called my module the Local User Management Module was specifically because I wanted to fill the hole that currently exists with local user accounts. For domain accounts, you should use either the Quest AD cmdlets or the Microsoft Windows 2008 R2 AD cmdlets (there is also the 2008 Gateway). This having been said, there are some additional scenarios I will examine for the Local User Management Module, and I can guarantee there will be a V2 release soon. There may even be a V3. Send email to me at scripter@microsoft.com, and let me know what you would like to see. In response to a comment on yesterday’s Hey, Scripting Guy! Blog post, I am thinking about incorporating security into the mixture. There was an additional question about why I created a function named Set-LocalUser instead of creating two functions–one called Enable-LocalUser and another called Disable-LocalUser. The first part of the discussion needs to take a look at the verb. In fact, the entire discussion revolves around the verb. The first thing I do when deciding a verb name is to use the Get-Verb cmdlet. When I use that cmdlet, I am able to see which verbs are allowed. The listing appears here, along with the command I used to obtain the results.

PS C:Usersed.WOODGROVE> get-verb | sort verb

Verb                 Group

——                 ——-

Add                  Common

Approve           Lifecycle

Assert              Lifecycle

Backup             Data

Block                Security

Checkpoint       Data

Clear                Common

Close                Common

Compare          Data

Complete         Lifecycle

Compress        Data

Confirm            Lifecycle

Connect           Communications

Convert            Data

ConvertFrom    Data

ConvertTo        Data

Copy                Common

Debug              Diagnostic

Deny                Lifecycle

Disable             Lifecycle

Disconnect       Communications

Dismount         Data

Edit                  Data

Enable              Lifecycle

Enter                Common

Exit                  Common

Expand             Data

Export              Data

Find                  Common

Format             Common

Get                   Common

Grant                Security

Group               Data

Hide                 Common

Import              Data

Initialize           Data

Install               Lifecycle

Invoke               Lifecycle

Join                  Common

Limit                Data

Lock                 Common

Measure           Diagnostic

Merge              Data

Mount              Data

Move                Common

New                 Common

Open                Common

Out                   Data

Ping                  Diagnostic

Pop                  Common

Protect              Security

Publish             Data

Push                 Common

Read                Communications

Receive            Communications

Redo                Common

Register           Lifecycle

Remove           Common

Rename           Common

Repair              Diagnostic

Request           Lifecycle

Reset               Common

Resolve            Diagnostic

Restart              Lifecycle

Restore            Data

Resume           Lifecycle

Revoke             Security

Save                 Data

Search             Common

Select              Common

Send                Communications

Set                   Common

Show                Common

Skip                  Common

Split                 Common

Start                 Lifecycle

Step                 Common

Stop                 Lifecycle

Submit             Lifecycle

Suspend          Lifecycle

Switch              Common

Sync                 Data

Test                 Diagnostic

Trace                Diagnostic

Unblock            Security

Undo                Common

Uninstall           Lifecycle

Unlock              Common

Unprotect        Security

Unpublish         Data

Unregister        Lifecycle

Update               Data

Use                  Other

Wait                 Lifecycle

Watch              Common

Write                Communications   As you can see from the above list, there are Enable and Disable verbs, so why did I not use them if they are available? For one thing, they are listed as lifecycle verbs. See MSDN for a great article that talks about some of the nuances between the different verbs. If Enable and Disable are lifecycle verbs, I need to see the other lifecycle verbs so that I can get a feel for how they behave (unfortunately, there is no help for the Get-Verb cmdlet to describe what the categories actually mean). Here is the command I used to get all the lifecycle verbs; the output from the commands also appears.

PS C:Usersed.WOODGROVE> get-verb | where { $_.group -eq ‘lifecycle’}

Verb                 Group

——                 ——-

Approve           Lifecycle

Assert              Lifecycle

Complete         Lifecycle

Confirm            Lifecycle

Deny                Lifecycle

Disable             Lifecycle

Enable              Lifecycle

Install               Lifecycle

Invoke                Lifecycle

Register           Lifecycle

Request           Lifecycle

Restart              Lifecycle

Resume           Lifecycle

Start                 Lifecycle

Stop                 Lifecycle

Submit             Lifecycle

Suspend          Lifecycle

Uninstall           Lifecycle

Unregister        Lifecycle

Wait                 Lifecycle   The output from the lifecycle verbs did not help too much. The next thing I did was see how the Enable and Disable verbs are used in Windows PowerShell. To do that, I used the Get-Command cmdlet with the verb parameter. Because the verb parameter will accept an array, I can obtain the output I seek in a single command. The command and returned information appear here.

PS C:Usersed.WOODGROVE> Get-Command -Verb enable, disable | select name

Name

———————

Disable-ComputerRestore

Disable-PSBreakpoint

Disable-PSRemoting

Disable-PSSessionConfiguration

Disable-WSManCredSSP

Enable-ComputerRestore

Enable-PSBreakpoint

Enable-PSRemoting

Enable-PSSessionConfiguration

Enable-WSManCredSSP As I looked over the listing of cmdlets, I saw things like Enable/Disable PSRemoting and Enable/Disable ComputerRestore. I then decided to look at cmdlets with a noun of Service. Here is the command and results.

PS C:Usersed.WOODGROVE> Get-Command -Noun service | select name

Name

————-

Get-Service

New-Service

Restart-Service

Resume-Service

Set-Service

Start-Service

Stop-Service

Suspend-Service In the above listing, you will see that there is no Enable/Disable service cmdets. They do have Set-Service. To disable or enable a service, I use Set-Service. This is because I am not actually disabling the service, but am instead changing a property of the service to Disabled. I will admit it causes confusion, and I have known a lot of people who resort to using WMI to Enable/Disable a service because they do not realize they can do it via the Set-Service cmdlet. The next thing to look at is what is my Set-LocalUser cmdlet actually doing? When I disable the user, it does not actually disable the user; rather, it sets a userflag property to the ADS_USER_FLAG_ENUM value of 2. It also sets the description property of the user at the same time. To enable a user, the Set-LocalUser function performs three actions: it sets the password, the userflags, and the description. Because of this, I decided that it was more than a simple Enable/Disable action that was being performed. The Set-LocalGroup function was a bit trickier. Here, I am simply calling either the add or the remove method, and from our listing of approved verbs, Add and Remove are on the list. But what should I call the function? Add-Group, Remove-Group? Of course not! If I am creating a group or deleting a group, I use Add-Group/Remove-Group (or more specifically in my case Add-LocalGroup and Remove-LocalGroup). The next thing I thought about was using the names Add-ToLocalGroup and Remove-FromLocalGroup. In the end, it seemed that Set-LocalGroup was the right decision because I am actually setting the group membership (regardless of the actual methods in use in the code). The other advantage is that I avoided having to create two functions; I was able to use only one function to perform both the add and the remove methods to the group membership. Now, let’s go back and look at the Set-LocalUser. I could have used Enable and Disable, and I actually did spend a lot of time thinking about the name. As you can see from the above discussion, one problem with Set-Service is that people do not think about using it to enable or to disable a service. According to the verbs, I guess I can use either Enable/Disable or Set, and still be following the guidelines. There is one other guideline that I probably violated and that is the principle of least shock, which is also an issue with Set-Service. If everyone who uses the software expects something to be called Enable-LocalUser, discoverability becomes an issue. Again, the overriding factor for me was wanting to incorporate the functionality, follow good Windows PowerShell naming conventions, and get the thing written in time for yesterday’s Hey, Scripting Guy! Blog post to be edited and published. You see, when it comes right down to it, to ship is to choose. Well, this concludes another edition of the Hey, Scripting Guy! Blog. Join me tomorrow when I will show you how to use the Local User Management Module . 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  

0 comments

Discussion is closed.

Feedback usabilla icon