Showing posts with label kixtart. Show all posts
Showing posts with label kixtart. Show all posts

Tuesday, September 27, 2011

10 Ways to Manage Windows Services

  1. Services Console (services.msc)
  2. Command Line: sc.exe
  3. WMI / COM Script: VBscript, KiXtart, Javascript, Perl, etc.
  4. .NET Script: PowerShell
  5. WMI Class Provider: Win32_Service
  6. Group Policy Object
  7. Group Policy Preferences
  8. Utilities: PSService.exe (Sysinternals/Microsoft)
  9. .MSI installer
  10. .EXE application or installer

Tuesday, July 19, 2011

KixTart+VBscript+BAT+PowerShell=zzzzzzzz

Boredom is the sugar-daddy of invention.  Necessity may be the mother, but boredom is the Yang to that Yin.  For example, while sitting around pondering the endless string of bad decisions I've made in life, I decided to build a mouse trap of script code.

KiXtart calls VBscript calls BAT calls PowerShell

You can take this and do whatever you like or ignore it and roll your eyes.  I got the inspiration from a recent discussion with a long-time colleague about a former colleague from back in the 1980's who wrote some insanity for MIT that used LISP to write C code and compiled and ran it based on environment condition tests.  In other words: it wrote the code it needed to suit the environment at that moment.  I really miss working with LISP, eh, ughg, whatever... ok, so there you have it.  Oh, each of the scripts is named "hello.xxx" where "xxx" is the appropriate extension for the language (.kix, .vbs, .bat, .ps1)... fa la la la laaaaaaa...

[CODE]

rem hello.cmd
@echo off
kix32.exe "%~dp0hello.kix"

;; hello.kix
@break on
shell "cscript.exe /nologo hello.vbs"

' hello.vbs
Set objShell = CreateObject("Wscript.Shell")
result = objShell.Run("%comspec% /c hello.bat", 1, True)

rem hello.bat
@echo off
powershell -ExecutionPolicy Unrestricted -File "%~dp0hello.ps1"

## hello.ps1
$a = new-object -comobject wscript.shell
$b = $a.popup("Hello world!",5,"Wasting Time",1)

[/CODE]

Monday, January 31, 2011

Bugs that May Never be Fixed

I'm guessing that you are most likely familiar with Windows Environment variables, since most of the readers here are pretty savvy about Windows stuff.  So, you should be pretty familiar with how 64-bit Windows variables are spread out too…

%programfiles%

%programfiles(x86)%

…and so on.

Well, guess what?  VBscript (WSH), has a little bug regarding how it handles the [programfiles] variable on 64-bit Windows 7.  To demonstrate, try this…

  • Open a CMD console
  • Type SET and press Enter
  • Observe the values for [ProgramFiles], and [ProgramFiles(x86)]
  • Type echo %programfiles% and press Enter.  Observe the output.
  • Now run the following VBscript code and observe the output
Set objShell = CreateObject("Wscript.Shell")
wscript.echo objShell.ExpandEnvironmentStrings("%ProgramFiles%")



Notice anything odd?  While the echo statement returns "C:\Program Files", the script result returns "C:\Program Files (x86)", as will "%programfiles(x86)%".  So how do you get the 64-bit native app install path of "C:\Program Files"?  Well, using the ExpandEnvironmentStrings method - you don't.



And you might expect that you could rely upon the Environment property of the WshShell object, like this…




Set WshSysEnv = objShell.Environment("PROCESS")
wscript.Echo WshSysEnv("ProgramFiles")



But, sadly, it too returns "C:\Program Files (x86)"



And just when you thought that maybe something like KiXtart could ride up on a stallion wearing a shiney suit of armor and save the day with …




Break ON
? ExpandEnvironmentVars("%ProgramFiles%")


Well, think again.

There is one workaround, but it's not very "elegant" in terms of methodology (but it works, hey):




Set objShell = CreateObject("Wscript.Shell")
sysdrv = objShell.ExpandEnvironmentStrings("%SystemDrive%")
progfiles = sysdrv & "\Program Files"




The same approach works for KiXtart and others as well.

Conclusion



Given that Microsoft left VBscript at the wedding altar, sobbing and destitute, and ran off in a stolen convertible to Vegas with PowerShell, I wouldn't expect any changes to this behavior - ever.

Thursday, December 9, 2010

My Favorite Scripting/Programming Languages

Just random blabbering stuff.  These are ranked in order of "most fun" to "least fun" to work with (entirely subjective of course)…

  1. KiXtart 4.6x
  2. PHP 5.x
  3. VBScript / ASP
  4. SQL / T-SQL
  5. Wise Script 8
  6. Javascript
  7. Visual LISP / AutoLISP
  8. BAT / CMD
  9. PowerShell v2
  10. VB.NET

Ordered by what I work with most often:

  1. BAT / CMD
  2. VBScript / ASP
  3. Wise Script 8
  4. SQL / T-SQL
  5. Javascript
  6. PHP 5.x (sad, very sad for me)
  7. PowerShell v2 (this might change soon)
  8. KiXtart 4.6x (oh, the agony!)
  9. Visual LISP / AutoLISP (almost never)
  10. VB.NET (almost never)

Friday, November 12, 2010

MSIEXEC in Multiple Languages

No, not Latin or Swahili.  Enjoy…

# powershell example

write-host "info: installing CatSkinner2010..."
$cmd = "/i \\server\share\folder\CatSkinner2010.msi /quiet /norestart"
$result = (Start-Process -FilePath "msiexec.exe" -ArgumentList $cmd -Wait -Passthru).ExitCode
if ($result -ne 0) {
write-host "error: exit code was "+$result
} else {
write-host "info: success"
}

' vbscript example

wscript.Echo "info: installing CatSkinner2010..."
cmd = "msiexec /i \\server\share\folder\CatSkinner2010.msi /quiet /norestart"
Set oShell = CreateObject("Wscript.Shell")
result = oShell.Run(cmd, 1, True)
If result <> 0 Then
wscript.Echo "error: exist code was " & result
Else
wscript.Echo "info: success"
End If

rem CMD example

msiexec /i \\server\share\folder\CatSkinner2010.msi /quiet /norestart
if not %errorlevel% == 0 (
echo error: exit code was %errorlevel%
) else (
echo info: success
)

; kixtart example

? info: installing CatSkinner2010...
$cmd = "msiexec /i \\server\share\folder\CatSkinner2010.msi /quiet /norestart"
$result = shell $cmd
if @error <> 0
? "error: exit code was "+@error
else
? "info: success"
endif

Thursday, June 17, 2010

Neat-O Windows Admin Tricks: Automating a Help Desk Web Form

Scenario:

Within your Microsoft Windows Active Directory network environment, you have an intranet web site.  That web site serves up a Help Desk web page for users/customers/losers/idiots/crackheads/ to use for requesting things.  It has a form to fill-out, and when they submit the form, it generates an e-mail or stores the info in a database table (and sends an e-mail), etc. whatever.  But the form has a text box for the user to manually type in the name of their computer and their user name and other general contact info.  You'd really like to have that general contact info, and the computer name info, automatically filled in, so that users/losers don't enter stupid or incorrect information.

What to Do:

This scenario involves two (2) general issues that need to be addressed.  Each of these issues can be addressed in a variety of ways.  I'm only covering one (1) way to address each.

Part 1 - Automating the User Contact information

User information is typically stored in Active Directory.  If you (or whomever) did their job properly, each user account object should have all the pertinent information entered, such as e-mail address, phone number, department, first and last name, location info, etc.  AD information is stored and shared via LDAP and ADSI.  You can leverage built-in LDAP and ADSI features within ASP (or pretty much any COM or .NET scripting platform) to access that information.  An ASP web application can use the following code to query Active Directory for user account information.

The catch?  You have to make sure you enable "Integrated" authentication for your intranet web site in order to capture the user account name in the background.  This is silent and automatic if the visitor's use Internet Explorer.  Otherwise, they will be prompted to provide their domain credentials before accessing the web page (username and password).  I will explain the code shown below just below that.



<%

Function UserName()
Dim tmp, domain : domain = "mydomain"
If Session("username") <> "" Then
UserName = Session("username")
Else
tmp = Trim(Request.ServerVariables("REMOTE_USER"))
If tmp <> "" Then
Session("username") = Ucase(Mid(tmp, Len(domain)+2))
UserName = Session("username")
Else
UserName = ""
End If
End If
End Function

%>




Part 2 - Automating the Computer Name information



This is only a tiny bit more complicated, but still pretty easy.  Because capturing the visitor's computer name is not nearly as straightforward, you have to try alternate methods.  One method is to capture the remote IP address, and perform some sort of background lookup to find the computer name.  Messy.  Ugly.  Painful.  Another method is a bit more circuitous, but it works very well: create a desktop (or start menu) shortcut which contains the computer name in the URL parameters.  Consider the following URL which would be requested from a shortcut on the desktop of a user working on computer named "Computer123":



URL: http:// intranet/helpdesk/helpform.asp?cn=Computer123



The small catch here is that you will need to make sure your web form can read the "cn=" parameter and transfer the assigned value (e.g. "Computer123") into the form edit box.  If your web form is ASP or ASP.NET, this is very simple to enable.  The example below shows how to capture this value with an ASP web page:











helpform.asp

<% cn = Trim(Request.QueryString("cn"))%>
<html>
<head>
<title>Help Desk Request Form</title>
</head>
<body>
<h1>Help Desk Request Form</h1>
<form id="user" method="post" name="form1" action="request.asp">
Your Name: <input type="text" name="user" size="20" value="<%=UserName()%>"/>
<br/>
Computer: <input type="text" name="comp" size="30" value="<%=cn%>"/>
<br/>
Details:<br/>
<textarea name="details" cols="55" rows="8"></textarea>
<br/>
<input value="Submit" type="submit" name="btnSubmit"/>
<input value="Reset" type="reset" name="btnReset"/>
</form>
</body>
</html>



There are many "easy" ways to create shortcuts, but they usually come down to using the Wscript.Shell object.  If you want/need to deploy a shortcut to a large number of desktops, you may want to consider scripting it.  You can "blast" out the change by running a script from your desktop or from a server, etc.  Or you can make it part of your login script, or deploy it as a package via Group Policy or SMS, SCCM, Altiris, etc.  Whichever way you deploy it doesn't really matter.  You should be able to automate the capturing of the computer name and use that to concatenate a URL text string to use for creating the shortcut target value.  The examples below show how you can do this within a login script (both VBScript and KiXtart flavors are shown).



Which way (script language, utility, etc.) you choose to go doesn't matter as long as it works.  Once you fetch the computer name, you can take that and concatenate the URL string and make the shortcut for the user to click on to access the Help Desk web form.  The script code below will create a shortcut on the user's desktop using the URL example above.











login.vbs

Function ComputerName()

Dim objShell
Set objShell = Wscript.CreateObject("Wscript.Shell")
ComputerName = objShell.ExpandEnvironmentStrings("%computername%")
End Function


url = "http:// intranet/helpdesk/helpform.asp?cn=" & _
ComputerName()
cap = "Help Desk Request Form"


Set wshShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
strPath = wshShell.SpecialFolders("Desktop")
scPath = strPath & "\" & cap & ".url"
If objFSO.FileExists(scPath) = False Then
Set objShortcutURL = wshShell.CreateShortcut(scPath)
objShortcutURL.TargetPath = url
objShortcutURL.Save
Set objShortcutURL = Nothing

End If




 











login.kix

$url = "http:// intranet/helpdesk/helpform?cn="+@wksta
$cap = "Help Desk Request Form"
$wshShell = CreateObject("Wscript.Shell")
$strPath = $wshShell.SpecialFolders("Desktop")
$scPath = $strpath+"\"+$cap+".url"
if exist($scPath) = 0
$objShortcutURL = $wshShell.CreateShortcut($scPath)

$objShortcutURL.TargetPath = $url

$objShortcutURL.Save()

$objShortcutURL = 0

endif




Putting it All Together



Duct tape, Elmer's Glue, staples, chewing gum, rubber bands… ok, you should now have a "helpform.asp" file sitting on your web server and shared from an IIS site on the server which has integrated authentication configured (do not enable any other authentication options).  Then you would have either a "login.vbs" or "login.kix" login script file.  Target that using Group Policy or simply send a link to the script to one of your test users (aka minion guinea pigs) to test out.  The script should be executed under the user context, not the system context, so it can grab the user information and computer information as well.



Now, when a user logs on, it will create a shortcut on their desktop to "Help Desk Request Form" which contains the URL shown in RED above (except with their actual computer name filled in, instead of "Computer123").



When the user launches the shortcut, it requests the URL along with the computer name.  The ASP form reads the "cn=" parameter to fetch the computer name.  The ASP form also uses the ServerVariables collection to fetch the user account name.  You can modify the **** out of this to suit your needs/whims, etc.  Read the following disclaimer however. – Cheers!



IMPORTANT DISCLAIMER:



Test this in an isolated NON-PRODUCTION environment before attempting to use it in a real production environment.  There is no warranty or guarantee of ANY KIND WHATSOEVER provided for this information, either explicitly or implicitly.  I assume no responsibility or liabilities for any stupid shit dumbass crap, loss of data, downtime, anger, resentment, hostility, depression, angst, mood swings, etc., that may occur as a result of what happens if you don't test it thoroughly before letting it lose on your network.

Thursday, December 3, 2009

My Ideal Scripting Language

I’ve sat around and been pulled into more conversations than I can count regarding what my “perfect” or “ideal” programming or scripting language would look like and how it might work.  So, I thought I’d take a stab at.

It would involve some built-in enumerations for things like operating system platforms.  For example…

iOS_OverPriced = “Apple OSX”
iOS_OverZealous = “Microsoft Windows”
iOS_OverHyped = “Linux”

Functionally, it would probably be somewhere between PowerShell, VBScript and KiXtart with more emphasis on KiXtart.  But it would also include dynamic typing and evaluation from LISP (e.g. cond() and apply() functions for example, but also lambda() and mapcar() … why not?).

The script engine would be self-contained and portable, and would run on any modern OS without any installation required.  Something like how KiXtart runs on Windows already.  The same code syntax regardless of platform, but allow for extensions.  Sound familiar?  I thought so.

No type-defs or declarations required, but optional (like VBScript and KiXtart).  Consistent syntax regardless of noun/verb modal use or situational use.  For example, one example, no requirement to prefix “Set” before instantiating an object.  Because that is just stupid fucktarded 80’s mentality, that’s why.

The syntax structure would be aimed at administrators, not programmers.  KiXtart is aimed at administrators, which I *LOVE*.  PowerShell is aimed at programmers.  I don’t count cmdlets as programming, unless you’re making them, and if you’re making cmdlets, you’re not likely a true administrator, you’re a closet programmer at the very least.

The engine component would support encoding and obfuscation features for code protection as well as compression, using auto-detection for runtime expansion.  There would be a default system log for all scripts unless explicitly overridden by the script itself.

It would support .NET interfaces and invocation, and would be packaged as a DLL or suitable component for OSX, Linux and so on to allow for it to be marshalled or invoked by other processes.

It would include functions for all standard types such as numerics, simple and complex arithmetic, strings and string manipulation, dates and regular expressions.  There would be no function left out from the other languages mentioned above which would result in “going backwards” for a particular functional use.  For example, calculating DateDiff() with PowerShell (don’t even get me started).

I’m sorry, but going from this…

days = DateDiff(“d”, date1, date2)


to this…



$a = New-TimeSpan $(Get-Date) $(Get-Date -month 12 -day 31 -year 2006 -hour 23 -minute 30)
$days = $a.Days

Is mind-numbingly irritating to me.

Oh well, dinner calls.  Gotta run.  More later…

Thursday, November 12, 2009

PowerShell vs KiXtart

Blah blah blah.  I’ve done this one before, but this is different.  Why?  I don’t know.  I don’t care either.  The weather outside is horrible.  Yard is flooded.  Tree limbs down all over the place.  Streets are flooded and limbs, leaves and debris are floating and lodged all over the place.  Anyhow, I was looking at some PowerShell code I’ve been working on and it kicked me in the face how similar it often is to KiXtart script.  Even the import-module aspect being like the include statement.  So, someone hit me with a “but you can load cmdlets and do so much cool stuff” remark.  So?  I can do that with a UDF or included KiXtart script as well.  Same thing.  Both can be obfuscated/encoded for compactness and (quasi) protection.  PowerShell offers signing, which (I don’t believe) KiXtart does, but that’s minor.  I’ve yet to find a large number of script writers signing their work anyway.  Maybe cmdlets are the exception?

Here’s the rub (in my humblest opinion): PowerShell exposes the wonderful world of .NET to your scripting arsenal.  So, why then do such a VAST majority of PowerShell script examples make NO reference to .NET functionality at all??!  Most are simply recoding of other languages to invoke LDAP, WMI/WBEM, ADSI, SQL, etc.  That was my complaint early on in Monad: when will the shift occur to make it obvious what PowerShell can do that the others CANNOT?  Not simply what it can do differently.  Nobody needs different, besides boneheaded brainwashed Apple fans. 

Most script writers (regardless of Mac/PC/Linux/whatever) want efficient more than different. They (we) need something clearly and obviously better.  I don’t want to hit a web site that shows me the same WMI query shit in a new language.  That’s no benefit to me whatsoever.  How about a nice, fat, juicy collection of scripts that (a) make use of the .NET framework on the front end, and (b) aren’t so ridiculously verbose that you shake your head in disbelief that somewhere in the pile of cryptic goo there is a real hidden benefit?  If that exists, please, someone, point me to it?

If the final round bell rang right now, I’d score the fight dead even.  Crowds don’t like a draw.  They want to see a clear winner.

Sunday, October 11, 2009

Why KiXtart is Still Relevant

I write code using quite a few languages and API’s, including VBscript, Javascript, PHP, ASP, BAT/CMD, PowerShell, Wise, and KiXtart.  Ok, so “big deal”.  I’m just one of many, many others that do the same, even those who cover more ground than I.  But in light of my recent blog post about the perceived irrelevance of programming languages based upon age, I wanted to speak up in support of KiXtart.

So, what exactly is KiXtart?

KiXtart is a free scripting language developed by Ruud Van Velson in the Netherlands, along with the support of an enormous network of friends, colleagues, and enthusiasts around the world.  It’s been around since the 1990’s and has grown and matured incrementally ever since.  As of this posting, the current version is 4.61.

To describe it to people who write script but who have never seen it themselves, I usually say this: “It’s like CMD scripting infused with VBscript, REG.EXE, ADSI and the infamous FileSystemObject.  But better.”  With version 4.60 and later, you get a COM interface that allows you to invoke KiXtart from other programming environments.  Want to get the primary group a user belongs to?  Just invoke the @PRIMARYGROUP macro and fetch the result.  Same goes for other macros.  You can find some examples of using the KiXtart COM interface at http://scriptzilla.blogspot.com

Ease of Use

Why “better”?  Because it also includes a very simple, yet powerful collection of “macro” objects, which are essentially pre-configured global variables.  Where many other languages require you to rely on custom functions to return things like computer name, user privilege level, IP address, domain name, cpu or operating system properties, KiXtart macros like @wksta, @priv, @address1, @domain and @cpu, and @producttype hand these bits of information to you like a drive-through fast food pick-up.  This can save you countless hours of work.  It’s true that PowerShell makes many of these chores brief, but not THIS brief, sorry.  The command reference is online and can be downloaded for offline use as well.  Go to http://www.kixtart.org/?p=commandRef

Another major benefit to using KiXtart?  Portability.

KiXtart uses the self-contained KIX32.exe engine to execute and tokenize scripts.  More on tokenizing later.  But unlike VBScript/JScript, which rely on Wscript.exe and Cscript.exe, and PowerShell which relies not only on the PowerShell client, but also on the entire .NET framework stack, KIX32.exe is completely self-contained.  It doesn’t require any client deployments in order to work.  In fact, in most environments that use KiXtart, the KIX32.exe engine is placed in the NETLOGON share of domain controllers along with the scripts and run entirely from there.  No need to copy KIX32.exe or any of the scripts to the client.  Very handy.  Very nice.

Protection

Sure, you can tokenize or Kelvinate/obfuscate script code in many languages, but it’s really simple with KiXtart using the –t option.  It simply encodes your .kix script into a machine-readable form with a .kx extension.  It’s much simple to invoke and requires no certificates or other preparation.

If you can write a BAT/CMD script, or work with VBScript/ASP, you will find it very easy to pick up KiXtart as well.  In addition to scripts posted on the KiXtart web site, there are literally hundreds of web sites with useful code you can take advantage of immediately.  The nice thing to working with a language that’s been around for a long time is the amount of content that accumulates.

Overall – KiXtart is easy to drop into any environment.  It’s easy to learn and use.  It’s portable.  It’s extremely well-supported by the public.  It’s fast, and you can easily encode it for added protection.  If you’re doing any work with logon scripts, or ANY scripting to automated your daily tasks, you should be giving KiXtart a try.

Friday, October 9, 2009

When it’s OK to use Scripting in lieu of Group Policy Preferences

If you’ve been unfortunate enough to have read this blog very long, you already know what a HUGE fan I am of Group Policy Preferences (GPP) in Windows Server 2008 Active Directory.  With Windows 7 it’s an incredibly powerful tool that just happens to be dirt simple to figure out and to use.  I already described how you can replace legacy logon scripts with GPO and GPP settings.  However, I also mentioned that there are situations when it makes better sense to use scripting instead.  I provided a crack-infested comparison chart of this stuff also.  This does not in any way devalue the importance or power of GPP.  I’m just trying to put into perspective the difference of when to use a hammer and when to use a screwdriver.  Each is a worthy tool for its own intended purposes.

Collecting Inventory

I mentioned this in the previous post, but to further clarify: It’s for situations when you do not already have a software product in use that collects this information for you.  If you don’t have something in your environment to automate the chore of collecting hardware and software information from all of your computers, and you don’t have the budget or time (to learn a new tool), scripting is an excellent solution as long as you are ready to dive into scripting.  There are plenty of ready-made scripting alternatives to help you do this, even my own, but you can just as easily build your own with a little time and solitude.

Conflicting Configuration Models

By this, I mean when your intended configuration scheme goes contrary, or perpendicular to, your existing configuration model.  What that fuck does that mean?  A good example is mapping drives and printers for users.  In many environments, this is done by grouping settings against domain group memberships.  Users within a given group are assigned a common set of drive letters and maybe a common set of printers, and so on.  However, it’s also common to see user accounts which are not stored under a common OU in Active Directory.

GPP makes it as simple as breathing to map drives and printers against an OU or a Site.  You can employ WMI Filtering to add some flexibility to this to work with user group memberships, but it’s a little tricky and adds some additional processing overhead as well.  The bigger and more complicated your WMI query (WQL) becomes, the more overhead it incurs as well.

Scripting makes this easy.  However, if you happen to have user accounts organized into OUs in such a way that groups don’t really control your drive mapping requirements, then by all means: go with GPP settings.  It’s the better way to go for that situation.  For other folks who use domain groups to control mappings, but happen to have groups that contain user accounts which reside in different OU’s and overlap with other users in different groups, then you need to focus on using the group membership model first.

Launching Things as the User

Group Policy is great for deploying configuration settings.  It can also tweak the registry, add and edit files, create and update shortcuts, and so on.  But sometimes you want to run a process at logon as the logged-on user.  There is an endless list of reasons why you may want (or need) to do that, but it isn’t at all uncommon.  For these types of tasks, a logon script is the easiest approach.

Conclusion

When I say “scripting”, it doesn’t really matter what you use.  KiXtart, VBScript, Javascript, BAT/CMD, or a roll of duct tape and some KY jelly, whatever.  It just refers to YOU writing a set of instructions to be executed when called upon.  This can be during computer startup, during user logon, called explicitly by double-clicking on a file, or run from a scheduled job.  I suppose you could justify defining a “script” as being an order yelled at a surbordinate as well.

Regardless, overall, in the grandest of grand schemes of network administration in a Windows Active Directory environment: Group Policy and Group Policy Preferences should be your FIRST candidate for implementing any mass-deployment settings.  If you CAN do it with another method, fine, but always research the comparison to see which option gives you the most flexibility, control and easy of use.

Tuesday, September 1, 2009

The Tools at Your Disposal

When I was a sysadmin for a small company I used to get hit with a question quite a lot regarding “how do I…” with respect to automating things or making changes to all computers with least effort.  Microsoft gives us a plethora (I always wanted to use that word in a sentence) of tools and technologies for free, with which we can assemble our own solutions.  The best part of this approach is that not only is it all free, but they provide an insane amount of documentation, samples, and community support to help us understand it better, and solve our own problems.  Of course, Microsoft also sells solutions, but the real fun is building your own.

So, maybe you’re facing a question like this “how do I automatically map drives or printers for my users?” or “how do display a login banner?” or “how do I apply registry updates on computers?” and so forth.  But you don’t know which path to take which would address these questions with the least amount of effort and cost.  It all boils down to what operating systems and tools you have at hand…

Task XP/Vista
WorkGroup
XP
2K3 AD
Vista
2K3 AD
Vista
2K8 AD
Windows 7
2K8 AD
Map User Drives Script Script Script GP Prefs GP Prefs
Map Printers Script Script Script GP Prefs GP Prefs
Desktop Settings Local Policy GPO, Script GPO, Script GP Prefs GP Prefs
Registry Updates Script Script, GPO Script, GPO GP Prefs GP Prefs
Clone Registry Settings Script Script Script GP Prefs GP Prefs
File Cleanups Script Script Script Script Script
Create/Update Shared Folders Script Script Script GP Prefs GP Prefs
Create/Update Files Script Script Script GP Prefs GP Prefs
Create/Update INI Files Script Script Script GP Prefs GP Prefs
Create/Update Folders Script Script Script GP Prefs GP Prefs
Create/Update Shortcuts Script Script Script GP Prefs GP Prefs
Create/Update Env Vars Script Script Script GP Prefs GP Prefs
Create/Manage Power Plans Script Script, GPO Script, GPO GP Prefs GP Prefs
Create/Update Scheduled Tasks Script Script Script GP Prefs GP Prefs
Register File Types Script Script, GPO Script, GPO GP Prefs GP Prefs
Configure Network Settings Script Script Script GP Prefs GP Prefs
Automate VPN Configs Script Script, GPO Script, GPO GP Prefs GP Prefs
Create/Update DSNs Script Script, GPO Script, GPO GP Prefs GP Prefs
Create Local Users/Groups Script Script Script GP Prefs GP Prefs
Collect Inventory Data at Login or Startup Script Script Script Script Script
Customize IE Configuration Script GPO GPO GP Prefs GP Prefs
Customize Office Settings Script GPO GPO GPO/GP Prefs GPO/GP Prefs
Configure Regional Options Script GPO GPO GP Prefs GP Prefs
Configure Local Services Script GPO GPO GP Prefs GP Prefs
Install Apps from Network Shares Script Script Script GP Prefs GP Prefs
Configure Event Fwd-ing Script Script Script GPO GPO

Conclusion

If you have a Windows Server 2008 license in your environment, and at least some Vista or (maybe) Windows 7 clients, you’d be fool to ignore Group Policy Preferences.  The features are amazingly powerful and simple to use.  You can rip out tons of scripting and replace it with a few mouse clicks.  I’m a scripting NUT so if I say something like that it means a lot (to those unlucky enough to know me).  GP Prefs are also nowhere near as complex to learn and use as Group Policy settings.  If you have half of a brain you can figure it out without having to dig into any documentation at all.  It’s that easy.

Caveates and Disclaimers

This table is simply a “best option” recommendation from my own experience.  It is not something developed or condoned by Microsoft in any respect.  You may often find it necessary, even ideal, to mix scripting, GPO and Group Policy Preferences techniques to address your needs.  One solution is not likely going to fit all or even most of your needs.

Items marked with “Script” imply user permissions constraints for login scripts.  For startup scripts, you may encounter some complexity when making changes for specific user profiles, as opposed to the “all-users” profile.  Startup scripts which need to access resources over a network may require additional permissions settings on those remote resources in order to allow access.

Scripting capabilities will vary by language and component dependencies.  Most often KiXtart, VBScript, BAT/CMD or PowerShell. 

PowerShell is not ideally suited for login scripting by itself, however it can be employed along with other scripts to perform specialized tasks with great results.  PowerShell scripting requires all clients involved to have the minimum supported version of .NET framework installed.

To enjoy the comforts of Group Policy Preferences, you simply need to have at least one Windows Server 2008 domain controller in your AD environment.  You do not have to change over to “native” mode.  GP Prefs are built into Windows 7 but require a separate (and free) download and installation for Windows XP and Vista clients.  Settings which only apply to Vista or Windows 7 are completely ignored by Windows XP clients.

All of this can be replaced or extended with the use of infrastructure management tools like SMS, System Center Configuration Manager, and so on.

Thursday, August 20, 2009

Creating Windows Event Log Entries with Scripting

KixTart: LOGEVENT

LOGEVENT (type, ID, message, target, source)

Examples:

$=LogEvent(4, 1, “This is just a user-defined event”, “”, “CustomEvent”)

$=LogEvent(1, 1, “Login script failed on client @wksta”, @LSERVER, “KixTart”)

Limitations:  You can only write to the Application log.
Advantages: You can invoke this without having elevated permissions.  You can submit events to remote computers (if permissions allow)

Windows Vista/7: EVENTCREATE

EVENTCREATE [/S system [/U username [/P [password]]]] /ID eventid
            [/L logname] [/SO srcname] /T type /D description

Examples:

EVENTCREATE /T ERROR /ID 100  /L APPLICATION /D "This is just a user-defined event"

EVENTCREATE /S Server123 /T ERROR /L APPLICATION /D “This is a custom event”

Limitations: It must be invoked with administrative permissions.  It requires the command shell, which is minimal overhead.
Advantages: Can write to all of the event logs (System, Application, etc.).  It can write to local and remote event logs (if permissions allow)

VBScript: LOGEVENT

object.LogEvent(intType, strMessage [,strTarget])

Examples:

Set objShell = CreateObject(“Wscript.Shell”)
cn = objShell.ExpandEnvironmentStrings(“%computername%”)
objShell.LogEvent 1, “This is a custom ERROR message”
objShell.LogEvent 1, “Error on computer “ & cn, “MyServer”

Limitations: It can only write to the Application log
Advantages: Compared to the other options available: none.

PowerShell: WriteEntry

$evtLog = New-Object –type System.Diagnostics.Eventlog –argumentlist Application
$evtLog.Source = “Custom Event”
$evtLog.WriteEntry(“This is a custom Error even”, “Error”)

$cn = [environment]::GetEnvironmentVariable(“computername”)
$evtLog = New-Object –type System.Diagnostics.Eventlog –argumentlist Application, MyServer
$evtLog.Source = $cn
$evtLog.WriteEntry(“Error occurred on $cn”, “Error”)

Wednesday, August 12, 2009

Fetching Twitter Direct Messages Using VBScript or KiXtart

Thanks to the Twitter API documentation (which is lacking examples) and the MSDN reference library on using MSXML/XMLHTTP methods, I’ve put together a simple script to query your Twitter DM inbox and fetch the 20 most recent items (that’s the built-in default of the Twitter API, by the way).  Enjoy!

VBscript Example:
Const username = "MyTwitterUserName"
Const password = "MyTwitterPassword"

Function Twitter_Get_Direct(strUser,strPass)
Dim oXml, strTwitterURL : strTwitterURL = "http://twitter.com/direct_messages.xml"
Set oXml = CreateObject("MSXML2.ServerXMLHTTP.3.0")
oXml.Open "GET", strTwitterURL, False, strUser, strPass
oXml.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oXml.Send()
Twitter_Get_Direct = oXml.responseText
Set oXml = Nothing
End Function

result = Twitter_Get_Direct(username, password)
wscript.echo result


KiXtart Example:



$username = "MyTwitterUserName"
$password = "MyTwitterPassword"

Function Twitter_Get_Direct($strUser,$strPass)
Dim $oXml, $strTwitterURL
$strTwitterURL = "http://twitter.com/direct_messages.xml"
$oXml = CreateObject("MSXML2.ServerXMLHTTP.3.0")
$oXml.Open("GET", $strTwitterURL, 0, $strUser, $strPass)
$oXml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
$oXml.Send
$Twitter_Get_Direct = $oXml.responseText
$oXml = 0
EndFunction

$result = Twitter_Get_Direct($username, $password)
? $result


I could probably post this in PowerShell also, but nobody seems to care, so unless someone asks I won’t bother.  Anyhow: putting this together with the previous examples and blog rants, using a little duct tape and glue, you can assemble a nifty little system for monitoring remote servers using Twitter, as well as sending control commands to the remote server to execute.  That’s right.  You can use the above script on a server to fetch DM messages and process them.  I’ll leave that to your imagination, but it does work and is pretty simple to do.  The only caveate is Twitter availability (not very good) and how robust you want to make the contraption.  Cheers!

Sunday, August 9, 2009

Sending a Twitter Direct Message using VBScript and KiXtart

There are quite a few examples on the web for sending Tweets to your own timeline using scripting and Curl, etc. However, I didn't find a lot of helpful examples on sending direct messages via Twitter using VBscript or KiXtart, so here's what I figured out and it seems to work. For more information on using the Direct_Messages.New API methods, refer to the Twitter API.  Enjoy!

VBScript Version

Const username = "MyTwitterUserName"
Const password = "MyTwitterPassword"
Const recipient = "skatterbrainz"

Function Twitter_Send_Direct(strMsg, recipient)
Dim oXml, strTwitterURL
strTwitterURL = "http://twitter.com/direct_messages/new.xml"
Set oXml = CreateObject("MSXML2.ServerXMLHTTP.3.0")
oXml.Open "POST", strTwitterURL, False, username, password
oXml.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oXml.Send "text=" & strMsg & "&user=" & recipient
Twitter_Send_Direct = oXml.responseText
Set oXml = Nothing
End Function

msg = "Test Message (sent from vbscript)"

result = Twitter_Send_Direct(msg, recipient)
wscript.echo result

KiXtart Version

break ON
$username = "MyTwitterUserName"
$password = "MyTwitterPassword"
$recipient = "skatterbrainz"

Function Twitter_Send_Direct($strMsg, $recipient)
Dim $oXml, $strTwitterURL
$strTwitterURL = "http://twitter.com/direct_messages/new.xml"
$oXml = CreateObject("MSXML2.ServerXMLHTTP.3.0")
$oXml.Open("POST", $strTwitterURL, 0, $username, $password)
$oXml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
$oXml.Send("text=$strMsg&user=$recipient")
$Twitter_Send_Direct = $oXml.responseText
$oXml = 0
EndFunction

$msg = "Test Update (sent from kixtart script)"

$result = Twitter_Send_Direct($msg, $recipient)
? $result


Note the wrapping of arguments with matching parenthesis (…) when using KiXtart.  This is not required for VBScript.  A small but important thing to keep in mind if you use both languages (as I do quite often).  Another “feature” which I have mentioned in previous posts, is that KiXtart supports inline string/variable expansion, so you can bury variables within strings and they work fine, which is nice.  PowerShell and some other languages can do that, but not VBScript.

Thursday, May 28, 2009

Shortcuts to Shortcuts

This is aimed at a fairly narrow niche, but here goes.

Let’s just say you have an intranet web site and you want to put a shortcut on each user’s desktop to the portal.  Easy enough.  Piece of cake.  No-Bake cake actually.  But let’s say you want to capture some information from each user hitting the web site.  Easy as well.  Piece of another cake.  Typically you would turn on integrated authentication and capture the HTTP_USER or REMOTE_USER session variables with IIS, or some equivalent with WebSphere or Apache or whatever you use.

But what if you wan to also collect the name of their computer?  The MAC address?  Whether they have Admin rights or not? 

You can do this with a lot of different scripting tools, but KiXtart provides a few more ready-made widgets to solve this task in less time.

The Ingredients:

  • A Windows-based network with Active Directory
  • Login script (KiXtart, in this case)
  • Liquefied Caffeine (hot or cold, doesn’t matter)

The Code:

Break ON
$scname = "Help Desk"
$url = "http://myintranet.company.com/helpdesk/?uid="+@userid+"&cn="+@wksta+
"&p="+@priv+"&m="+@address+"&d="+@date+"&t="+@time

$objShell = CreateObject("Wscript.Shell")
$path = ExpandEnvironmentVars("%USERPROFILE%")+"\Desktop\$scname.url"

? "creating/updating shortcut..."
$sc = $objShell.CreateShortcut($path)
$sc.FullName = $scname
$sc.TargetPath = $url
$sc.Save()
? "shortcut has been updated"


The time-saver here is the library of built-in "macros" available in KiXtart. These are basically global variables with special names, which expose information without having to paste in a bunch of additional code.



This is evident in the example above, by using the macros @userid (username), @wksta (computer name), @priv (“ADMIN” or ”USER”), @address (MAC), @date (current date in YYYY/MM/DD format), and @time (current time).



The code above will both create and replace the shortcut (if it already exists) each time the user logs on.  This automatically captures their login date and time in the URL of the shortcut.  Arguably a silly effort, but it’s just to show you CAN do this.  Rather than having to instruct users (and God help you!) on how to query their computer name, or installing another script or widget to do that with another mouse click, this takes care of that in one step.  As soon as the user hits the URL, your web page has the computer name, MAC, username, local permissions, date and time, and whatever.  You can also include @producttype (operating system name) @csd (version), @onwow64 (32 or 64 bit) and so on.



As I said, you can do this with VBScript, PowerShell, Perl, Python, and probably even with two sticks and bucket of mud and a case of beer.  But sometimes you need to pick the tool that does the job fastest and easiest.  That doesn’t mean it will be the best tool for all other needs.

Monday, May 18, 2009

Finding Old Files = Old Stuff is New Again

There are a lot of scripts (and canned apps as well) floating about that will find files older than a given date or older than a given number of hours, days, weeks, months, years, decades, centuries, millenium.  Ok, seriously, you can't have any "real" electronic files older than a decade unit of measure.  Can you?

Some of the most succinct examples I've seen are those written by Don Hite.  If you haven't seen Don's site, you should (click here).  Here's one, and another.

Rather than try to "me-too!" this, I thought about dropping this into the blender to find out Will it Blend?  What I mean is: how does it look in different scripting languages.

Task: Identify files in a specified folder older than "x" days.  Either by Date-Created or Date-LastModified.  You have to be careful.  Sometimes, and I still don't know why, you can find situations where Windows reports that a file was Created on a date which is more recent than the LastModified date.  Maybe a time warp?  Who knows.  So I take the most-recent date for comparison.

Let's blend them, shall we?

VBScript Example

strFolder = "c:\temp"
intDaysOld = 60

Dim objFSO, objFolder, objFile, age, age1, age2, note
Dim iCount : iCount = 0
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(strFolder)

Wscript.Echo "scanning " & strFolder
Wscript.Echo

For each objFile in objFolder.Files
age1 = DateDiff("d", objFile.DateLastModified, Now)
age2 = DateDiff("d", objFile.DateCreated, Now)
If age1 < age2 Then
age = age1
note = ""
ElseIf age2 < age1 Then
age = age2
note = "*"
Else
age = age1
note = ""
End If
If age > intDaysOld Then
Wscript.Echo objFile.Name & vbTab & _
objFile.DateCreated & vbTab & _
objFile.DateLastModified & vbTab & _
objFile.Size & vbTab & _
age & vbTab & note
iCount = iCount + 1
End If
Next

Set objFolder = Nothing
Set objFSO = Nothing

Wscript.Echo
Wscript.Echo iCount & " files found older than " & intDaysOld & " days old"


KiXtart Example



Break ON

$strFolder = "c:\temp"
$intDaysOld = 60

Dim $objFSO, $objFolder, $objFile, $age, $age1, $age2, $note
Dim $iCount, $Tab

$iCount = 0
$Tab = Chr(9)

$objFSO = CreateObject("Scripting.FileSystemObject")
$objFolder = $objFSO.GetFolder($strFolder)

? "scanning $strFolder" ?

For each $objFile in $objFolder.Files
$filename = $objFile.Name
$age1 = DaysOld($objFile.DateLastModified)
$age2 = DaysOld($objFile.DateCreated)
If $age1 < $age2
$age = $age1
$note = ""
Else
If $age2 < $age1
$age = $age2
$note = "*"
Else
$age = $age1
$note = ""
EndIf
EndIf
If $age > $intDaysOld
? $objFile.Name + $Tab +
$objFile.DateCreated + $Tab +
$objFile.DateLastModified + $Tab +
$objFile.Size + $Tab +
$age + $Tab + $note
$iCount = $iCount + 1
EndIf
Next

$objFolder = 0
$objFSO = 0

?
? "$iCount files found older than $intDaysOld days old"

;----------------------------------------------------------------
; comment: Calculate Days from DATE to Today
;----------------------------------------------------------------

Function DaysOld($date)
Dim $sc, $result
$sc = CreateObject("ScriptControl")
$sc.Language = "vbscript"
$result = $sc.Eval("DateDiff(" + Chr(34) + "d" + Chr(34) + ", " + Chr(34) + $date + Chr(34) + ", Now)")
$sc = 0
$DaysOld = $result
EndFunction


PowerShell v2 Example



param(
[string]$compare_method = "created"
)

$strFolder = "c:\temp"
$intDaysOld = 60
$tab = [char]9
$backdate = (Get-Date).AddDays($intDaysOld * -1)
$i = 0

# verify a valid compare-method argument value was requested
# default is "created" if none is specified

if (($compare_method -ieq "created") -or ($compare_method -ieq "modified")) {

# determine most-recent age of a given file object

function ShowInfo($file) {
if ($compare_method -eq "created") {
$d = new-timespan $(get-date $file.CreationTime) $(get-date)
if ($file.CreationTime.AddDays($intDaysOld * -1) -le $backdate) {
$file.Name $tab $file.CreationTime $tab $file.Length $tab $d.Days $tab "C"
}
}
else {
$d = new-timespan $(get-date $file.LastWriteTime) $(get-date)
if ($file.LastWriteTime.AddDays($intDaysOld * -1) -le $backdate) {
$file.Name $tab $file.LastWriteTime $tab $file.Length $tab $d.Days $tab "M"
}
}
}

#****************************************************************
# comment: filter files using datestamp age comparison
#****************************************************************

filter FileAge($days) { if (( $_.LastWriteTime -le $backdate -and $_.CreationTime -le $backdate )) { $_ }}

write-host "scanning $strFolder...`n"
if ($compare_method -eq "created") {
write-host "FileName $tab DateCreated $tab Size $tab Age $tab Note"
}
else {
write-host "FileName $tab DateModified $tab Size $tab Age $tab Note"
}
dir $strFolder | FileAge $intDaysOld | foreach-object { ShowInfo $_; $i++; }
write-host "`n$i files were found older than $intDaysOld days"
}
else {
write-host "`ninvalid parameter!"
write-host
write-host "usage: .\list-old-files.ps1 [compare-method]"
write-host
write-host " created - compare files by date-created"
write-host " modified - compare files by date last-modified"
write-host " created is the default (if not specified)"
write-host
}


### Some useful links...


http://powershell.com/cs/blogs/tips/archive/2008/11/20/finding-old-files.aspx


http://www.microsoft.com/technet/scriptcenter/topics/winsh/convert/dateadd.mspx


Code Talk



Most of the code you see here is adapted from what others post on the Internet.  I search and scavenge and assembly my own spin on things to suit (a) my needs and (b) my preferences for format, structure and documentation.  What you see here is about 75 percent discovery and 25 percent adaptation and reformatting.



The PowerShell example warrants a little explanation.  It's very different from the other two obviously.  I had to scrounge around to put that together, and I'm almost certain someone will look at it either laugh hysterically or throw-up all over their screen and keyboard.  Following up with something like "wow, what an idiot!  That can be done with [x] lines of code!"  So, don't blindly accept my work as the end-all solution to a problem.  It's simply meant to be an "example".  One example.  I urge you to explore your own solutions and improve upon anything I spew forth.



Ok, back to the code: First, I establish a date ($backdate) which is [60] days in the past from the current date.  Then I compare datestamps of files against that.  The function ShowInfo() compares the date stamp on the file object with the $backdate value to determine the age and then formats a line of data with tab separation.  The collection is first obtained from the Dir() function.  The output of which is piped directly into the FileAge filter expression to weed out files that do not exceed the specified age limit (60 days).  Then I allow the use of an input argument ($compare-method) to determine whether to calculate the file age by using the CreationTime property, or the LastWriteTime property.



Conclusion



Each of these can easily be ammended, appended, prepended, suspended and stipended (maybe up-ended) to do more useful things.  Maybe delete, rename or move the files it finds.  Whatever.  The point of this exercise is as always to just observe each language next to the other and see how they can each help.  If you work with one language all the time, it's like using the same hammer all the time.



You need to open your tool box and play with the other tools.  Wait, that didn't sound quite right, umm, whatever.  If you have a snippet posted that you'd like to share, drop a comment here and I'll gladly link you up.

Wednesday, May 13, 2009

A Chevy is a Chevy, even in China

Have you ever spent a few minutes watching a TV channel that is entirely in a language other than your own?  Ever notice how in the middle of blabbering away about something, you can still pick out familiar terms like "McDonald's" or "Pepsi" or "Chevrolet"?  That's because some words just don't have a translation, so they use the original in every other language.  It goes both ways.  We say "Latte" and "Kayak" and "Lasagna" even in America.

What does this have to do with scripting or programming? Well, sometimes a piece of code just ends up being the same thing no matter what language you try to hammer it into.  Let's consider the RootDSE object provided by your friendly neighborhood Active Directory interface.  You use it (or should) when you need to query information from AD, such as users, groups, or computers.  It can give you way more than that, but those are just quick examples.

The first step is to bind to the RootDSE object and then you can start asking for information from it.  In this case, I want obtain the various naming context object values, as well as the DNS name.  Let's do this in each language, shall we?  Lets!

VBScript

Set objRootDSE = GetObject("LDAP://rootDSE")
Wscript.Echo "defaultNamingContext = " & objRootDSE.Get("defaultNamingContext")
Wscript.Echo "rootdomainNamingContext = " & objRootDSE.Get("rootDomainNamingContext")
Wscript.Echo "configurationNamingContext = " & objRootDSE.Get("configurationNamingContext")
Wscript.Echo "dnsHostName = " & objRootDSE.Get("dnsHostName")

KiXtart

$objRootDSE = GetObject("LDAP://rootDSE") 
?" defaultNamingContext = " + $objRootDSE.Get("defaultNamingContext")
?" rootdomainNamingContext = " + $objRootDSE.Get("rootDomainNamingContext")
?" configurationNamingContext = " + $objRootDSE.Get("configurationNamingContext")
?" dnsHostName = " + $objRootDSE.Get("dnsHostName")

PowerShell v2.0

$rootDSE = [ADSI]"LDAP://RootDSE"
"defaultNamingContext = " + $rootDSE.defaultNamingContext
"rootdomainNamingContext = " + $rootDSE.rootDomainNamingContext
"configurationNamingContext = " + $rootDSE.configurationNamingContext
"dnsHostName = " + $rootDSE.dnsHostName

As you can see, they are remarkably similar.  There's just not a lot you can do different between them.  The only meaningful difference is that each language provides somewhat more succinct coding than the one above it.  Wscript.Echo becomes ? and then becomes implicit in the last example.  Interesting, but nothing to write home about.  I think I'll stare at this and rub my chin for a few minutes.  Pretend I'm pondering the style of a painting hanging in a museum.

Playing With Fire

That's usually the first thing anyone says to someone when they're answering the question: "What is the 'registry'?"  They say "it's an evil box of destruction that will destroy your computer and all of mankind.  You're playing with fire!"  Ok, I think I actually heard someone say that once.  It was probably me.

Today's walk down destruction lane will focus on the registry.  And because I try to make everyone happy (and end up making nobody happy as a result) will pain the picture with not one, not two, not even three, but FOUR languages!  Amazing!  How the *$(^^*#$ does he do that?!  Pssstt.  Come closer... I have to whisper this.... it's called... Google.

I've been writing scripts for almost 20 years in a variety of languages, but I still look for the easy way out.  That's what scripting is all about: saving time and saving work.  Programmers look to make something awe-inspiring.  Scripters look to make things that free them up to drink more beer and make jokes about programmers.  Scripters also get to the bar earlier and therefore finish off the snacks before the worn-out programmers show up.  You know: while you're compiling, I'm doing something more fun.

Enough of that gibberish.  It'll melt your mind.  Here are four ways to query a registry key and pull all the sub-keys and values beneath it.  They aren't all purely recursive.  Two of them are, two of them aren't.  The two that are can be considered completely cheating, but who cares.  The VBScript and KiXtart examples are one-deep, not exhaustively recursive.  The CMD and PowerShell examples are.  Let's start with the ugly first, but please read the comments at the end as well?

VBScript:

Option Explicit

' Adapted from ActiveXperts registry script library...
' Click Here

Const strComputer = "."

Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_USERS = &H80000003
Const REG_SZ = 1
Const REG_EXPAND_SZ = 2
Const REG_BINARY = 3
Const REG_DWORD = 4
Const REG_MULTI_SZ = 7

Dim StdOut, oReg
Set StdOut = WScript.StdOut

Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strComputer & "\root\default:StdRegProv")

EnumSubkeys HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Services"

Sub EnumSubkeys(hive, parentkey)
Dim subkey, arrSubKeys
Wscript.Echo "PARENTKEY: " & parentkey
oReg.EnumKey hive, parentkey, arrSubKeys
For Each subkey In arrSubKeys
EnumValueTypes hive, parentkey & "\" & subkey
Next
End Sub

Sub EnumValueTypes(hive, strKeyPath)
Dim i, x, ValueName, arrValueNames, arrValueTypes, strValue, arrValues
Wscript.Echo "SUBKEY: " & strKeyPath
oReg.EnumValues hive, strKeyPath, arrValueNames, arrValueTypes
If VarType(arrValueNames) > 8192 Then
For i=0 To UBound(arrValueNames)
ValueName = arrValueNames(i)
Wscript.Echo vbTab & "Value Name.: " & ValueName
Select Case arrValueTypes(i)
Case REG_SZ
Wscript.Echo vbTab & "Data Type..: REG_SZ"
oReg.GetStringValue hive, strKeyPath, ValueName, strValue
Wscript.Echo vbTab & "Value......: " & strValue
Wscript.Echo
Case REG_EXPAND_SZ
Wscript.Echo vbTab & "Data Type..: REG_EXPAND_SZ"
oReg.GetExpandedStringValue hive, strKeyPath, ValueName, strValue
Wscript.Echo vbTab & "Value......: " & strValue
Wscript.Echo
Case REG_BINARY
Wscript.Echo vbTab & "Data Type..: REG_BINARY"
oReg.GetBinaryValue hive, strKeyPath, ValueName, strValue
If VarType(strValue) > 8192 Then
For x = 0 to UBound(strValue)
Wscript.Echo vbTab & "Value......: " & strValue(x)
Next
End If
Wscript.Echo
Case REG_DWORD
Wscript.Echo vbTab & "Data Type..: REG_DWORD"
oReg.GetDWordValue hive, strKeyPath, ValueName, strValue
Wscript.Echo vbTab & "Value......: " & strValue
Wscript.Echo
Case REG_MULTI_SZ
Wscript.Echo vbTab & "Data Type..: REG_MULTI_SZ"
oReg.GetMultiStringValue hive, strKeyPath, ValueName, arrValues
For each strValue in arrValues
Wscript.Echo vbTab & "Value......: " & strValue
Next
Wscript.Echo
End Select
Next
Else
Wscript.Echo vbTab & "Data Type..: " & VarType(arrValueNames)
End If
End Sub




KiXtart:





; Adapted from fEnumKey() by Howard A. Bullock
; Click Here

Break ON

$regkey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services"
$=EnumSubkeys($regkey)

Function EnumSubkeys($parentkey)
Dim $index, $error, $x
$index = 0
Dim $keyname[$index]

If KeyExist($parentkey)
Do
$x = EnumKey($parentkey, $index)
$error = @error
If Not $error And $index > Ubound($keyname)
ReDim Preserve $keyname[$index]
EndIf
If Not $error
$keyname[$index] = $x
$index = $index + 1
$=EnumKeyValues("$parentkey\$x")
EndIf
Until $error
Else
$keyname[0] = ""
Exit 2
EndIf
EndFunction

Function EnumKeyValues($subkey)
Dim $i, $valuename, $v, $vname, $rval, $rtyp
$i = 0
Dim $vname[$i]
? "SUBKEY: $subkey"
Do
$v = EnumValue($subkey, $i)
$err = @error
If Not $err And $i > Ubound($vname)
ReDim Preserve $vname[$i]
EndIf
If Not $err
$vname[$i] = $v
$rval = ReadValue($subkey, $v)
$rtyp = ReadType($subkey, $v)
$i = $i + 1
? Chr(9)+"ValueName..: $v"
? Chr(9)+"Value......: $rval"
? Chr(9)+"Data Type..: $rtyp"
?
EndIf
Until $err
EndFunction




PowerShell:





dir hklm:system\currentcontrolset\services -recurse -ea silentlycontinue




CMD Shell:





reg query HKLM\SYSTEM\CurrentControlSet\Services /s 



Comments:

You might have noticed that the last two examples are just a teensy-weensy bit shorter than the first two.  Just a little.  That's not really a fair comparison.  Comparing the brevity of languages is a slippery slope.  Because they can be described as smoke-and-mirrors in some respects.  Each "language" (or "engine" if you will) comes with a particular (singular or community based) mindset regarding what belongs in the "core" versus what should be left to YOU to add-on.



PowerShell derives much of its potential from cmdlets, which are modular functions which collectively fill-out the PowerShell capability set.  You could do very similar things with KiXtart UDF's (User Defined Functions), which are identical in many respects.  The difference beneath that layer is that PowerShell derives from the .NET services layer, while KiXtart derives from an aggregate of COM and WMI interfaces.  The results almost identical in most respects, but there is only major difference overall: KiXtart is procedural, while PowerShell is object-oriented.  Does that mean that KiXtart is *always* procedural and PowerShell is *always* object oriented?  No.  Cars aren't always cars either.



So, basically, essentially, in other words, and so on, you could produce an almost identical functional expression with KiXtart that you see with PowerShell by simply building (or downloading) a UDF and it would (or could) be as brief and simple.  The REG.EXE command (CMD Shell example) is a compiled, single-purpose executable, rather than a scripting engine or environment.  I included it simply to show that PowerShell is but one way to screw up your registry with the fewest keystrokes.  (A dangerous combination if you drink beer AND a Red Bull in the same hour as you start playing with that.)



As I mentioned in an earlier post, there are trade-offs to each and every "tool" you use.  Some will be faster and easier at some things, but harder to lug around.  A good example of this is CMD shell, which is already pervasive, versus PowerShell, which is still in its infancy of pervasiveness.  Then there's KiXtart which is like a suitcase with a nuclear bomb in it.  Powerful, yet completely portable and fairly lightweight.  PowerShell on the other hand is not lightweight, and requires not only .NET Framework, but it's own installation, in order to crank it up and start using it.  But once it's roots are planted, it rocks.



Well, the goal of this article was to show some simple comparisons and discuss some of the differences between them.  I hope this helps!  Cheers!