Accommodate or Dictate Your Scripts
I’ve been thinking a lot about this lately. What, you ask? How to approach writing a script or advanced function. Most of the time I’ve always in the accommodate world, where I try to imagine every scenario that might happen and account for that in my script. But is this the right approach? Should I instead simply dictate that this is how you use the script? A lot of people do it (VMware requires Connect-VIServer before you do anything). Should I start doing the same?
It’s pretty intuitive that a script that anticipates everything you may do would be pretty easy to use, right? It makes for a lot of work, and the scripts can get pretty complicated, but UX is high? Isn’t it? I’m not so sure anymore. They work in a lot of different ways, but sometimes that confuses people even more. “How do I use it? Anyway you want.” Not a really satisfying answer.
The alternative is to write the script in such a fashion that they must be run a certain way This certainly limits choices for the user but a script that uses object output can avoid a lot of this by simply letting the user decide what to do with their data and not worrying about making sure they do it right. This second approach both appeals and appalls me. It appeals to my dominant, nit-picky side because I can make sure you do things correctly (at least my interpretation of correct). But it appalls me in the way that I have no idea how people might want to use this script, and who the hell am I? But lately I’ve been finding myself leaning this way because it simplifies my code and frankly embraces some basic tenants of PowerShell. The idea that a script does one thing, one thing only and if you want to do more you pass it down the pipeline to the next script, which in turn does one thing and so on.
This recently applied to a module I’ve been working on at athena. The SQL.Automation module has several functions in it all relating to SQL. The primary function, and if you know me you know this, is Invoke-SQLQuery. But there are several other functions that all work with SQL Availability Groups, Get-AGCluster, Get-AGDatabaseState and Get-AGReplicaState–there’s also a Set-AGClusterFailover but it’s not 100% yet (so close!). The three “Get” functions all retrieve different information from the Availability Group and when I originally wrote them they all gathered all of the AG information they needed independently. This worked, of course, but required a lot of work on my part. So I started working on “version 2” that simply leveraged Get-AGCluster (which did everything I needed anyway) and this is where I started to hit me. I had all kinds of code in there to determine if I needed Get-AGCluster, why not just require the use of Get-AGCluster? Setup pipeline and all of the other Get-* commands would work great with Get-AGCluster information coming down the pipe. And it really worked, what was going to be a long rewrite process suddenly went very quickly, since I had all the information I needed from the Get-AGCluster the other scripts essentially became loops with SQL queries retrieving the specific information that they needed to.
I think going forward I will probably go this route. I’m also beginning to think I should publish the SQL.Automation module.
I’m really curious what you think: Accommodate or Dictate in your Scripts? Comment and chime in your thoughts.