Skip navigation

Tag Archives: microsoft

Now, it’s time to evaluate PowerShell.  As of late, PowerShell has become my preferred method for scripting.  PowerShell is built on top of the .NET framework.  It uses cmd-lets (commandlets) to perform the majority of it’s command line actions.  It’s also fully integrated with WMI and the .NET Framework.  It works off of Objects, which we discussed previously with VBScript.  It shares a lot of similarities to the BASH Shell, but the object based pipe is one of the more powerful features.  Objects in the pipe remain objects through to output.  This is an important element to remember if you are already familiar to the BASH shell and find yourself working in a Windows environment now (such as myself).

There’s a lot of good reading over what PowerShell is, so I’ll go ahead and start with our code and break it down as we’ve done in the past.  We’ll be introducing CASE statements this time around, as well as arguments.  I hope you enjoy it.

 Function Get-PowerShell {

The purpose of this script is to start, stop, or restart services on a single machine or list of machines read from a file.


#Service State Changer Script
#Author: Daniel Belcher

#Syntax:
#    .\set-remoteservice.ps1 stop spooler laptopname

PARAM([string]$STATE,[string]$SERVICE,[string]$TARGET)
$ErrorActionPreference = "silentlycontinue"
Function Set-RemoteService{
SWITCH($STATE){
        "Start"
            {$SET.Start()
                Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Starting`n"}
       "Stop"
            {$SET.Stop()
                Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Stopping`n"}
        "Restart"
            {$SET.Stop()
        Start-Sleep -Seconds 1
             $SET.Start()
                 Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Restarting`n"}
             }

if ($STATE -ne "Start"){if  ($STATE -ne "Stop"){if ($STATE -ne "Restart"){
    Write-Output = "Invalid service state entered`n";Break}}}

                         }
if ($SERVICE -eq "")
    {$SERVICE = Read-Host "Please enter a valid service name"}
    if ($STATE -eq "")
        {$STATE = Read-Host "You need a valid state entry. Start, Stop, or Restart"}
        if ($TARGET -eq "")
            {$TARGET = $ENV:COMPUTERNAME}       
    $FILECHECK = Test-Path $TARGET
        if ($FILECHECK -eq $TRUE){
            $READ = gc $TARGET
                ForEach ($ITEM in $READ)     {
            $PING = gwmi -Query "select * from Win32_PingStatus where Address=’$ITEM’"
    if ($PING.StatusCode -ne 0)
        {Write-Output “$ITEM not found online, or invalid hostname.`n"}
    else
        {$SET = (Get-Service $SERVICE -ComputerName $ITEM)
                        Set-RemoteService
                    Start-Sleep -Seconds 1   
 
$STATUS = (Get-Service $SERVICE.ToUpper() -ComputerName $ITEM.ToUpper()).Status
         Write-Output = "Machine: $ITEM`nService: $SERVICE`nStatus: $STATUS`n"
}
                                }
                            }   
        else
            {$PING = gwmi -Query "select * from Win32_PingStatus where Address=’$TARGET’"
    if ($PING.StatusCode -ne 0)
        {Write-Output "$TARGET not found online, or invalid hostname.";
                            break}
            $SET = (Get-Service $SERVICE -ComputerName $TARGET)
                        Set-RemoteService
                Start-Sleep -Seconds 1  

$STATUS = (Get-Service $SERVICE.ToUpper() -ComputerName $TARGET.ToUpper()).Status
        Write-Output = "Machine: $TARGET`nService: $SERVICE`nStatus: $STATUS`n"
}


Copy and paste the above code and save it as a .ps1 which is the standard file extension for PowerShell scripts.  Now, lets set our execution settings from the PowerShell command line:

Set-ExecutionPolicy unrestricted

You can now execute the script by right-clicking and run with PowerShell, or by launching it from within the shell by navigating to it’s directory and typing it’s name.

Now lets look at our interest areas:

  1. Sequence
  2. Command usage
  3. Output usage
  4. Conditional logic

Sequence:

Sequence inside PowerShell is as expected.  It’s all run inline, and requires everything called to be defined before it’s call.  Looking at the above code you will see that we call Set-RemoteService two times, and we’ve defined it at the beginning of the script.  In VBScript, or in Command, calling a sub, function, or a GOTO statement will find the appropriate section and run that code.  So being aware of how your scripts process instructions is very important to their functionality.

Now for something new, arguments.  Arguments are, more or less, are user defined variables.  They are dynamic, and as such, need to be controlled to some degree.  So a fair amount of the script sequence is spent insuring that the variables passed are useable.  With PowerShell you are capable of defining the variable type.  This gives you incredible control and removes one more potential for unforeseen errors.

PARAM([string]$STATE,[string]$SERVICE,[string]$TARGET)

As you can see, with the PARAM statement we are opening 3 variables for input.  They are $STATE, $SERVICE, $TARGET.  We are also insuring whatever is entered is a string value.  So that leaves us with 2 issues.  The user needs to enter something, and it needs to be useable.  We will discuss this more in our conditional logic.

Command usage:

As we discussed at the beginning, PowerShell uses cmd-lets for the majority of it’s work.  It also allows you to call WMI and .NET methods and functions.  So similarly to to VBScript we can instantiate them for additional functionality.  The possibilities are plain astounding, and well beyond scope for this blog post.

Output usage:

Same as with our previous entries, all the work done is aimed at getting an appropriate output.  As such, capturing, reading, and modifying the output of all our commands is important.  How PowerShell does this, is similar to both VBScript and BASH.  You can pipe for output as input for other commands as well allowing for some very elegant one liner solutions.

Since PowerShell keeps everything in the pipe an object, your variables become immensely powerful since they are more than just stored returns from commands, but instead stored objects with invoke able methods, or property lookup, etc.  Lets evaluate how this works quickly:

$SET = (Get-Service $SERVICE -ComputerName $TARGET)

Here we are using a Get-Service Cmd-Let to grab a specific service, on a specific machine.  Now the variable service we are grabbing will have certain native METHODS and PROPERTIES we can leverage to either invoke or pull data.  What we need to understand is that the $SET variable is now an Object, not a Value.

$SET.Start()

Start and Stop are two methods belonging to Win32_Service objects.  So what we are doing here is saying essentially:

$SERVICE object, Invoke Method Start

So everything you see at the above link is now callable, or retrievable through this variable.  Pretty cool huh?  One thing of note, if you invoke a method that changes the state of an object, it’s properties will not be refreshed until it is called again.  You will notice in the code I declare a $STATUS variable.

$STATUS = (Get-Service $SERVICE.ToUpper() -ComputerName $TARGET.ToUpper()).Status

Because the state is changed, the property Status is changed.  Hence I couldn’t just recycle $SET with the $SET.Status.  The object needs to be refreshed. (Don’t be confused by the .ToUpper(), these are just native PowerShell methods for string management.  In this case UpperCase)

Now the $STATUS variable could just be considered a value.  Since it is in fact a capture of the property value.  Starting to make better sense now?

I don’t really utilize any direct error handling inside this script outside of the $erroractionpreference value.

$ErrorActionPreference = "silentlycontinue"

This just means if I hit an error, move on to the next statement, and do so without any visible output to the user.

Conditional logic:

We’ve discussed if statements before, and as promised, I offer you a practical case statement:

SWITCH($STATE){
"Start"
{$SET.Start()
Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Starting"}
"Stop"
{$SET.Stop()
Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Stopping"}
"Restart"
{$SET.Stop()
Start-Sleep -Seconds 1
$SET.Start()
Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Restarting"}
}

The syntax says switch, but the methodology is the same.  All that is happening is we are saying if $STATE = “Start” or “Stop” or “Restart” do this.  Either 3 separate if statements, or 1 case statement.  Fairly efficient manner for handling complex conditions that hinge around one variable.

So what does this PowerShell script do?

It begins by declaring 3 variables that are to be string values.

Then it tells the shell to remain silent and continue on errors.

Next it builds a function to be reused through the remainder of the script

Function Set-RemoteService{

This is where our case statement is built and stored.  We also have an additional conditional statement in there to insure that one of the 3 pre-defined strings were typed correctly.  Now we perform an if statement to determine that the variables were defined at execution, if not then we prompt the user for input.  If $TARGET is empty we simply define it as $ENV:COMPUTERNAME, which is equivalent to LOCALHOST (just a bit more elegant).

Now it performs a quick check to determine if the $TARGET passed was a file or simply a name.  If it was a file, the file is opened and assigned to the $READ variable where a for each statement is run against it.

A $PING test is done using a WQL to determine if the machine is online, or if the name is even valid.  (the reason we do this, is that it’s increasingly faster to ping for response than to wait for a timeout to occur on a RPC request).

If the machine successfully pings it moves on to declare the $SET variable object for use in the Set-RemoteService function.  Then it executes the function, then a 1 second pause, and then builds an object property variable that is used in a console message to the user notifying them the state of the service after the action.

If it wasn’t a file, it does the same thing without a ForEach statement.

}


So you want a little more?

I’m going to quickly show you how you can integrate this script, into a profile function.  It’s exceptionally easy, and I recommend it for scripts you’ve built that you find invaluable for day to day use.

Here goes the modified code:

Function Set-RemoteService {
PARAM([string]$STATE,[string]$SERVICE,[string]$TARGET)
$ErrorActionPreference = "silentlycontinue"
Function SRS{
SWITCH($STATE){
                       "Start"
                           {$SET.Start()
                                  Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Starting`n"}
                       "Stop"
                           {$SET.Stop()
                                  Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Stopping`n"}
                       "Restart"
                           {$SET.Stop()
                     Start-Sleep -Seconds 1
                            $SET.Start()
                                  Write-Output "`nService: $($SERVICE.ToUpper()) `nStatus: Restarting`n"}
                           }

if ($STATE -ne "Start"){if ($STATE -ne "Stop"){if ($STATE -ne "Restart"){
      Write-Output = "Invalid service state entered`n";Break}}}

                          }
if ($SERVICE -eq "")
    {$SERVICE = Read-Host "Please enter a valid service name"}
    if ($STATE -eq "")
        {$STATE = Read-Host "You need a valid state entry. Start, Stop, or Restart"}
       if ($TARGET -eq "")
           {$TARGET = $ENV:COMPUTERNAME
      $FILECHECK = Test-Path $TARGET
             if ($FILECHECK -eq $TRUE){
                 $READ = gc $TARGET
                           ForEach ($ITEM in $READ) {
           $PING = gwmi -Query "select * from Win32_PingStatus where Address=’$ITEM’"
    if ($PING.StatusCode -ne 0)
       {Write-Output “$ITEM not found online, or invalid hostname.`n"}
   else
       {$SET = (Get-Service $SERVICE -ComputerName $ITEM)
                              SRS
                          Start-Sleep -Seconds 1
$STATUS = (Get-Service $SERVICE.ToUpper() -ComputerName $ITEM.ToUpper()).Status
             Write-Output = "Machine: $ITEM`nService: $SERVICE`nStatus: $STATUS`n"
}
                                                       }
                                               }
             else
                   {$PING = gwmi -Query "select * from Win32_PingStatus where Address=’$TARGET’"
         if ($PING.StatusCode -ne 0)
              {Write-Output "$TARGET not found online, or invalid hostname.";
                                                  break}
                    $SET = (Get-Service $SERVICE -ComputerName $TARGET)
                                   SRS
                        Start-Sleep -Seconds 1

$STATUS = (Get-Service $SERVICE.ToUpper() -ComputerName $TARGET.ToUpper()).Status
             Write-Output = "Machine: $TARGET`nService: $SERVICE`nStatus: $STATUS`n"
}

}

We’ve enclosed the entire thing into a Function statement now, and redefined the internal function.  The reason for this was, well, I prefer to stick with cmd-let naming patterns.  I’d rather call set-remoteservice at the command line.

Now, we need to build our profile, from inside PowerShell, at the command line type:

notepad.exe $PROFILE

Now paste the above code and save it.  If you are inside ISE press F5 to run the script and update your profile, or if you are in the standard shell type:

powershell

This will start a new shell instance inside your previous shell with the updated profile.

Be mindful, PowerShell and PowerShell ISE have two separate profiles.  So if you perform this in ISE the standard shell won’t receive the update and vice versa.


That does it for my stick to the script series.  I hope you’ve learned a lot regarding scripts through this and it’s shed some light onto what scripting is and de-mystified it for you.

I will continue to post scripts for various things over time, as well as neat tricks as I learn them so I wouldn’t say this is truly the end. 

Editors:

If you intend to do a lot of scripting, I recommend you grab yourself a steady editor.  Preferably one with configurable highlighters.  Below are a list of a few free and premium editors I recommend.  I’ll start with the freebies:

  • Context – A nice, windows based, free editor (my usual windows editor). Configurable highlighters, comparison tool, and configurable execution keys.  All the basic needs of a script writer.  Minus integrated libraries.  I believe development has all but stopped since 09, so this might not be the best editor to stick to.
  • Emacs – Extremely powerful development environment built for OSS, but with Windows versions.  This is more than a simple editor, it’s a Swiss army knife.  It will require a bit of a learning curve to begin using it but is worth it in the end.
  • VIM – My preferred editor in Linux, switchable modes, highlighters, line numbers, split views, compares, command line style editing.  Like emacs, it’s extremely powerful, and has a bit of a learning curve.  I prefer VIM, but Emacs is far more extensible, just fyi.
  • NotePad++ – A really nice OSS editor for Windows.  I should probably start using this over Context, but I’ve already spent so much time with Context configuring it, I’ve become extremely comfortable with it.  I’d recommend this editor to any novice windows scripter.
  • PowerGUI – Both a free, and paid edition.  I recommend this editor for PowerShell whole heartedly because of it’s syntax and object recognition tools.  If you are going to do a lot of work with PowerShell, I recommend using this editor for it.
  • PSEdit – Included with the PowerShell ISE.  It’s the standard PowerShell editor.

Premium:

If you are interested in learning more:

Stick to the Script Parts 1 – 4:

  1. Stick to the Script…
  2. Stick to the script #!/BIN/BASH…
  3. Stick to the script CreateObject(“Wscript.Shell”)…
  4. Stick to the Script PS C:\> PowerShell …

Advertisements