O R G A N I C / F E R T I L I Z E R: 2008

Dec 3, 2008

removing lines from repadmin output with powershell

i challenged a coworker to put his powershell class to good use and come up with a script that would actually have an impact, albeit little, to his day-to-day administrative work. he came up with a little script that would dump repadmin output to a text file and mail him an attachment. here’s the script:

%{repadmin /replsum * /bysrc /bydest} > logfile.txt

$filename = “logfile.txt”
$smtpServer = “mysmtp.mydomain.com”

$msg = new-object Net.Mail.MailMessage
$attachment = new-object Net.Mail.Attachment($filename)
$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$msg.From = “myemail@mydomain.com”
$msg.To.Add(”myemail@mydomain.com”)
$msg.Subject = “Repadmin Report ” + [System.DateTime]::Now
$msg.Body = “The daily Repadmin log file is attached”
$msg.Attachments.Add($attachment)

$smtp.Send($msg)

this is okay, but opening that attachment every time would be irritating… so i thought it might be easier just to get the output into a variable and write it to the body of the email directly. WOW. that proved to be more difficult than i thought! the idea is simple, but the formatting was killing me.

so to begin, i removed the opening line and used the following to create an array to hold the information coming in from repadmin. also, i prefer sorting by delta… so that’s why i modified that.

$myRepInfo = @(repadmin /replsum * /bysrc /bydest /sort:delta)

didn’t need the $filename variable nor the $attachment variable anymore so i dropped both of those. everything else is pretty much the same. once i reached the $msg.body statement is where i had some trouble with it. the first thing i noticed is that $myRepInfo had extra linefeeds once converted [1]. to get around that was actually pretty easy. feeding it through the where object seemed to work.

$myRepInfo | Where-Object { $_ }

now i have something a little more presentable. adding this to $msg.body couldn’t be easier, right? a simple statement like $msg.body = $myRepInfo | ? { $_ } should do fine. it doesn’t though. once i send it to $msg.body, it loses its linefeeds altogether!

Replication Summary Start Time: 2008-12-03 08:49:11 Beginning data collection for replication summary, this may ta
ke awhile:   ..................................................   ............... Source DC           largest delt
a  fails/total  %%  error  myDCServer1               59m:15s    0 / 111    0    myDCServer2                59m:15s
 0 /  38    0    myDCServer3                 59m:15s    0 /  24    0    myDCServer4                59m:15s    0 /
28    0    myDCServer5                 59m:15s    0 /  24    0    myDCServer6                59m:11s    0 /  24   

well, to get around that, we need to modify that statement once more to replace each line with the same line plus a linefeed. here’s the resulting command:

$msg.Body = $myRepInfo | Where-Object { $_ } | ForEach-Object { $_ -replace $_,"$_`n" }

that makes the output a little more favorable like this:

Replication Summary Start Time: 2008-12-03 08:49:11
Beginning data collection for replication summary, this may take awhile:
  ..................................................
  ...............
Source DC           largest delta  fails/total  %%  error
 myDCServer1                 59m:15s    0 / 111    0
 myDCServer2                 59m:15s    0 /  38    0
 myDCServer3                 59m:15s    0 /  24    0
 myDCServer4                 59m:15s    0 /  28    0
 myDCServer5                 59m:15s    0 /  24    0
 myDCServer6                 59m:11s    0 /  24    0

here’s the finished script. a little more compact… not much different. the end product, at least for me, makes it nicer. :)

$myRepInfo = @(repadmin /replsum * /bysrc /bydest /sort:delta)
$smtpServer = "mysmtp.mydomain.com"

$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$msg.From = "myemail@mydomain.com"
$msg.To.Add("myemail@mydomain.com")
$msg.Subject = "Repadmin Report " + [System.DateTime]::Now
$msg.Body = $myRepInfo | Where-Object { $_ } | ForEach-Object { $_ -replace $_,"$_`n" }

$smtp.Send($msg)

[1] my understanding from some deep inner circles is that some utilities like repadmin or ipconfig use a printf function. if you want to see the kind of output it displays, you can push this through an octal dump command. this can be accessed from through a unix subsystem (services for unix, etc) or by using the cygwin tools – which is what i did.

this is what a typical output would look like. it’s all the “\r” formatting that junks it up.

repadmin /replsum * /bysrc /bydest /sort:delta | od -c

0000000   R   e   p   l   i   c   a   t   i   o   n       S   u   m   m
0000020   a   r   y       S   t   a   r   t       T   i   m   e   :
0000040   2   0   0   8   -   1   2   -   0   3       0   9   :   3   7
0000060   :   0   9  \r  \r  \n  \r  \r  \n   B   e   g   i   n   n   i
0000100   n   g       d   a   t   a       c   o   l   l   e   c   t   i
0000120   o   n       f   o   r       r   e   p   l   i   c   a   t   i
0000140   o   n       s   u   m   m   a   r   y   ,       t   h   i   s
0000160       m   a   y       t   a   k   e       a   w   h   i   l   e
0000200   :  \r  \r  \n           .   .   .   .   .   .   .   .   .   .
0000220   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .
*
0000260   .   .   .   .   .   .   .   .  \r  \r  \n           .   .   .
0000300   .   .   .   .   .   .   .   .   .   .   .   .  \r  \r  \n  \r
0000320  \r  \n  \r  \r  \n   S   o   u   r   c   e       D   C
0000340                                       l   a   r   g   e   s   t
0000360       d   e   l   t   a           f   a   i   l   s   /   t   o
0000400   t   a   l           %   %           e   r   r   o   r  \r  \r

Dec 2, 2008

using powershell to replace “find” or “findstr”

this is one of those things i’m blogging to remind myself instead of bugging hal rottenberg. :)

in order to find something inside a list of files, you can use find or findstr. what’s the difference between those? findstr is a bit more robust, accepting pattern matches with regex, for example. in most cases though, i’m just looking for a string inside of a list of text files. so here we go with find and the general output we can expect…

C:\temp>find /i "wscript.echo" *.*

---------- DATE2INTEGER8.VBS
' wscript.Echo CurrentDate(Now)
' WScript.Echo CurrentDate(dDateThreshold)
      WScript.Echo oRecordSet.Fields("cn") & ":" & oRecordSet.Fields("displayname") & ":" & Integer8Date(oRecordSet.Fields("pwdlastset").Value,lBias)

---------- DLNAMES.TXT

---------- DNS_DEBUG.LOG

---------- TEMP_SCRIPT.SM
 WScript.Echo
 WScript.Echo "=========================================="
 WScript.Echo "Computer: " & strComputer
 WScript.Echo "=========================================="
    WScript.Echo "ArpAlwaysSourceRoute: " & objItem.ArpAlwaysSourceRoute
    WScript.Echo "ArpUseEtherSNAP: " & objItem.ArpUseEtherSNAP

i was kind of perturbed about having to switch back and forth from cmd shell to powershell so i asked hal about it one day… and he told me to use select-string. it turns out if acts differently if you don’t pipe anything to select-string. as you can see, the output is much nicer too …

[12] » Select-String -SimpleMatch -Pattern "wscript.echo" -Path *.* | Format-Table filename, linenumber, line -autosize

Filename          LineNumber Line
--------          ---------- ----
date2integer8.vbs         11 ' wscript.Echo CurrentDate(Now)
date2integer8.vbs         12 ' WScript.Echo CurrentDate(dDateThreshold)
date2integer8.vbs         33     WScript.Echo oRecordSet.Fields("cn") & ":" &
temp_script.sm             8    WScript.Echo
temp_script.sm            10    WScript.Echo "Computer: " & strComputer
temp_script.sm            18       WScript.Echo "ArpAlwaysSourceRoute: " &
temp_script.sm            19       WScript.Echo "ArpUseEtherSNAP: " &
temp_script.sm            20       WScript.Echo "Caption: " & objItem.Caption

i purposely wrote the command verbosely for clarity. to be succinct, in this case, you’ll get the same result with:

ss "wscript.echo" *.* | ft f*, l* -auto

Dec 1, 2008

discovery data manager fails while processing a ddr

seems like a good day to blog about sms.  this happened with sms 2003.  yes, i realize there’s a configmgr.  very soon, i’ll be using it!  :)  for the rest of the unfortunate few, here’s the background:

i received this error in mom today.  this is one of those indicators that should immediately tell you that a great day is about to ensue.

mySMSServer - SMS 2003 Perf Threshold: Site Server DDR Backlog > 10,000 over 3 hours.

SMS Discovery Data Manager: Total DDRs Enqueued: value = 8700. The average over last 12 samples is 5280.83.


that’s just bad.  further investigation showed that the server failed to process ddrs since 11/28/08.  i checked around to see if anything changed, but there wasn’t anything unusual.  so … off to the status messages.  here’s what i found:

Microsoft SQL Server reported SQL message 242, severity 16: [22007][242][Microsoft][ODBC SQL Server Driver][SQL Server]The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value.


now you just have to know that can’t be a good thing.  i presumed it was a generalized error message, checking the table for congruency against a working sms server.  everything looked fine.  off to the logs (ddm.log in particular)…

Update base table: System_DISC : execute sql update System_DISC set ItemKey = 17602, DiscArchKey = 5, User_Name0 = "myUser", User_Domain0 = "myDomain", SMS_UUID_Change_Date0 = "01/01/1601 08:01:44" where ItemKey = 17602

*** update System_DISC set ItemKey = 17602, DiscArchKey = 5, User_Name0 = "myUser", User_Domain0 = "myDomain", SMS_UUID_Change_Date0 = "01/01/1601 08:01:44" where ItemKey = 17602

*** [22007][242][Microsoft][ODBC SQL Server Driver][SQL Server]The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value.


that looks bad, particularly the field for sms_uuid_change_date0.  i tailed this back to the .ddr it was complaining about and found the property.  i just changed 1601 to a value it could accept… like 2008.  it actually happened on two or three.  i happened to see those come in to ddm.log when i did some syntax highlighting to show them in real time.  after making the modification to the ddrs, they processed through.  all ~8000 or so…

yes, you can test this exact scenario!  knowing that it was running a datetime conversion, i ran the following query and got the following response.  looks eerily similar eh?

SELECT convert(datetime,'01/01/1601 08:01:44')

Msg 242, Level 16, State 3, Line 1
The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value.


there are probably plenty of reasons why discovery data manager will choke.  i know i ran into plenty of articles about it.  interesting that they weren’t tossed out as bad ddrs.  honestly, i have no idea why the date came in as that format.

using preloadpkgonsite.exe to stage compressed copies to child site distribution points

UPDATE: john marcum sent me a kind email to let me know about a problem he ran into with preloadpkgonsite.exe in the new SCCM Toolkit V2 where under certain conditions, packages will not uncompress.  if you are using the v2 toolkit, PLEASE read this blog post before proceeding.

 

here’s a scenario that came up on the mssms@lists.myitforum.com mailing list. when confronted with a situation of large packages and wan links, it’s generally best to get the data to the other location without going over the wire. in this case, 75gb. :/

the “how” you get the files there is really not the most important thing to worry about. once they’re there and moved to the appropriate location, preloadpkgonsite.exe is required to install the compressed source files. once done, a status message goes back to the parent server which should stop the upstream server from copying the package source files over the wan to the child site.

anyway, if it’s a relatively small amount of packages, you can run the execution like so:

preloadpkgonsite.exe <pkgid>

of course, if it was a small amount of packages, we wouldn’t be having this conversation right now. so what do you do when you have an enormous directory with 75gb worth of packages?

well, it would look a little something like this…

  1. on the site server, navigate to <installdir>\bin\i386. this is where the preloadpkgonsite.exe should have been dropped.
  2. run the following command:
for /f "delims=." %a in ('dir /b \\<siteserver>\sms_<sitecode>\inboxes\pkginfo.box\*.pkg') do preloadpkgonsite.exe %a

that will effectively run preloadpkgonsite.exe against every known package on that child site server. now there are caveats to making this work – one being that the .pkg has to exist on the child site server.

i’m really blogging this for sherry kissinger who wrote this excellent blog article on replacing secondary site servers. she claims she doesn’t want to dig this up in her email. i think really, she didn’t want to be bothered with having to blog this. :)

this is just a small part to an overall solution for managing sites across the wan. make sure you read the information associated with the preloadpkgonsite.exe tool to understand the caveats and dependencies.

Oct 27, 2008

jalasoft and savision – that’s great teamwork…

i just got this press release.  i have to say, this is great stuff!  unless you’re near an “envisioning center” it’s really difficult to get a grasp of how all these partner products work together to form a cohesive monitoring platform with operations manager.  here’s a little blurb about it…

Jalasoft and Savision team up at Microsoft Tech·Ed IT Professionals 2008

Santa Clara/Utrecht, October 27, 2008--

Jalasoft and Savision
announced today that they are teaming up to provide demonstrations of their products at TechED IT Professionals 2008 this November in Barcelona.  Jalasoft’s Xian Network Manager Io and Savision’s Live Maps are both products designed to work with Microsoft System Center Operations Manager 2007. Live Map’s elaborate mapping features give Xian users the power to create network aware topologies, business process and geographical maps of your network. Additionally, Xian Network Manager adds network devices down to interface level allowing you a detailed view of your topology so you can pinpoint exactly where the problem is located.

“Xian Network Manager provides data and alerts; Live Maps brings it all together into an easy to understand graphical representation” said Dennis Rietvink CEO at Savision, “Customers who combine these two products with Operations Manager 2007 will be astonished by the visualization power.”

Jalasoft and Savision can be found at booth E6 and E7 at Tech·Ed IT Professionals which takes place November 3 – 7, 2008 in Barcelona, Spain.

For more information contact:
Claudia Ricaldez, Claudia.Ricaldez@jalasoft.com, +1 888 402 6717

About Jalasoft
Jalasoft provides advanced systems management solutions for the heterogeneous computing environment. Xian Network Manager Io, in combination with Microsoft System Center Operations Manager or Microsoft System Center Essentials, is designed to proactively monitor and manage critical infrastructure components such as network devices and UNIX servers to increase availability and accelerate problem detection and resolution with the objective of improving the ROI customers realize from their infrastructure.

About Savision
Savision provides advanced visualization solutions for Microsoft’s System Center Operations Manager. Savision’s Live Maps allows IT Pros to quickly create monitoring maps that visualize business processes, network and service topologies, executive dashboards and geographical maps. Founded in 2006, Savision is headquartered in the Netherlands and is privately held. Savision’s founders and executives bring years of enterprise systems and application management experience from large IT service companies. For more information, go to
http://www.savision.com.

southeast management user group – november 14, 2008!

hey folks,

we’ve finished planning another user group and have the presenters and sponsors lined up.  acresso and securevantage were kind enough to buy us some food!  please make sure to register soon so we can get an accurate headcount to feed everyone.  :)

head over to the official smug site for the agenda, located at: http://systemcenterusergroup.com/blogs/smug/

if you just want to register, here’s the link: http://msevents.microsoft.com/CUI/InviteOnly.aspx?EventID=5B-11-D1-1D-08-92-D8-84-78-E1-CD-6A-5C-13-92-AA&Culture=en-US

 

look forward to seeing you all!

Oct 6, 2008

ping sweeping with FOR statements …

i just wanted to familiarize myself with the for statement in powershell by playing around with a few examples.  this one, i find particularly useful in situations where for example, i’m sitting in a lab and need to know what IPs are available.

back to what i was saying, to get familiar with the for statement, i thought i’d start off with what i know in cmd shell.  if you look at this this block below, you’ll see that i’m using the /L switch of the for statement to handle stepping through a sequence of numbers.

for /l %i in (1,1,255) do @ping -n 1 99.206.102.%i | find /i ": bytes=" > nul && @echo Successfully pinged 99.206.102.%i Successfully pinged 99.206.102.2 Successfully pinged 99.206.102.3 Successfully pinged 99.206.102.14 Successfully pinged 99.206.102.17

 
 

well, that worked out.  while the cmd shell has an IF statement, it doesn’t have a true if/then/else conditional statement.  you can simulate this in batch script… but you can’t do it with the cmd shell.  anyway, here’s the equivalent in powershell.  note though, that we can use if/then/else and can output results if the ping fails.  :)

for ($i = 1; $i -le 255; $i ++) {If ((Get-WmiObject win32_pingstatus -Filter "address='99.206.102.$i'").Statuscode -ne 0) {"Failed to ping 99.206.102.$i"} else {"Successfully pinged 99.206.102.$i"}}

Successfully pinged 99.206.102.1
Successfully pinged 99.206.102.2
Successfully pinged 99.206.102.3
Failed to ping 99.206.102.4
 
 
the first method was using the wmi class win32_pingstatus.  for the hell of it, here’s the same thing, using the .net ping method.  notice we have to initialize the ping object first, though.

$ping = New-Object System.Net.NetworkInformation.Ping

for ($i = 1; $i -le 255; $i ++) {If ($ping.Send("99.206.102.$i").Status -ne "Success") {"Failed to ping 99.206.102.$i"} else {"Successfully pinged 99.206.102.$i"}} Failed to ping 99.206.102.1 Failed to ping 99.206.102.2 Successfully pinged 99.206.102.3 Failed to ping 99.206.102.4

 
 
the for statements look pretty identical.  so, first cmd shell, then powershell:
for /l %<variable> in (<start>,<step>,<end>) DO <command_block>
 
for (<init>; <condition>; <repeat>) {<command_block>}

Oct 2, 2008

executing batch files remotely with psexec …

if you’ve got a batch file of some sort (bat or cmd) sitting out somewhere that you want to execute remotely, you’ll want to make sure you’re following the right syntax to get this to work.

here are some examples that DO NOT work:

psexec \\myDesktop \\myServer\myShare\test.cmd

 

PsExec could not start \\myServer\myShare\test.cmd on myDesktop:
Access is denied.

 

by default, psexec runs the context in localsystem.  since the system most likely doesn’t have access to the share, let’s give psexec some credentials which has access to the share:

psexec \\myDesktop -u myUser -p myPassword \\myServer\myShare\test.cmd

 

PsExec could not start \\myServer\myShare\test.cmd on myDesktop:
Access is denied.

 

ah, this isn’t going to work either, but we’re getting closer.  the access denied message throws me off a little since i know the password is right.  it seems the problem is that psexec will not execute the .cmd or .bat file without knowing the context in which to execute… so we give it one.  cmd.exe.

psexec \\myDesktop -u myUser -p myPassword cmd /c \\myServer\myShare\test.cmd

 

C:\WINDOWS\system32>mkdir c:\testme
cmd exited on myDesktop with error code 0.

 

now there we go… :)

Sep 30, 2008

dsmod bug when using the –c option?

UPDATE: thanks to some anonymous commenters, i have corrected my example in this post. it seems i left off the trailing %a in the for loop! oops. fixed now.

i was visiting up in roanoke extolling about the boundless possibilities with command shells, scripting, etc to a near liability.  in other words, i bored them nearly to death.  :)

to my surprise, it stuck.  i’ve been exchanging conversation with one of the site admins and ran across this bug while running through a sample scenario on listing members from one group and adding them to another.  typically, you could do this quite easily with the dsquery tool set.

it looks something like this:

dsquery group -name "myGroup" | dsget group -members | dsmod group "cn=myNewGroup,ou=etc,dc=etc,dc=etc" -addmbr –c
 
so what are we doing here?
  1. dsquery group –name “myGroup” – retrieves the dn of the group
  2. dsget group –members – retrieves the membership list (dn) of the group passed through the pipe
  3. dsmod group “cn=mynewgroup…” –addmbr –c – adds the members of the previous group into specified group.

this works fine as long as there are no conflicts.  if you run into conflicts, the process bombs out with this error:

dsmod failed:CN=myNewGroup...:The specified account name is already a member of the local group.
 
the –c option specifed above should go right past this condition and keep trying other members.  it doesn’t work no matter what position you place it, however.  to get around this, you can use for looping.  :)
 
for /f "delims=" %a in ('dsquery group -name "myGroup" ^| dsget group -members') do dsmod group "cn=myNewGroup..." –addmbr %a
 
so how is this different?
  1. for /f “delims” %a in (‘dsquery…’) – retrieves the membership list of the group and assigns them as a token value of %a
  2. dsmod group “cn=myNewGroup…” –addmbr – for each member, we’re adding them individually to the group.

in this case, even if we run into failures, it doesn’t matter since we’re kicking off dsmod as separate commands each time.

and of course, to do this in powershell, you’d execute a command like this:

Get-QADGroupMember "myGroupName" | foreach {Add-QADGroupMember -identity "CN=myNewGroup..." -member $_}

Sep 16, 2008

verifying replication failure with admp and mom 2005

you’ve no doubt seen this error message if you’re monitoring active directory replication.

The following DCs have not updated their MOMLatencyMonitor objects within the specified time period (8 hours). This is probably caused by either replication not occurring, or because the 'AD Replication Monitoring' script is not running on the DC.

Format: DC, Naming Context, Hours since last update

My-Site
myDCserver, NDNC:DC=DomainDnsZones,DC=myDomain,DC=com, 16
 

typically, this error is generated when a DC is no longer replicating.  the ADMP script watches changes to an attribute called adminDescription.  under the container MOMLatencyMonitors off the root of the watched naming context, exist objects that represent all of the DCs for that naming context.

for example:

myDCserver, NDNC:DC=ForestDnsZones,DC=myDomain,DC=com, 9
 

this statement indicates that the domain controller myDCserver has not replicated the required value for 9 hours or more in the naming context of DC=ForestDnsZones,DC=myDomain,DC=com.  there are two places this can fail:

  1. the domain controller is having trouble replicating.
  2. the MOM Agent is not operating correctly to write to the adminDescription.

to narrow down the problem, follow the steps below.

the domain controller may be having trouble replicating.

to validate this condition, we can use repadmin.  issuing the following command gets us some usable data.

repadmin.exe /showrepl myDCserver
 
DC=ForestDnsZones,DC=myDomain,DC=com
    mySite\myDCserver2 via RPC
        DC object GUID: 67x4141y-x526-45xy-x32y-8x04yx041yx7
        Last attempt @ 2008-09-16 09:46:41 was successful. 
 

it's important to pay attention to the naming context that was specified.  in this case, we see that the last attempt was successful and very close to the current timeframe.  this indicates that replication is not the issue.

 

the mom agent is not operating correctly to write to the adminDescription.

as stated above, the admp script to check replication uses the objects in these containers to handle a type of synthetic replication.  for the mom agent running the script, it writes to its own object's adminDescription attribute.  in order to see where a problem may exist, we can utilize dsquery to list the current attributes for all objects in the naming context of ForestDnsZones.

dsquery * cn=momlatencymonitors,dc=forestdnszones,dc=cox,dc=com -scope onelevel -attr name admindescription
 

and receive the following results:

name admindescription
myDCserver 20080916.0301
myDCserver1 20080916.1301
myDCserver2 20080916.1401
myDCserver3 20080916.1501
myDCserver4 20080916.1301
myDCserver5 20080916.1101
myDCserver6 20080916.1301
myDCserver7 20080916.1301
myDCserver8 20080916.1201

from the results, we can determine that this time, the replication alert is actually a mom error as noted by the delta between myDCserver and any other in the list.

Sep 15, 2008

restarting services and terminating processes with mom 2005

this particular example is for softgrid.  i thought it might be useful to generalize it for any purpose, though.  you probably already have services that may require a restart every now and then.  that’s pretty easy in mom.  you can do it by issuing a simple net stop && net start command as illustrated in this post.

the general perception is that admins are lazy.  to help perpetuate this obvious lie, i tried to use the simple method above but failed.  it turns out that some services don’t terminate the processes upon stopping, as you would expect.  short of trying some ridiculously long for loop statements inside of the batch response, you have to go with a script.

i really did consider going with batch script but ended up needing a bit more flexibility.  for instance, instead of blindly going through the cycle, i wanted to make sure we were still in the given condition before we went ahead with it.  to do that, we have to check the process utilization state.  anyway, the script does the following:

  • examines process(es) and the processor utilization rate
  • stops the service
  • terminates the running process(es)
  • starts the service
  • creates a log output
  • stamps the alert description with informative data

of course, we need to give it the process and service name we want it to attack.  for that, you’ll need the following parameters when you set up this script in mom.

  • sProcess – process name
  • iThreshold – threshold that the process utilization must be above
  • sService – service name to restart
  • sLogName – name of log file to generate

a bit more minutia – the script will check the process utilization 10 times in a row, then divide by 10 for the average.  if the average is above the threshold, it goes through the cycle to reset the thing.  you can change all that crap around in the script but is not exposed by parameter.

we’re in testing with opsmgr, so whenever we go live, i’ll have to convert these scripts.  i’ll post them in opsmgr format as i get them prepared.  for now, here’s the mom 2005 version:

'==========================================================================
' NAME: Service/Process Restart
'
' AUTHOR: Marcus C. Oh
' DATE  : 9/15/2008
'
' COMMENT: Recycles runaway processes and services based on a threshold
'          Logs to %windir%\temp directory
'
' VERSION: 1.0
'==========================================================================

' Standard event constants
Const EVENT_TYPE_SUCCESS = 0
Const EVENT_TYPE_ERROR   = 1
Const EVENT_TYPE_WARNING = 2
Const EVENT_TYPE_INFORMATION = 4

' Parameters for MOM
sProcess = ScriptContext.Parameters.Get("Process")
iThreshold = CInt(ScriptContext.Parameters.Get("Threshold"))
sService = ScriptContext.Parameters.Get("Service")
sLogName = ScriptContext.Parameters.Get("LogName")

sComputer = "."
bCycle = False

Set oAlert = ScriptContext.Alert


' Spin up the File System provider and create the log file
Set oShell = CreateObject("Wscript.Shell")
sWinDir = oShell.ExpandEnvironmentStrings("%WinDir%")
Set oFS = CreateObject("Scripting.FileSystemObject")
Set myLogFile = oFS.CreateTextFile(sWinDir & "\temp\" & sLogName,True)


' Spin up WMI
Set oWMIService = GetObject("winmgmts:\\" & sComputer & "\root\cimv2")


' Check the process from the parameter to see if the utilization 
' is currently above the indicated threshold.

myLog "[Starting process cycling...]"

'Set oPerfData = ScriptContext.Perfdata
myLog VbCrLf & vbTab & "Checking process(es) for: " & sProcess

Set cProcessNames = oWMIService.ExecQuery("Select handle from Win32_Process Where Name like '" & sProcess & "%'")
For Each oProcName In cProcessNames
    iLoop = 0
    iProcTime = 0
    myLog vbTab & "Examining process handle " & oProcName.handle
    While iLoop < 10
        Set cProcesses = oWMIService.ExecQuery("Select * From Win32_PerfFormattedData_PerfProc_Process Where IDProcess = '" & oProcName.handle & "'")
        For Each oProcess in cProcesses
            iProcTime = iProcTime + CInt(oProcess.PercentProcessorTime)
            myLog vbTab & oProcess.Name & " utilization aggregate - " & iProcTime & " (sample value - " & CInt(oProcess.PercentProcessorTime) & ")"
        Next
        iLoop = iLoop + 1
        mySleep(1000)
    Wend
    
    myLog vbTab & "Aggregate utilization for process handle " & oProcName.handle & " - " & iProcTime
    
    If iProcTime/10 > iThreshold Then
        myLog vbTab & "Process utilization matches criteria."
        myLog vbTab & "Divided by 10 - " & iProcTime/10
        bCycle = True
        Exit For
    Else
        myLog vbTab & "Process utilization at " & iProcTime/10 & " does not exceed threshold of " & iThreshold & VbCrLf
    End If
Next

If bCycle = True Then
    ' Stop the service.
    Call CommandService(sService,"Stop")
    mySleep(5000)
    
    
    ' Terminate all running processes.
    If VerifyService(sService,"Stopped") Then
        myLog VbCrLf & vbTab & sService & " has stopped successfully."
        myLog VbCrLf & vbTab & "Terminating process(es): " & sProcess
        Call TerminateProcess(sProcess)
    End If
    mySleep(5000)


    ' Start the service.
    Call CommandService(sService,"Start")
    mySleep(10000)
    
    
    'Verify the service started.
    If VerifyService(sService,"Started") Then
        myLog vbTab & sService & " has started successfully."
    Else
        myLog vbTab & sService & " has failed to start."
    End If

    
    ' Rewrite the original description with additional data.
    oAlert.Description = oAlert.Description & VbCrLf & VbCrLf &_
        "Remediation script for runaway processes has been executed." &_
        "Please review the following log for details: " & sWinDir & "\temp\" & sLogName
Else
    myLog vbTab & "Process utilization exceed threshold."
    
    ' Rewrite the original description with additional data.
    oAlert.Description = oAlert.Description & VbCrLf & VbCrLf &_
        "No remediation attempt required."
End If

myLog VbCrLf & "[Stopping process cycling...]"

' Close out the file
myLogFile.Close


' Subs and Functions ------------------------------------------------------

' Start/stop the service
Sub CommandService(sService,sAction)
    Set cServices = oWMIService.ExecQuery("Select * from Win32_Service where Name='" & sService & "'")
    For Each oService in cServices
        myLog VbCrLf & vbTab & sAction & " -- " & sService
        If sAction = "Stop" Then
            oService.StopService()
        ElseIf sAction = "Start" Then
            oService.StartService()
        End If
    Next
End Sub

' Verify the service state
Function VerifyService(sService,sState)
    Set cServices = oWMIService.ExecQuery("Select * From Win32_Service Where Name ='" & sService & "'")
    For Each oService in cServices
        If oService.State = sState Then
            VerifyService = True
        End If
    Next
End Function

' Terminate the processes
Sub TerminateProcess(sSGProcess)
    Set cRunningProcesses = oWMIService.ExecQuery("Select * from Win32_Process Where Name like '" & sSGProcess & "%'")
    For Each oRunningProcess in cRunningProcesses
        oRunningProcess.Terminate()
    Next
End Sub

' General sleep sub to switch between MOM and cmd line
Sub mySleep(iSleep)
    ScriptContext.Sleep(iSleep)
End Sub

Sub myLog(sData)
    myLogFile.WriteLine(sData)
End Sub

' Standard Event creation subroutine
Sub CreateEvent(iEventNumber,iEventType,sEventSource,sEventMessage)
    Set oEvent = ScriptContext.CreateEvent()
    oEvent.EventNumber = iEventNumber
    oEvent.EventType = iEventType 
    oEvent.EventSource = sEventSource
    oEvent.Message = sEventMessage
    ScriptContext.Submit oEvent
End Sub

Sep 3, 2008

troubleshooting device drivers with dpc problems

another little gem.  here’s what you need and some highlights:

process explorer
kernrate
hklm\system\currentcontrolset\services

  1. run process explorer
    • open the DPCs property
    • check the performance graph - see if it’s high
  2. if it is, run kernrate for 30-60 seconds
    • ctrl-c to escape and view the results
    • the offending item should be at the top or close
  3. find the subkey associated with the offending item
    • path is noted above
    • modify the “start” value to 4 in order to disable it.  (at your own risk)

thanks to steven daugherty … read the full article in windows it pro.

resetting windows socket layer with netsh

this comes out of windows it pro. cool enough to save. here’s a link to the related article. the command you issue is:

netsh winsock reset
 
thanks to apostolos fotakelis

Aug 27, 2008

sysinternals is now a suite

suite?  sweet.  i thought it was always a complete pain in the ass to have to download different utilities in the suite.  now you can get the entire zip all at once.  http://technet.microsoft.com/en-us/sysinternals/0e18b180-9b7a-4c49-8120-c47c5a693683.aspx

Aug 20, 2008

renaming files with powershell or for loop …

i have a directory of scripts with names like mom_myScript.vbs or sms_myScript.vbs.  this is all so that i can do a relatively simple directory search to see what kind of scripts i have for a particular technology i’m working with.  the problem is, i flip-flip on my use of hyphens and underscores and have apparently done it often enough to warrant a little a clean up.

first, the old way i would have done this in cmd shell.  it’s basically a for loop to go through the list of files in a directory that matches where the script has a hyphen.  to pull back just the file name, i’m using the dir /b command.  i’ve broken down the file name into tokens that’s separated by the hyphen and then renaming the files, positioning underscore between the tokens.

for /f "tokens=1-2 delims=-" %a in ('dir /b mom-*.*') do @ren %a-%b %a_%b

 

here’s my new, preferred way to do it in powershell.  basically, i’m pulling back the list of files i want to work with and passing it through to the rename-item cmdlet.  the script block here is pretty cool.  i can run a replace on the fly which doesn’t require any token breakdowns, etc.

dir mom-*.* | ren -NewName {$_.Name –replace 'mom-','mom_'}

Aug 19, 2008

sql server 2005 database health script noise…

out of the box, the sql server 2005 db health script is amazingly noisy.  here’s a description of a sample event that you don’t need to see (unless you report on them).

The database myDatabase in instance myInstance is in a healthy state.

 

aside from reporting on the value, you probably don’t care.  if you want to make this events stop for normal database state, you’ll need to modify the script sql server 2005 database health.

 

to begin with, look for this line:

Public Function CheckDBHealth(sInstance, sHighSevDatabases)

 

if you search far enough down (around line 2400), you’ll see a block of code that looks like the following:

Set objEvent = ScriptContext.CreateEvent()
objEvent.EventSource = SCRIPT_NAME
objEvent.EventNumber = iEventId
objEvent.EventType = iEventType
objEvent.Message = message
objEvent.SetEventParameter(sInstance)
objEvent.SetEventParameter(oDatabase.Name)        
objEvent.SetEventParameter(sState)
ScriptContext.Submit objEvent

 

to quiet down the script, we just need to put some logic around the block.  in order to do this, we’ll add a simple if/then that checks against the already defined boolean variable bDatabaseHealthy.  If it’s false, then we’ll write the event.  If not, we’ll just quietly let it ride… here’s the how the new block should look:

If Not bDatabaseHealthy Then
    Set objEvent = ScriptContext.CreateEvent()
    objEvent.EventSource = SCRIPT_NAME
    objEvent.EventNumber = iEventId
    objEvent.EventType = iEventType
    objEvent.Message = message
    objEvent.SetEventParameter(sInstance)
    objEvent.SetEventParameter(oDatabase.Name)        
    objEvent.SetEventParameter(sState)
    ScriptContext.Submit objEvent
End If

 

wow, at some point, i need to start blogging on opsmgr.  :/

Aug 14, 2008

default refresh periods for dynamic dns

i wrote this article on dns aging/scavenging simplified awhile back.  one of my coworkers recently asked me what the default refresh period was.  wow, i had totally forgotten since i had written it and since i had forgotten to put it in the original post, it was more time on google than i wanted to spend to find it.  that means – blog it.  so here it is… the default refresh periods. 

you can find this information from this article: http://technet.microsoft.com/en-us/library/cc757041.aspx.

service default refresh period
net logon 24 hours
clustering 24 hours
dhcp client

24 hours

The DHCP Client service sends dynamic updates for the DNS records. This includes both computers that obtain a leased Internet Protocol (IP) address by using Dynamic Host Configuration Protocol (DHCP) and computers that are configured statically for TCP/IP.

dhcp server

Four days (half of the lease interval, which is eight days by default).

Refresh attempts are made only by DHCP servers that are configured to perform DNS dynamic updates on behalf of their clients, for example, Windows 2000 Server DHCP servers and Windows Server 2003 DHCP servers. The period is based on the frequency in which DHCP clients renew their IP address leases with the server. Typically, this occurs when 50 percent of the scope lease time has elapsed. If the DNS default scope lease duration of eight days is used, the maximum refresh period for records that are updated by DHCP servers on behalf of clients is four days.

 

and while i’m at it, if you wanted to change some of these defaults, you can do this by group policy as of windows 2003.  i guess that should be pretty old news by now.  here’s the link for that article: http://support.microsoft.com/default.aspx/kb/294785.

imaged machines and the dnsapi event id 11163

i wonder if this is going to end up a long-winded post.  i never intend for that to happen because somewhere i picked up that technical information should be succinct.  however, when i started looking into this problem, it seemed like there just wasn’t good information on it.

synopsis

a user in your environment needs to have their machine reimaged.  as a loyal IT citizen, you promptly do so by any manner that happens to be your favorite (e.g. mdt, swimage, ghost, etc).  you bring up this machine as the same name.  later on, you try to remotely manage the machine but realize that the ip it once had is different.  you spin your wheels a bit trying to figure out why the new ip hasn’t registered in dns.  upon reviewing the event log of the machine, you discover events that look eerily similar to these:
Event Type:    Warning
Event Source:    DnsApi
Event Category:    None
Event ID:    11163
Date:        8/12/2008
Time:        5:32:32 PM
User:        N/A
Computer:    myComputer
Description:
The system failed to register host (A) resource records (RRs) for network adapter with settings:

   Adapter Name : myAdapterGuidorName
   Host Name : myComputer
   Primary Domain Suffix : myDomain.com
   DNS server list :
         10.x.x.x, 10.x.x.x
   Sent update to server : 10.1.1.1
   IP Address(es) :
     10.x.x.x

thanks to this blog post, i stopped chasing the elusive message “sent update to server : 10.1.1.1”.  so let’s leave that alone and move on to the actual problem.

 

some background

alright, so the prerequisite for this condition happening above is that you would need an AD-integrated zone that has secure-only dynamic updates.  when you do this, acls are placed on the dns objects in ad.  also, you’ll notice your dns objects will have an available security tab.
if a zone has secure-only set, the security tab will show an ace for the machine.  so, as it’s stated above, i’d see an ace for

MYCOMPUTER$ (myDomain\myComputer$)

in a zone with nonsecure and secure dynamic updates, there will be no such ace for the computer object.  what’s this mean?  anyone can make updates to that record.  sounds okay so far, right?

 

the real problem

you’re probably way ahead of where i’m getting with this.  essentially, the reimaged machine is no longer the same computer object as before.  stepping through this, in a secure-only zone…
  1. the original dns object was created by the computer through dynamic dns update
  2. ace is placed on the dns object by AD
  3. computer is reimaged with the same name
  4. computer objectsid changes during this operation causing a mismatched ace on the dns object*
  5. computer attempts to create/update the dns object in the zone
  6. attempt fails because the computer no longer has access to the dns object.

* you can verify this condition pretty easily.  check the security tab of the dns object.  do you see something that says "account unknown" followed by a long string of numbers?

 

the not-so-real solution

  1. the easiest/quickest fix is to go into the zone and delete the record. 
  2. you could also configure dns scavenging.  however, there is a lag period of when the record would get cleaned out.
  3. reimage the machine as a new computer name so that a new dns object is created instead of hitting a conflict against an existing object.
  4. modify your imaging process so that the record is deleted prior by an account w/ elevated privileges to the record.
  5. utilize the dhcp lease-expiration process that automatically unregisters the dns record.
i haven’t tried the method in step 5.  i’m curious how well it works.  leave me a comment if you have it in place.

Aug 12, 2008

show vmware snapshots script

here’s a simple, little powershell script to show all of your snapshots.  you have to use the vmware vi toolkit and virtual center to do this.  i have mine going to a html file, in this example.

# =============================================================================
# NAME: VMSnapshots
# AUTHOR: Marcus C. Oh, Cox Communications, Inc.
# DATE  : 8/5/2008
# COMMENT: A real simple script to pull back snapshots of a VM.
# =============================================================================

$myVC = $Args[0]

If ($Args[0] -eq $null) {
    Write-Warning "Please provide a server name as an argument."
} else {
    $VCServer = Connect-VIserver -server $myVC -credential (Get-Credential $_.username)
    Get-VM -Server $VCServer | Get-Snapshot `
        | ConvertTo-Html -Property created,quiesced,powerstate,`
        @{label = "Note";expression = {If ($_.Description -ne ''){$_.Description}else{"None"}}},vm `
        -Title "VM Snapshots Report" > c:\temp\VMSnapshot.html
}

don’t roll vmware update 2 … yet (updated – fixed!)

if you’ve had the displeasure of applying update 2, here’s what you’re in for.

An issue has been uncovered with ESX/ESXi 3.5 Update 2 that causes the product license to expire on August 12. VMware engineering has isolated the root cause of this issue and will reissue the various upgrade media including the ESX 3.5 Update 2 ISO, ESXi 3.5 Update 2 ISO, ESX 3.5 Update 2 upgrade tar and zip files in the next 36 hours (by noon, August 13, PST). They will be available from the page: http://www.vmware.com/download/vi. Until then, we advise against upgrading to ESX/ESXi 3.5 Update 2.

The Update patch bundles will be released separately later in the week.

The issue is being tracked on KB 1006716 on http://kb.vmware.com/.

WHAT TO DO:

Reference this community article and have them reset your ESX clocks back.

http://communities.vmware.com/thread/162377?tstart=0

The work-around: turn off NTP (if you're using it), and then manually set the date of all ESX 3.5u2 hosts back to 10th of August. This can be done either through the VI Client (Host -> Configuration -> Time Configuration) or by typing date -s "08/10/2008" at the Service Console command line on the ESX hosts.

if it weren’t for the fact that someone is going to be in hell fixing this, it would be pretty funny. imagine if your guests are syncing time with your esx servers. let’s say you set that clock back. guess what? so do your guests… and if you don’t know it, kerberos requires time differences of < 5 mins. that means your clients are no longer capable of authenticating with active directory (unless you set that back, too). of course, if you do that, then all of your users will complain that their clocks are wrong. your emails still stamp funny, etc, etc, etc.

now if you have your guests syncing to ntp or ad or something, you should be in the clear.

 

UPDATE:

We have released the express patches for the product expiration issue. Please go to http://www.vmware.com/go/esxexpresspatches for more information.
Problem:
An issue has been discovered by many VMware customers and partners with ESX/ESXi 3.5 Update 2 where Virtual Machines fail to power on or VMotion successfully. This problem began to occur on August 12, 2008 for customers that had upgraded to ESX 3.5 Update 2. The problem is caused by a build timeout that was mistakenly left enabled for the release build.

The following message is displayed in the vmware.log file for the virtual machine:
This product has expired. Be sure that your host machine's date and time are set correctly.

There is a more recent version available at the VMware web site: http://www.vmware.com/info?id=4.

background information on active directory

i was watching this interesting thread about the history of active directory and its roots going along the activedir.org mailing list. looks like joe captured it here: http://blog.joeware.net/2008/08/11/1420/ if you’re interested in reading it.

while i’m at it, he also posted a link to active directory’s ldap compliance. this is something, i too, lose all the time. so here it is for reference: http://www.microsoft.com/windowsserver2003/techinfo/overview/ldapcomp.mspx

Aug 4, 2008

how to query for slash and backslash in active directory

often times when integrating with other idm solutions or using directory sync or some sort, the other system may not be able to parse the slash or backslash properly. here’s one way to root out where those objects may be residing and what they are. if you want to find objects in AD that may contain a slash (/) or a backslash (\) in the object cn, you can use a simple query like this:

adfind -default -f "(|(cn=*\2f*)(cn=*\5c*))" dn cn

same thing with dsquery, if you prefer that:

dsquery * domainroot -filter "(|(cn=*\2f*)(cn=*\5c*))" -attr distinguishedname cn

you can find this and more in the list of escapable characters at: http://msdn.microsoft.com/en-us/library/aa746475.aspx. don’t miss joe richards’ comment in the community section. :)

and of course, you can find this information in rfc2254. (the msdn list is more complete, oddly.)

Jul 30, 2008

inventory tool for dell update pulled!

in case you missed it, check out this link: http://www.microsoft.com/downloads/details.aspx?FamilyID=92a9bb94-1806-487b-a697-92492bf8cc8e&DisplayLang=en

it seems microsoft has pulled the ITDU scan tool.  we noticed this issue come up dell servers with perc 6 controllers.  if it hadn’t been for one of our mindful development teams, we probably wouldn’t have noticed.  it looked as if scanwrapper.exe was calling perc5.exe to interrogate the controller card.  this was causing errors to pop up in the event log indicating the mismatched condition.

our quick workaround was to stop remove any servers in our collections that had perc 6 listed as the scsi controller in hardware inventory.  seems to run fine on older hardware still.

here are the details from the page:

Please Note:
Effective July 2008 the Inventory Tool for Dell Updates file (SMS2003ITDU_ENU.exe) has been pulled due to an issue that was causing installations to fail. An updated version of the tool will be posted again as soon as it is available.
SMS 2003 Inventory Tool for Dell Updates is an add-on to SMS 2003 Service Pack 1 (SP1) that enables customers to use the SMS 2003 Software Update Management feature to update their Dell servers. Customers will be able to deploy BIOS, firmware, and driver updates to their Dell servers using the same process that they use for deploying security and other updates with SMS.
SMS 2003 Inventory Tool for Dell Update includes the following components:

  • Setup – Windows Installer based setup that allows SMS administrator to install all required components on the SMS site server.
  • Inventory Tool for Dell update (scan tool) – this tool is being built using SDK components provided by Dell Inc. It scans a Dell server for installed and missing updates, just like MBSA scans the computer for Microsoft security updates.
  • Sync tool for Dell update – this tool downloads a catalog from Dell’s website on a recurring schedule. This catalog describes all published Dell updates.
  • Update to Distribute Software Update Wizard (DSUW) – Setup will install an update to DSUW to show new UI that allows to manually import multiple component updates contained within a single system update.
  • Version 3.0 must be installed to coincide with work with the latest Dell catalog.

sql query for top user of a computer

this pulls data from the v_gs_system_console_user table to determine who the top user is.  there’s a drawback to the asset intelligence method of gathering this information that’s presented in the v_gs_system_console_usage table in that if you have applications running as service accounts performing logins, they may show up as the top user.

declare @machine nvarchar(50)
set @machine = 'myComputerName'

select   top 1
         sys.name0 as 'name',
         usr.systemconsoleuser0 as 'user',
         usr.numberofconsolelogons0 as '# logons',
         usr.totaluserconsoleminutes0 as '# mins'
from     v_r_system sys
         inner join v_gs_system_console_user usr
         on sys.resourceid = usr.resourceid
where    sys.name0 like @machine
order by usr.totaluserconsoleminutes0 desc

 

and something for fun, i gathered from this post.  if you want the second most active or top user, then you’d run the query like this:

declare @machine nvarchar(50)
set @machine = 'myComputerName'

select   top 1 *
from     (
         select   top 2
                  sys.name0 as 'name',
                  usr.systemconsoleuser0 as 'user',
                  usr.numberofconsolelogons0 as '# logons',
                  usr.totaluserconsoleminutes0 as '# mins'
         from     v_r_system sys
                  inner join v_gs_system_console_user usr
                  on sys.resourceid = usr.resourceid
         where    sys.name0 like @machine
         order by usr.totaluserconsoleminutes0 desc
         ) as temp
order by [# mins] asc

 

depending on whether you want to track this by the numberofconsolelogons0 field or the totaluserconsoleminutes0 field, you would simply change the order by [field] asc/desc expression.

Jul 24, 2008

cmd shell is not dead: redirection and conditional execution...

sometimes, going back to your roots can be the fastest way to get something done.  i visit robvanderwoude.com so often when i'm looking for things related to batch that i feel like i should be donating to it.  it's the cat's meow.

here's two links to things i use often when dealing with the cmd shell:

this is one of the best gems of redirection that i love.  just using a redirect in a statement will only generate half of the expected output if the application truly writes to all of the available data streams.  that's when you do something like this which will output both stdout and stderr to the same file.

command > file.txt 2>&1

Jul 18, 2008

system center capacity planner and jonathan hardwick

i had the pleasure of going up to redmond for a couple of a days when the capacity planner team had opened up invitations for mvps.  it was pretty fun.  i was there with a couple of exchange mvps and a room full of tap customers.

it looks like the product has matured quite a bit from what i saw back then.  since we’re getting ready to plan for opsmgr, i thought it might be worthwhile to try it out.  anyway, jonathan made his announcement here which i seemed to have missed.  :)  oops.

that’s okay though.  it looks like i’m finding it just in time for me since the opsmgr module was released not too long ago

Jul 16, 2008

monitoring dns [revisited] …

update: i revised this script to add the “debug” parameter because i was having some issues with it reporting inaccurately about looking up domains.  if you set this parameter to true, you’ll find a log called “dns_debug.log” in your %windir%\temp directory.  anyway, i finally got it all fixed.  i went ahead and updated this post along with the post date.

you might recall a previous post about monitoring dns synthetically.  after much frustration with how poorly i wrote it the first time (lacking key things like sleep and retry attempts), i decided to update it a little bit to make it work better.  instead of the previous two parameters, you’ll now need four.

  • HostNames – comma-delimited list of hosts to query (“microsoft.com,google.com,yahoo.com”) or whatever…
  • LogSuccessEvent –boolean value to log successes (very noisy)
  • Repeat – how often to try before determining the query fails
  • Sleep – interval between queries (in milliseconds)
  • Debug – boolean value set to log activity to %windir%\temp\dns_debug.log

 

the event id values are still the same:

  • source – DNS Synthetic Script
  • 41000 – no hostnames defined
  • 41001 – lookup failed
  • 41002 – lookup succeeded

here’s the revised script.  i’ll post it later if you want to just download it.

'==========================================================================
' NAME:        DNS Lookup
' AUTHOR:        Marcus Oh
' DATE:        7/16/2008
' COMMENT:    Returns 'error' if nslookup fails.
'
' 7/2/2008    Updated to include variables for iSleep and iRepeat.
'            Rewrote the logic to loop hosts and lookups.
' 7/16/2008    Added Debug switch to write to log in %windir%\temp called
'            dns_debug.log
'==========================================================================

' Standard Event Type Numeric Values
Const EVENT_TYPE_SUCCESS = 0
Const EVENT_TYPE_ERROR   = 1
Const EVENT_TYPE_WARNING = 2
Const EVENT_TYPE_INFORMATION = 4

Dim iSleep, iRepeat, oLogFile

Set oShell = CreateObject("Wscript.Shell")
sComSpec = oShell.ExpandEnvironmentStrings("%ComSpec%")

' Parameters for MOM
sHosts = ScriptContext.Parameters.Get("HostNames")
bLogSuccessEvent = CBool(ScriptContext.Parameters.Get("LogSuccessEvent"))
iSleep = ScriptContext.Parameters.Get("Sleep")
iRepeat = ScriptContext.Parameters.Get("Repeat")
bDebug = ScriptContext.Parameters.Get("Debug")

If bDebug Then
    sWinDir = oShell.ExpandEnvironmentStrings("%WinDir%")
    Set oFSO = CreateObject("Scripting.FileSystemObject")
    Set oLogFile = oFSO.CreateTextFile(sWinDir & "\temp\dns_debug.log",True)
End If

aHosts = Split(sHosts,",")

If Len(sHosts) > 0 Then
    For Each host In aHosts
        DNSlookup(host)
    Next
Else
    CreateEvent 41000,EVENT_TYPE_INFORMATION,"DNS Synthetic Script","No host names defined."
End If

Function DNSlookup(sHost)
    sCommand = sComSpec & " /c nslookup " & sHost
    
    iSuccess = 0
    For i = 0 To iRepeat
        f_debugLog("Executing command: " & sCommand)
        Set oShellRun = oShell.Exec(sCommand)
        Do Until oShellRun.StdOut.AtEndOfStream
            sLine = Trim(oShellRun.StdOut.ReadLine)
            f_debugLog(sLine)
            NameTag = LCase(Left(sLine,5))
            Select Case NameTag
                Case "name:"
                    iSuccess = iSuccess + 1
                    sData = Trim(Mid(sLine,6))
                    aLine = Split(sLine, ":")
                    sData = Trim(aLine(1))
                    f_debugLog("Successful!  Attempt #: " & iSuccess)
                    Exit Do
            End Select
        Loop
        ScriptContext.Sleep(iSleep)
    Next
    
    f_debugLog("Total successful attempts: " & iSuccess)
    
    If iSuccess > 0 Then
        f_debugLog("Successfully looked up " & sHost & "." & vbTab & "Successful attempts: " & iSuccess)
        If bLogSuccessEvent Then
            CreateEvent 41002,EVENT_TYPE_INFORMATION,"DNS Synthetic Script","Successfully looked up " & sHost & "."
        End If
    Else
        f_debugLog("Lookup failed for " & sHost & "." & vbTab & "Successful attempts: " & iSuccess)
           CreateEvent 41001,EVENT_TYPE_ERROR,"DNS Synthetic Script","Lookup failed for " & sHost & "." & VbCrLf & "Successful attempts: " & iSuccess
    End If
End Function

' Standard Event creation subroutine
Sub CreateEvent(iEventNumber,iEventType,sEventSource,sEventMessage)
    Set oEvent = ScriptContext.CreateEvent()
    oEvent.EventNumber = iEventNumber
    oEvent.EventType = iEventType 
    oEvent.EventSource = sEventSource
    oEvent.Message = sEventMessage
    ScriptContext.Submit oEvent
End Sub

Function f_debugLog(sDebugInfo)
    If bDebug Then
        oLogFile.WriteLine "DEBUG:" & sDebugInfo
    End If
End Function