Sunday, March 24, 2013

Why Every SysAdmin Should Learn to Write a Script

I'm a bit overdue for a perceptual conceptual brain-twisting deep-dive article, and caffeine seems to always push me in that direction, so it was time.  And besides, I've been torquing my brain to the limits working a huge article series for 4Sysops on Troubleshooting Configuration Manager clients.  On top of that I'm in the final throws of my next book, and no shortage of "real" work at the office to contend with, and trying to maintain sanity with four kids still at home, and well, it'll be worth the wait, but I may need a stiff drink when it's wrapped up.

So, without further adieu...


Why the Title?

There's no shortage of articles, blogs and whatnot that tout the "benefits of scripting and coding" for anyone who spends time on a computer.  Okay, maybe they're mostly aimed at those who earn a paycheck spending time on a computer, but that's okay too.  Most of the articles I've read however seem to focus on the benefits of automating common tasks, as well as extremely difficult or complex tasks.  That's fine also, but there's more to it than that.  Much more.  To me they're kind of missing the main point:

Writing a script teaches you a particular way of thinking.

Sure, automating tasks pays pretty well, but there's more to be gained by an everyday computer technician from learning to write even the most basic script. One of the main reasons, indeed THE reason, that I chose the title for this article has nothing to do with automating tasks at all. Let us drink the Kool Aid of digression and digress with me for a bit, mmkay?...

A Different Kind of Thinking

Writing software code, whether it's to be compiled or interpreted, involves a process of thought.  Granted, there are some variations of the thought process as it pertains to a particular programming language, as well writing functional, procedural or object-oriented code, but the process itself; the process of telling the computer to perform certain actions, remains.  It's a logic process.

If This is True: Do That  /  Otherwise: Do something else...

Disregarding semantics for a moment, the flow of logic is roughly the same whether you work with C++, JavaScript, LISP, Python, Ruby or whatever.  Picture the following example scenario:

Task: Instruct a Robot to retrieve a box of cereal from a cabinet on the other side of the room.

That sounds easy, right? "Hey, robot.  Get me the box of cereal."

But the robot doesn't move.  It needs to be told:
  • where to go
  • how to get there
  • what exactly to retrieve
  • how to actually retreive it
  • where exactly to go next
  • how to go to the next location
  • when to perform each step
The only basic questions it doesn't absolutely need to ask are the who and why aspects. For now, anyway.  Let's ignore the possibility that it's a super-advanced robot that understands these things. Let's instead go with a robot that was just born and has to be taught like a baby human would.  These steps come with a few qualifying pieces as well:
  • where to go: relative to current location
  • how to get there: walk, roll, crawl / turns, ascentions (climbs),descentions, and distances
  • what exactly to retrieve: which object(s), means for identifying or locating
  • how to actually retreive it: grasp, slide, push, pull, tip, scoop / carry, store, clutch
  • where exactly to go next: relative to current location
  • how to go to the next location: walk, roll, crawl / turns, ascentions (climbs),descentions, and distances
  • when to perform each step: now/immediate, x minutes/seconds from now/from previous step

It should make you pause for a moment and begin breaking down the sequence of tasks in detail:
  1. Turn [n] degrees to your [right/left]
  2. Move forward approximately [x] feet and stop
  3. Turn [n] degrees to your [right/left]
  4. Move forward approximately [x] feet and stop
  5. Raise arm upward to [x] degrees from default position
  6. Extend arm forward [x] inches
  7. Grasp cabinet handle
  8. Pull grasped handle, swing arc to [left/right] until door is opened [x] degrees from starting position
  9. Release handle
  10. ...
You get the idea.  Those of you that have actually programmed machines like this already know that I'm glossing over a lot of detailed steps in between.  This is actually the basis for how a programming language is developed: step by step, nouns and verbs. Then come the adjectives (gently, loud, etc.), until the language is matured enough to accomplish the general class of tasks it was intended to address.
Side Note: When asked "what is the best programming language?" the answer is: "There is no such thing as a 'best programming language'".  Every language is better at some particular tasks than others.  A programming language is a tool.  A tool is a means for accomplishing one or more tasks. Each tool is conceived and built to address a particular set of tasks.   
Contextually, some languages are "better" to master with regards to job opportunities or specific projects.  From a technical point of view, the concept of "better" depends on specific comparisons of individual features as they align with a set of specific tasks which are to be accomplished.  The more general or multi-purpose capability a particular tool becomes, the less specialized it becomes at the same time. 
That was a lot of mumbo-jumbo, sheesh.
You may start letting your mind wander into additional challenges, like navigating stairs, odd angles, and so on.  Hopefully another thought crossed your mind: How much we, as human beings, take for granted this little stuff.  Imagine what someone fighting through physical therapy has to deal with as they learn to walk or speak again following a major injury.

In most respects, this way of thinking forces you to shoehorn your intentions into the mouth of the beast we call a computer. The point is that when you're forced to structure your thinking in someone else's terms, it also forces you to think more clearly about what it is you're trying accomplish.

In many respects, it forces you into a serialized or sequential mode, even if your goal requires solving parallel processes.  You have a Start and a Finish.  A beginning and an end.  Initialization and termination. An Alpha and an Omega (for you religious folks, a little humor, pardon me). Regardless of having to contend with seemingly random or asynchronous sub-tasks, the overall context has (or should have) a defined start and end.  If you're looking to run a program that just goes off and does a bunch of unrelated things; does a lot of unrelated and undefined things, and never ends, that's been done already: It's called politics.

Let's put this into somewhat of a "real world scenario" for a  moment. A

Example: Finding all Files in a Folder that Contain a Particular Text Value or Numeric Pattern

This is a very common task indeed.  Whether it's finding documents that contain a reference to a particular contract number, or data files that contain a numeric pattern, or sales records with a particular date, log files that include a specific name or code, the logic for pursuing the answer is pretty much the same.  What differs is the trivial stuff like formats, locations, and the periphery like environment and concurrency limitations.  I say "trivial" in the sense that those are relatively less important than the primary goal being sought.  I'm fully aware that in many cases those so-called "trivial" extraneous things can become the biggest headache to deal with.  Okay, enough blabbering from me.  Let's dissect this creature...

What are we trying to accomplish here?
  1. We need to identify what we are looking for: ____
  2. We need to identify where to look for it: ____
  3. We need to identify what to do when we find it: ____
There are millions of other things we can do, and may need to do, in addition to these basic questions, but in most cases these are the three most important questions to answer.  You could boil them down into two primary categories: Target and Action.  What is the target and What action do we take on it (or because of it)?

For this example, we'll keep it simple and go with the following parameters:

Find all instances of the text value "CNX-10114025" within all of the documents contained in the Folders located on server FPS0025 under the shared folder root of "Contracts".  The target UNC path then becomes "\\FPS0025\Contracts".  Maybe a quick inspection of the folder structure reveals that there are nested folders which will need to be scanned, as well finding that there are an assortment of file formats to be found:

  • Microsoft Word, Excel, of various versions (.doc, .xls, .docx, .xlsx, etc.)
  • ASCII Text (.txt)
  • XML files
  • HTML source files
  • PowerShell scripts (.ps1)
You probably stared at that list and immediately began to think through the process for opening, reading, and parsing each file type.  You're already staring at the trees, let's get back to the forest.

Here's what you should have thought:
  1. Access the root folder
  2. Look for files in the root folder
  3. Check each sub-folder
  4. Look for files in each sub-folder
  5. Check for the next level of sub-folders
  6. Look for files in each sub-folder
  7. Wash. Rinse. Repeat
An average programmer or scripter (script-writer? script-kiddie? I can never figure out what the proper term is anymore) would get to step 4 and immediately blurt out "Recursion, bitches!" and then quiet down after realizing they just upset everyone else in the library.  I happen to be sitting at a table in our "central" public library actually, so that's why I made that connection.  I haven't blurted out anything yet though.

Forget recursion for the moment; stay focused on the forest.  The trees will come later.  And, yes, I know that the preceding sentence is completely illogical, let's keep moving along...

Are you seeing the process yet?  If you're following along with my mindless rambling, you're probably nodding in confirmation of understanding that you have to think it through in an orderly fashion.  The key here is "orderly" (i.e. sequential.  serialized.  linear).

Side Note: Yes, I know that there are non-linear, amorphous, parallel and even polymorphic/non-linear/parallel problems to be solved, but this is Dave 101 and that course isn't until next semester (if ever).  Context is everything.  The context here is: how does writing a script benefit a systems administrator who's never considered writing a script before now.

Pseudo-Code to the Rescue

Most programmers will have likely put there head into both hands and moaned at reading that line above.  The prevailing view of pseudo-code is that it's for beginners.  Noobs.  Training-wheels.  And in some respects that's true, but it works. No reasonable programmer or scripter can argue that writing pseudo-code is detrimental to anything besides (A) their ego and (B) their time.  I've seen it used by some of the best and brightest programmers when conditions were sufficiently warranted.  What did that mean?  I mean that when faced with an incredibly complex task to accomplish, pseudo-code can help map out the primary steps and it can bring clarity to the major components of the impending code to be written.

What is Pseudo-Code Anyway?

Rather than explain it, I'll just show it:  First, here's a chunk of actual program code...

If Not amx_InstallRegKeyExists ( regKey3 ) Then
   dPrint "info", 0, "AMX is not installed. Proceeding with install sequence."
   If Not amx_PreReqsFound ( arrReqIDs ) Then
      dPrint "info", 0, "Installing AMX prerequisite components..."
      p1 = amx_InstallMissingPrereqs ( arrReqIDs, False, vbMinimized )
   Else
      p1 = True
      If amx_InstallSequence ( arrAppIDs, False, vbMinimized ) = True Then
         p2 = amx_InitRebootSequence()
      Else
         dPrint "error", Err.Number, Err.Description
      End If
   End If
Else
   dPrint "info", 0, "AMX is already installed. No action required."
End If

Now, let's write that in pseudo-code:
  1. If application "AMX" is NOT already installed (then...)
    1. install prerequisite software items
    2. install AMX application
    3. check that everything installed properly
    4. reboot the computer
  2. Otherwise (else...)
    1. nothing to do, exit cleanly
That's it.  Pure and simple. Now, the question you may ask is "Hey, dumbass: where do I buy a book on this 'pseudo-code' programming language?".  You don't.  You can't.  There's no such official programming language by that name.  It's a term used to describe the writing of program instructions in human-readable terms. It's like a "faux diamond", "pleather jacket", or an "honest politician". It's "fake". The word "pseudo" means "not actually, but having the appearance of...", or fake, so you get the picture.

Master or Commander?

Am I saying that you need to spend a lot of time mastering the art of writing scripts?  No.  I'm only suggesting that learning how to write even one script successfully will help you in the long run.  It's sort of like walking a mile in the computer's shoes.  I'm not going to trick you and deny that writing scripts isn't both addictive and lucrative.  The more you write the more tasks you'll think of to use them on.

Pointers

Some of the sites I look to for help and inspiration when working on new scripts (if I don't have code laying around from twenty years of tinkering with it):

1 comment:

Joseph said...

Great explanation of why to code!

Next week, I am hosting a Lunch and Learn where we are covering why learn powershell 3. The number one question for my co-workers, "why do I need to learn this when I can do it in the GUI"