Showing posts with label scheduling. Show all posts
Showing posts with label scheduling. Show all posts

Sunday, November 11, 2012

Crude But Effective: Part 2, the Electric Boogaloo

In my previous article I described a system for replicating some of the functionality of the ConfigMgr Right-Click Tools (aka "SCCM Right-Click Tools"), through a web interface (intranet web portal application) using a combination of HTML, ASP, and a database back-end   What I planned to do was provide a little more detail of each of the pieces in follow-on articles.  This way, if you really cared enough, you could build your own setup (and probably do a better job of it than I have).

In this article I'm going to expand on the part of the process which involves the database back-end  and the script that runs on a schedule to query, process and update the database table.

The Database Table

To bring all of the processing into one central "hub", I chose to use a Microsoft SQL Server database, and create a table to capture the incoming requests from the portal.   My database server is named "DB1" and is running on SQL Server 2012, but it doesn't matter what version you use really.  I've tested this setup on 2005, 2008 and 2008 R2 with equal results.  The name of my database is "AMS" (for Asset Management Services), but you can call it whatever you want, just modify the names below to suit your needs.  The table I created is named "ClientToolsLog", but again, that's not required, so you could name it "DogPoo" and it won't matter.

USE [AMS]

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[ClientToolsLog](
 [ID] [int] IDENTITY(1,1) NOT NULL,
 [ActionName] [varchar](50) NOT NULL,
 [Network] [varchar](50) NOT NULL,
 [Comment] [varchar](255) NULL,
 [AddedBy] [varchar](50) NOT NULL,
 [DateAdded] [smalldatetime] NOT NULL,
 [DateProcessed] [smalldatetime] NULL,
 [ResultData] [varchar] (50) NULL,
)
GO

GRANT SELECT, INSERT, UPDATE, DELETE on ClientToolsLog TO amsManager
GO
 
GRANT SELECT on ClientToolsLog TO amsReadOnlyUser
GO

The Table Structure

Each of the columns has a purpose, so I'll explain them each below:
  • ID - This is used to identify the specific row in the table.  Because it's an integer value, and auto-incremented by 1, you don't specify a value for this field when inserting a new row. You only need it if you want to query, modify, or delete a specific row.
  • ActionName - (required) This is where the specific action name is entered.  I use my own abbreviated codenames to save on space (this log can easily grow very quickly with multiple users!).  For example, I use "MACHINE_POLICY" to indicate "Machine Policy Retrieval and Evaluation", and "HWINV" to indicate "Hardware Inventory Cycle", and so on. (see image below for the list of default available actions for ConfigMgr 2012 clients)
  • Network - (required) This is for storing the AD domain name or the CM site name, the choice is yours and it really doesn't matter, but I made it mandatory so you can modify "NOT NULL" to "NULL" if you prefer.  It's just there to enable filtering on specific environments when needed.
  • Comment - (optional) This is for entering a comment if desired. I had initially intended this to be a [textarea] field on the web form, but decided to skip it to avoid unnecessary data.
  • AddedBy - (required) This stores the username of the person who submitted the request from the web site form.  For this to work, you MUST enable "Windows Authentication" in IIS for the web site or the virtual folder.  If you leave it on "Anonymous" there won't be any way to track who the user was unless you build in forms-based authentication (yuck!)
  • DateAdded - (required) This stores the date and time when the request was submitted
  • DateProcessed - This is initially NULL until the script comes along and processes the request, at which time it enters the date and time it was completed.
  • ResultData - This is also initially NULL until the script updates the row when the request has been processed.

Security

I chose SQL accounts for this setup, but you could use mixed-mode.  I do a lot of things by force of habit, so SQL accounts are pretty common for my work, so I tend to use mixed-mode setups.  In any case, I have two user accounts for this system:
  • amsManager - This account has rights to SELECT, INSERT, UPDATE and DELETE data and rows in the table.  I use this account from within the web application to insert new records, and it's used in the script (discussed later) to update the rows when requests are processed.
  • amsReadOnlyUser - This account only has SELECT rights, and is used for any applications/scripts/processes where someone needs to be able to consume (read) the data but not have the ability to modify or delete anything.

The Script

Now that the database is created, the table created and the permissions applied to the table, the next step is getting a script to work with it to do the heavy-lifting.  You can do this with almost any language, including PowerShell, VBscript, KiXtart, Perl, Python or whatever.  As long as the language you choose can do the following things it should work fine:
  • Open a database connection to query (read) and update data in the rows.
  • Execute shell operations to call external .exe applications (SendSchedule.exe), as well as invoke COM interfaces such as WMI and SWBEM requests.
Again, out of habit, I chose VBScript.  I was going to do it with PowerShell, but I got lazy.  Here's the code, but I have to mention that one key "action" is left out for now, and that's the "Re-Run Advertisement" option.  The reason is that I'm still working on this part and having some challenges.  When I get it working reliably and consistently I will post an update:

'****************************************************************
' Filename..: ams_client_tools.vbs
' Author....: David M. Stein
' Date......: 11/11/2012
' Purpose...: invoke ConfigMgr Agent "client actions" on remote clients
'             using a SQL table and WMI invocation
' SQL.......: DB1\AMS
' Comment...: Beware of line-wrapping!  If I wrap it I used [& _]
'****************************************************************
Dim query, conn, cmd, rs, objShell, scriptPath, recID, objFSO

' controls DebugPrint output
Const verbose = True

' database connection
Const dsn = "DRIVER=SQL Server;SERVER=DB1;database=AMS;UID=amsManager;PWD=P@ssw0rd$123;"

' database table name
Const strTable = "dbo.ClientToolsLog"

'------------------------------------------------------------
scriptPath = Replace(wscript.ScriptFullName, "\" & wscript.ScriptName, "")
'------------------------------------------------------------
' constants used by this script (abridged format)
'------------------------------------------------------------
Const adOpenDynamic = 2 Const adOpenStatic = 3 Const adLockReadOnly = 1 Const adLockPessimistic = 2 Const adLockOptimistic = 3 Const adUseServer = 2 Const adUseClient = 3 Const adCmdText = &H0001 Const adStateClosed = &H00000000 Const adStateOpen = &H00000001 Const ForReading = 1 Const ForWriting = 2 Const ForAppend = 8 Const TristateUseDefault = -2 Const TriStateTrue = -1 Const TriStateFalse = 0 '------------------------------------------------------------ DebugPrint "info: begin processing..." Set objShell = CreateObject("Wscript.Shell") query = "SELECT * FROM " & strTable & _ " WHERE DateProcessed IS NULL ORDER BY ID" Set conn = CreateObject("ADODB.Connection") Set cmd = CreateObject("ADODB.Command") Set rs = CreateObject("ADODB.Recordset") On Error Resume Next conn.ConnectionTimeOut = 5 conn.Open dsn If err.Number <> 0 Then wscript.echo "fail: database connection failed" wscript.quit(err.Number) Else On Error GoTo 0 End If rs.CursorLocation = adUseClient rs.CursorType = adOpenStatic rs.LockType = adLockReadOnly Set cmd.ActiveConnection = conn cmd.CommandType = adCmdText cmd.CommandText = query rs.Open cmd If Not(rs.BOF And rs.EOF) Then xrows = rs.RecordCount counter = 0 Do Until rs.EOF recID = rs.Fields("ID").value compName = rs.Fields("ClientName").value actName = rs.Fields("ActionName").value actCode = ClientActionCode(actName) addBy = rs.Fields("AddedBy").value DebugPrint "record id...... " & rs.Fields("ID").value DebugPrint "client name.... " & compName DebugPrint "action name.... " & actName DebugPrint "action code.... " & actCode DebugPrint "requestor...... " & addBy DebugPrint "request date... " & rs.Fields("DateAdded").value DebugPrint "network........ " & rs.Fields("Network").value If IsOnline(compName) Then retval = ExecAction(compName, actName, actCode, addBy) Else DebugPrint "result......... offline!" retval = 100 End If DebugPrint "result......... " & retval MarkRecord recID, retval DebugPrint "-------------------------------------------" rs.MoveNext Loop DebugPrint "info: " & counter & " processed" Else DebugPrint "info: no records found" End If rs.Close conn.Close Set rs = Nothing Set cmd = Nothing Set conn = Nothing '------------------------------------------------------------ ' function: return datestamp formatted for log file use '------------------------------------------------------------ Function LogTime() LogTime = FormatDateTime(Now, vbShortDate) & " " & _ FormatDateTime(Now, vbLongTime) End Function '------------------------------------------------------------ ' function: return TRUE if computer responds to a PING request
' note: this features can be impacted by firewall settings!
'------------------------------------------------------------

Function IsOnline(strComputer)
  Dim objPing, query, objStatus, retval
  If strComputer <> "" Then
    query = "SELECT * FROM Win32_PingStatus WHERE Address='" & strComputer & "'" 
    Set objPing = GetObject("winmgmts:{impersonationLevel=impersonate}")._
      ExecQuery(query)
    For Each objStatus in objPing
      If Not(IsNull(objStatus.StatusCode)) And objStatus.StatusCode = 0 Then
        IsOnline = True
      End If
    Next
  End If
End Function

'------------------------------------------------------------
' function:
'------------------------------------------------------------

Function ClientActionCode(actionName)
  Select Case actionName
    Case "MACHINE_POLICY":
      ' Machine Policy Retrieval and Evaluation Cycle
      ClientActionCode = "{00000000-0000-0000-0000-000000000021}"
    Case "HWINV":
      ' Hardware Inventory Cycle
      ClientActionCode = "{00000000-0000-0000-0000-000000000001}"
    Case "SWINV":
      ' Software Inventory Cycle
      ClientActionCode = "{00000000-0000-0000-0000-000000000002}"
    Case "DISCOVERY":
      ' Discover Data Collection Cycle
      ClientActionCode = "{00000000-0000-0000-0000-000000000003}"
    Case "RERUN_ADV":
      ' Re-Run Advertisement
      ClientActionCode = "RERUNADV"
    Case "INST_SOURCE":
      ' Windows Installer Source List Update Cycle
      ClientActionCode = "{00000000-0000-0000-0000-000000000032}"
    Case "UPDATE_SCAN":
      ' Software Updates Scan Cycle
      ClientActionCode = "{00000000-0000-0000-0000-000000000113}"
    Case "AMT_PROV":
      ' AMT Auto Provisioning Policy / Out-of-Band Mgt Scheduled Event
      ClientActionCode = "{00000000-0000-0000-0000-000000000120}" 
    Case "BRANCH_DP":
      ' Branch Distribution Point Maintenance Task 
      ClientActionCode = "{00000000-0000-0000-0000-000000000062}"
    Case "UPDATE_DEP":
      ' Software Updates Deployment Evaluation Cycle 
      ClientActionCode = "{00000000-0000-0000-0000-000000000108}"
    Case "SW_METERING":
      ' Software Metering Usage Report Cycle 
      ClientActionCode = "{00000000-0000-0000-0000-000000000031}"
    Case "USER_POLICY":
      ClientActionCode = "{00000000-0000-0000-0000-000000000027}"
    Case Else:
      ClientActionCode = ""
  End Select
 
  ' list of codes for future inclusion...
  '
  '{00000000-0000-0000-0000-000000000010}  File Collection
  '{00000000-0000-0000-0000-000000000021}  Request machine assignments
  '{00000000-0000-0000-0000-000000000023}  Refresh default MP
  '{00000000-0000-0000-0000-000000000024}  Refresh location services
  '{00000000-0000-0000-0000-000000000025}  Request timeout value for tasks
  '{00000000-0000-0000-0000-000000000026}  Request user assignments
  '{00000000-0000-0000-0000-000000000032}  Request software update source
  '{00000000-0000-0000-0000-000000000061}  DP: Peer DP status report
  '{00000000-0000-0000-0000-000000000062}  DP: Peer DP pending status check
  '{00000000-0000-0000-0000-000000000111}  Send unset state messages
  '{00000000-0000-0000-0000-000000000112}  Clean state message cache
  '{00000000-0000-0000-0000-000000000114}  Refresh update status
End Function

'--------------------------------------------------------
' function: 
'--------------------------------------------------------

Function ExecAction(clientName, actionName, actionCode, userID)
  Dim strCmd, result

  DebugPrint "info: executing action request for " & clientName

  If actionCode = "RERUNADV" Then
    ' result = RerunAdv(compName, advID)
    ' [[ I will cover this in part 4 of this article ]]
    result = 200 ' denotes request was ignored (for now)
  Else
    strCmd = scriptPath & "\SendSchedule.exe " & actionCode & " " & clientName
    wscript.echo "info: command = " & strCmd
    result = objShell.Run(strCmd, 1, True)
  End If

  '--------------------------------------------------------

  ExecAction = result

End Function

'------------------------------------------------------------
' function: 
'------------------------------------------------------------

Sub MarkRecord(recID, pVal)
  Dim query, conn, cmd, rs

  wscript.echo "info: marking record completed..."

  DebugPrint "info: id = " & recID & " / result = " & pval

  query = "SELECT * FROM " & strTable & " WHERE id=" & recID
 
  Set conn = CreateObject("ADODB.Connection")
  Set cmd  = CreateObject("ADODB.Command")
  Set rs   = CreateObject("ADODB.Recordset")
 
  On Error Resume Next
  conn.ConnectionTimeOut = 5
  conn.Open dsn
  If err.Number <> 0 Then
    wscript.echo "fail: connection failed"
    wscript.quit(err.Number)
  Else
    On Error GoTo 0
  End If
 
  rs.CursorLocation = adUseClient
  rs.CursorType = adOpenDynamic
  rs.LockType = adLockPessimistic
 
  Set cmd.ActiveConnection = conn
 
  cmd.CommandType = adCmdText
  cmd.CommandText = query
  rs.Open cmd
 
  If Not(rs.BOF And rs.EOF) Then
    rs.Fields("DateProcessed").value = Now
    rs.Fields("ResultData").value = pVal
    rs.Update
  Else
    DebugPrint "error: no records found"
  End If
 
  rs.Close
  conn.Close
  Set rs = Nothing
  Set cmd = Nothing
  Set conn = Nothing

End Sub

'------------------------------------------------------------
' function: verbose echo printing
'------------------------------------------------------------

Sub DebugPrint(s)
  If verbose = True Then
    wscript.echo s
  End If
End Sub

What The Script Does

As I mentioned before, each time the Scheduled Task runs, it calls the script.  The script performs the following actions in the order/sequence listed below:
  • Opens a Connection to the database using ADO (COM) with SQL user permissions
  • Submits a Query for all rows where the DateProcessed value is NULL (indicating the request has not been processed yet).  The results are obtained as an ADO RecordSet object.
  • Iterates the RecordSet rows to gets the remote Computer Name, and ActionName field to determine the specific things that need to be done for the requested action (for example: look up the Action Code GUID)
  • Initiates a WMI (Win32_Ping) request to determine if the remote computer is online.
    • If not online, the ResultData column is updated with a value to indicate the client was offline
    • If online, the Action is processed...
  • Executes the requested Action:
    • If a "Client Action" is requested: Open a Shell session using WScript Shell object (COM) and executes the SendSchedule.exe application with the appropriate GUID for the Action and the name of the remote computer.  Gets the result/exit code from the SendSchedule process.
    • If "Re-Run Advertisement" is requested:  (to be continued)
  • Updates the database table row by entering the appropriate result code (ResultData) and the timestamp of the completion (DateProcessed)
  • Exits
Not really complicated actually.  This is a pretty straightforward and common process for interacting with database tables with ADO.  You could separate the requests and the results into two tables if you prefer, but I'm not shooting for 3NF or 4NF here.  I'm too lazy for that much work.

The Scheduled Task

This is where the Security aspect comes into play.  You need to execute the script under a context which has permissions to invoke the Configuration Manager Agent on remote computers over your network from a WMI interface.  I created a special Domain user account for this and added to the local Administrators group on every desktop and laptop computer using Group Policy and Restricted Groups.

Before setting up the Scheduled Task, I highly recommend testing the script directly.  Open a session (interactive login or use RunAs to open a CMD console) under the credentials of the user account you intend to use for the Scheduled Task.  Test the script until you are satisfied it works correctly.

As a force of habit, I use a simple BAT script to wrap my calls to VBScript to I can pipe the output (wscript.echo or DebugPrint results) to a log file if I want.  Or you can do it from within the VBScript code using basic FileSystemObject (FSO) methods if you prefer.  Either way, it can be helpful to generate a log file to diagnose issues where the database is unavailable for some reason when the scheduled task is executed.

The Schedule you choose is entirely arbitrary.  I run mine at ten (10) minute intervals all day, every day.  It also doesn't matter how you choose to create the Scheduled Task.  You can obviously use the GUI, or do it from the command line using SchTasks.exe, or from a script or whatever.

Summary

All of this I've covered here is essentially the "back-end" of the process.  I hope it you find it useful and helpful.  Let me know by posting a comment below?  In the next part of this article I will delve into the web form and the user interaction aspects.

Thursday, December 16, 2010

Systems Management Interfaces

I'm sure I'm missing a bunch of other items, but this is a rough start anyway.  I was thinking of what "things" a system admin deals with most often, as it pertains to local vs remote management of each item.  The two that bother me most are Power Plans and Scheduled Tasks.  I sure hope Microsoft decides to clean those up within WMI.  On an even more grandiose delusion I have: I'd love to see them standardize their command syntax and syntactical structures.  Just compare the SC, SCHTASKS and POWERCFG commands (use /? for each).  Holy crap!   And the first person who chimes with "PowerShell is the standard command structure…": Wrong!  It's a bandaid on an amputated leg.  You don't add tools to fix a problem.  You replace them.  It's called efficiency.  I'm cranky tonight.  Going for a walk in the icy slush, which makes me even crankier because cold weather SUCKS!

  COMMAND REMOTE IMPORT EXPORT WMI (root/) WMIC
Scheduled Tasks SCHTASKS YES YES YES ? JOB
Services SC YES NO NO CIMV2, Win32_Service SERVICE
Registry REG YES YES YES DEFAULT, StdRegProv REGISTRY
Event Logs WEVTUTIL YES YES YES CIMV2, Win32_NTLogEvent NTEVENT
Power Plans POWERCFG NO YES YES CIMV2\Power -
Computer Serial Number - - - - CIMV2, Win32_SystemEnclosure SYSTEMENCLOSURE
Drive Mappings NET USE NO NO NO CIMV2, Win32_LogicalDisk NETUSE
Shared Folders NET SHARE YES NO NO CIMV2, Win32_Share SHARE

Thursday, December 9, 2010

Old Topic / New Thoughts: Special User Accounts

You have scheduled tasks and services running around like 3-legged blind cats with collars made of catnip.  They've been drinking Red Bull all day also.  Not good.  Now one of the suits walks in with a folded up magazine about "Cutting Edge IT" he lifted from the seat pocket on his red-eye flight from Vegas.  He says: "You boyz, listen up! I read that you best be a-changin them there passwords on them there service accounts.  Y'all hear?!"  And after a short pause with no sound, you all start back to work as if he never entered the room.

Ok, I needed a mental break.  On to something more serious:

You changed a password and now shit is breaking.  Or even more tedious is that the account keeps getting locked because something out there in the depths of space is still trying to use it and getting the gas-face from AD.  You could start the manhunt for where the problems are coming from.  You can query AD for which DC handled the offending request that locked the account.  You can query all computers in the Forest or Domain for scheduled tasks, services, DSNs, auto-runs, and so on.  Fear not there are three easy "options" to mitigate this crap:

1. Create a new account.  Disable the old account

2. Restrict the account to only specific computers

3. Use machine accounts (aka "SYSTEM")

I prefer option 3 for everything.  Like killing ants with a Gatling-style howitzer.  But that's just me.  As for option 1, it'll leave a mess behind, that's for sure.  But the problem will eventually be solved.  However, it will only work its way out if you combine it with option 2.

Option 2 is really what you should have been doing all along.  A funny little trick we used to do waaaay back to **** with co-workers was to get on our computers in the morning, and enter the user ID for the co-worker and fat-finger the password (intentionally) enough times to lock the account.  Then chuckle, sip some coffee and log in normally and wait for the yelling from across the room.  Good times. 

Funny: maybe (to me it was), but this is actually a serious problem.  A very serious problem. It means with a casual query of AD (very simple for any user to do) I can guess some of the critical account names and intentially lock them and disrupt business processes.  This is kind of like that guy in the new All State commercials with the band-aid on his face going around causing problems like Mr. Murphy himself.  Option 2 above helps mitigate this vulnerability from being exploited and hitting your sensitive operations accounts.

Option 3 not only mitigates a sneaky prank attack like that described above, but it also removes the need to monkey with stupid passwords.  I mean - seriously - this is 2010 and we're STILL having to stop everything to change passwords - modify scheduled jobs - edit config files and DSN's - fix Dr. Asswipe's PHd hard-coded stupid-ass application (the guy in accounting who insists on writing his own apps and smokes a pipe, you know the guy), and STILL get any work done?  Seriously?!  We've gone NOWHERE.  Our forefathers would be crying in their colonial beer mugs.

Saturday, May 1, 2010

Scheduling a Task on 500 Windows Computers

Back in 2008, I produced (if you can call it that) a series of training videos on Windows Server 2008 highlighting various features.  Those are still posted on BlogCastRepository.com, but they require a paid subscription to view them.  There are two reasons why that’s not worth pursuing at this point:

1. There are now dozens of video tutorials on the web which do a much better job of demonstrating these features.

2. The owner of BlogCastRepository.com, Brian S. Tucker, passed away sadly.  No one has been able to determine who is (or will) assume management of the site.  It is rudderless.

Anyhow, the video clips are too large to post anywhere that doesn’t charge a fee and I can’t justify the expense.  So they will just sit until the site vanishes.  I have backups, but nowhere to host them. Hint?

On to the subject…

One of my personal favorite features of Windows Server 2008 and R2, as well as Windows 7, is the new and VASTLY improved Task Scheduler.  It simply rocks.  For years I’ve used Cron, even on Windows, because it handled most of my needs better than the XP Task Scheduler or AT or even JT (ugh).  Here is just one example…

Task:  Set an identical scheduled task on multiple computers running Windows 7 or Windows Server 2008, but without using Group Policy or Group Policy Preferences.  And without using something like SCOM.

Steps:

  1. Create task on first computer and test to ensure it works properly
  2. Export task to XML file
  3. Store the XML in an accessible share on the network
  4. Run BAT file to invoke SCHTASKS /CREATE /XML <xml-file-path> on each remote computer (/S computername)

Bonus:  Modify the same scheduled task on multiple computers

Steps:

  1. Edit the task on the first computer and export to XML file (replace existing file)
  2. Run BAT file to invoke SCHTASKS /DELETE /TN <task-name> on each remote computer
  3. In the same BAT file, invoke SCHTASKS /CREATE /XML <xml-file-path> to import the task from the XML file

Examples of why you might need to change a task configuration:

  • Schedule change
  • Task change (script, program, etc.)
  • User credentials or password

Thursday, March 18, 2010

SCHTASKS vs AT vs WMI - We All Lose

Dear Microsoft MSDN folks:
It's now 2010 and Windows 7 is your current wunderkind product.
After you get done drinking and partying, and clean up all the puke and beer stains on the rugs, maybe you can take a look at something that has been BROKEN FOR A LONG LONG LONG LONG LONG LONG LONG TIME?
It's the CIM model for handling things like Task Scheduler (no, not AT, so if you start blabbering about AT and WMI just please STFU).  There are a few others, but this is really busted broke and needs some tender loving care.
Why do you still support two entirely separate job scheduling mechanisms on a client?  Why are they incompatible?  Why haven't you at least provided a wrapper object to aggregate the two?  If you laid off the folks that could have fixed this, why not just release a free bandaid to help us insignificant customers of yours out a little?
Now, I'm back to writing my own crude XML dump utility which uses SCHTASKS like a whipping boy.  God, this is so frigging stupid as hell, but here’s the code: Scriptzilla Blog Post

Saturday, March 6, 2010

Putting Technology to Everyday Use: Twitter

I’ve already discussed Twitter on this blog and on my other other blog (Scriptzilla), but I wanted to describe a process I’ve been using to keep control over my home network even while I’m out and about.  The Twitter API provides a robust set of tools for reading, composing and managing tweets and direct messages (aka “DM’s”).  The meat of this is in their API Documentation and can be leveraged with almost any existing scripting or programming language.

I posted a few examples for how to read and send Twitter DM’s for Windows computers using VBscript.  You can just as easily do this with KiXtart or PowerShell (but not as directly), and tie your processes to events such as logons, startups or event-triggers or whatever. I use UberTwitter to manage my home network from afar by having it send me event DM’s and listening for my commands.  You can use any mobile Twitter client or web/desktop Twitter client however, even the Twitter web site itself.  How you get there is irrelevant.  A mobile client on your smartphone is the ideal way though.

For example, I can tweet (DM) my server’s Twitter account to do a “hand-in-the-face” for one of my kids when they step out of line (skip chores, skip homework, act disrespectful, letter sent home from school, etc.).  The workflow is something like the following…

tdm_flowchart 

The third block in this flowchart is actually multiple steps.  They are described more thoroughly in my other blog posts.  Basically that “block” involves a scheduled task that keeps a watch on the Twitter DM inbox, looks for specifically formatted messages, parses the message for specific phrases that indicate coded instructions (you make that up as you see fit), and executes a corresponding script action to do the work.  Then the computer deletes the DM (so that it doesn’t keep reading it every scheduled cycle), and (optionally, but recommended) the computer sends a DM confirmation back to my other Twitter account, letting me know it got the instruction and processed it.

The key to this process is careful planning, setting up, testing, testing and more testing and then making sure everything is locked down securely.  Make sure you set your computer’s Twitter account as private (locked) and it only follows your regular (human) Twitter account.  You have to follow your computer’s Twitter account also (and have it accept the request), so you can exchange DM’s.  If you use “tweets” instead of DM’s, you’ll be leaving yourself wide open to hacks and accidental catastrophes.

If you find this stuff interesting or useful, post a comment and share your thoughts.

Thursday, August 21, 2008

Using the SYSTEM account instead of a domain user Service account

Man, this comes up all the time, but I still get on the bandwagon and try to reiterate the benefits of this each time it comes up.




When running a scheduled task, especially for things like scripts or back-ups or monitoring services, etc. you should ALWAYS try to make it work using the local SYSTEM account rather than a domain user "service" account. Why?


  1. No having to deal with passwords.


  2. Much more narrowly defined security model


  3. Local administrative rights


  4. Simple as dirt


What exactly am I talking about?


A domain user "service" account is simply a user account created in Active Directory, which is almost always granted special domain group and security rights, and is used to run scheduled tasks unattended. The problem is that such accounts are managed in the "open" and require a *known* password. Known by someone. Anyone. This introduces two problems:



  1. The person that knows the password has to record it securely and ensure there is a failsafe backup (someone else has access to the password in case person "A" is hit by a bus).


  2. The password may require being reset on a periodic interval (group policy or even worse: SOX)


By using the built-in, local "SYSTEM" account, you circumvent a lot of these issues, and make the security model much simpler to manage. For example:



  1. The password is managed by the computer itself (and AD). No human involvement here.


  2. The password resets are handled automatically.

To make this change, you need to understand that computers have account in Active Directory just like Users do. The only functional difference is that the name has a $ suffix appended and computers are only members of one domain group by default: Domain Computers. This group, by default, is not a member of any other groups. Not even "users" or "domain users". It's a group on a shelf, waiting to be used if needed.


So if you run a task on computer "A" that needs to read from, or write to, a shared folder on computer "B", you simply grant either (A) the specific computer account "A$" or the domain group "Domain Computers", the appropriate rights to the shared folder. That's it. Done. This model works extremely well in most cases. I've used it for back-ups, monitoring, scripting, reporting, scanning, and lot's of things.


Is this "new"? Not at all. Microsoft uses this model for SMS 2003 "Advanced Security" as does System Center Configuration Manager 2007 and System Center Operations Manager 2007. Many other products, Microsoft and others, use this security model as well.


So why haven't IT administrators and software developers climbed on board with this? I have no freaking idea. It's so much simpler and cleaner and less hassle. Maybe this will catch on, but so far I'm not seeing it spread like wildfire.