Showing posts with label cmd. Show all posts
Showing posts with label cmd. Show all posts

Monday, March 31, 2014

RoboCopy RoboCopy Where For Art Thou RoboCopy

Comparing exit codes with exit codes...

[vbscript: windows 8.1 64-bit]

Set objShell = CreateObject("Wscript.Shell")

cmdstr = "robocopy \\server1\d$\path1 /xo /s \\server2\f$\path1 /xf *.db *.bak"

result = objShell.Run(cmdstr, 1, True)
exitcode = err.Number

wscript.echo "result is " & result
wscript.echo "exit code is " & exitcode 

[/vbscript]

If the [source] or [target] path are bogus (e.g. do not exist), it makes result = 16, but if both are valid, result = 2.  In every case, exitcode = 0.

Why fix something when you can easily order a battalion of coders to just move on to build a new fort?

Sunday, November 4, 2012

Why I'm Still Not 100% on PowerShell

In the past, I have been somewhat critical of PowerShell.  Not because it is somehow technically inferior to alternatives, but because of environmental ramifications.  I was an early adopter actually, having joined up in the Monad testing program, and I was very excited about the potential it offered.  Today, I use PowerShell more than I ever have, but there are still many types of tasks that I don't use it for:

  • Deploying / Installing Software to Remote Computers
  • Maintaining Legacy Script Files
  • Heterogeneous Windows Versions and .NET versions
The main reason I don't use it for software deployments is the slower execution time.  Compared with basic Batch/CMD scripts, or even VBscript, it just takes longer to "spin-up" the .NET and PowerShell foundation goodies before it even parses the script code.  Multiply that by multiple installs per remote computer, and hundreds of remote computers, and the aggregate time difference can be significant.  This is especially true on older hardware and older operating systems, which brings up the point of "pervasiveness" of PowerShell in a mixed environment:  

Many environments I walk into (I'm a consultant after all) are not homogeneous when it comes to operating systems versions, or even "common applications".  It's not unusual to find multiple configurations of .NET, Java Runtime, DirectX, MSXML, Oracle client, and SQL Native Client installations.  One thing I rarely have to contend with is inconsistent support for Windows Scripting Host, let alone the ever-present CMD shell.  I can't say the same for PowerShell.  I wish it were as consistent and pervasive but it's just not.  I'm sure that it will be someday, but we all know how long it takes customers to upgrade to newer operating systems.

To be fair, the same was true for Windows Scripting Host back in the early days of Windows NT.  Microsoft cranked out several versions, until they finally stopped on 5.8 and let it sink in.  That had a nice impact on most shops since they were no longer worried that as soon as they deployed WSH they would have to follow up with another upgrade.  PowerShell is still evolving, so many IT managers aren't over-eager to deploy 3.0 when they seem to expect a 3.1 to come out any day.

I know it's not exactly logical to expect that PowerShell and .NET could be somehow combined and made to be more cohesive (for more streamlined deployment), it would help.  The size of such a deployment package would be excessive for many shops to deal with, and might be tough to deploy on legacy hardware when local disk storage is almost maxed out.  No, it seems Microsoft is betting on customers upgrading to Windows 8 and that would take care of everything as it pertains to achieving a ubiquitous PowerShell presence.  I don't think that's going to happen anytime soon however, for a variety of reasons.  So, for now, while I continue to expand my PowerShell scope, I am still dependent on VBScript and Batch scripts for many tasks.

I'm sure there are some of you out there that will shake your head in disbelief at all this, and that's fine.  I welcome constructive feedback.  So if you have some insights or ideas about how this can be managed more effectively, please let me know?

Tuesday, September 27, 2011

Querying Services on Remote Computers

I'm still amazed that so many up-and-coming admins and SE's knee-jerk to using a script to perform a task that is readily available from a command prompt.  In this case, the SC command.  The SC.exe command resides in the %WINDIR%\System32 folder on most Windows XP, Vista and Windows 7 systems (as well as Windows Server 2003, 2008 and 2008 R2).  It's also alive an well on Windows 8 (Dev Preview build, anyway), and I presume resting within Windows Server 8 as well (I haven't seen it yet).

The syntax is rather basic:

sc <server> <option> <parameters>

but most people use it for querying the local services state, as follows:

sc query

The flexibility comes with the "<server>" argument, which is the NetBIOS computer name (with preceeding \\), so to query a remote computer named DESKTOP1 over your network...

sc \\DESKTOP1 query

And just add a piped "| more" to pause screen-by-screen (or redirect into a file, etc.)

One short and simple command, instead of a bunch of VBScript or even a lengthy Powershell cmdlet.  Nothing wrong with those options, but it's nice to know there are other alternatives.

Remember: To break open this little nugget, open a CMD console and type "sc /?", press Enter and enjoy!

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]

Thursday, July 14, 2011

Scripting and Configuration Manager Packages

Config Manager admins (like Altiris, etc.) are often splitting their time between deploying MSI, EXE application installers as well as custom scripts.  Quite often, and for good reason, they use CMD or BAT scripts to wrap up a sequence of things that would otherwise take longer or require more effort to do within a re-packager like InstallShield.  The problem that I see quite often is forgetting to return a meaningful result code to the Config Manager agent.

For example (BEFORE):

@echo off
msiexec /I “%~dp0myinstaller.msi” /quiet /norestart
cacls “%programfiles%\myApps” /e /t /c /g users:c
reg add HKLM\software\myApps\Fubar /v “Tarfu” /t REG_SZ /d “ABC” /f

If you don’t bother to explicitly return the %errorlevel% value you could be in for a very unhappy surprise.   A little dose of error checking helps a lot.  Here’s (just) one example:

(AFTER):

@echo off
set log=%temp%\myapp.log
echo %DATE% %TIME% msiexec /I “%~dp0myinstaller.msi” /quiet /norestart >>%LOG%
msiexec /I “%~dp0myinstaller.msi” /quiet /norestart
echo %DATE% %TIME% exit code %errorlevel% >>%LOG%
if %errorlevel%==0 (
   goto Configure
) else (
   if %errorlevel%==3010 (
      echo %DATE% %TIME% reboot required (suppressed) >>%LOG%
      goto Configure
   ) else (
      goto Failure
   )
)
goto END

:Continue
echo %DATE% %TIME% doing custom configuration changes… >>%LOG%
cacls “%programfiles%\myApps” /e /t /c /g users:c
reg add HKLM\software\myApps\Fubar /v “Tarfu” /t REG_SZ /d “ABC” /f
echo %DATE% %TIME% installation complete >>%LOG%
goto END

:Failure
echo %DATE% %TIME% installation failed: exit code %errorlevel% >>%LOG%
exit %errorlevel%

:END
exit %errorlevel%

Monday, May 23, 2011

Another AutoCAD 2012 Install Script Spin

Just in case you had any shred of curiosity about how many ways you can deploy software, well, it’s damn near infinite.  Here is yet one more permutation of a script installer for AutoCAD 2012 used with System Center Configuration Manager 2007.

@echo off

rem ****************************************************************

rem  Filename..: setup.cmd

rem  Author....: David M. Stein

rem  Date......: 05/23/2011

rem  Purpose...: install network client of AutoCAD 2012

rem ****************************************************************

CLS

SETLOCAL

SET APPNAME=Acad2012

SET LOG=%TMP%\%APPNAME%_install.log

SET UNC=\\Servername\Apps\Autodesk\Acad2012

echo %DATE% %TIME% installing... %APPNAME%... >%LOG%

echo %DATE% %TIME% source....... %~dps0 >>%LOG%

echo %DATE% %TIME% target....... %COMPUTERNAME% >>%LOG%

echo %DATE% %TIME% WinDir....... %WinDir% >>%LOG%

echo %DATE% %TIME% progfiles.... %ProgramFiles% >>%LOG%

echo %DATE% %TIME% temp......... %TMP% >>%LOG%

echo ----------------------------------------------- >>%LOG%

rem ----------------------------------------------------------------------

rem Check if AutoCAD 2012 is already installed

rem ----------------------------------------------------------------------

if exist "%ProgramFiles%\Autodesk\AutoCAD 2012\acad.exe" (

                echo %DATE% %TIME% info: application is already installed >>%LOG%

                exit 0

)

if exist "%ProgramFiles%\Autodesk\AutoCAD 2012 - English\acad.exe" (

                echo %DATE% %TIME% info: application is already installed >>%LOG%

                exit 0

)

rem ----------------------------------------------------------------------

rem Check if .NET Framework 4.0 is already installed - or install it

rem ----------------------------------------------------------------------

if exist "%WinDir%\Microsoft.NET\Framework\v4.0.30319\clr.dll" (

                echo %DATE% %TIME% info: dotnet framework 4.0 is already installed. >>%LOG%

) else (

                echo %DATE% %TIME% info: installing MS .NET 4.0 client runtime... >>%LOG%

                pushd "%UNC%\AdminImage\3rdParty\NET\4\wcu\dotNetFramework"

                dotNetFx40_Full_x86_x64.exe /q /norestart /lang:ENU

                echo %DATE% %TIME% info: exit code is %errorlevel% >>%LOG%

                popd

)

rem ----------------------------------------------------------------------

rem Install DirectX components

rem ----------------------------------------------------------------------

echo %DATE% %TIME% info: installing DirectX package for acad2012... >>%LOG%

echo %DATE% %TIME% info: command is -- msiexec /i Acad2012DX.msi /quiet /norestart >>%LOG%

pushd "%UNC%"

msiexec /i Acad2012DX.msi /quiet /norestart

echo %DATE% %TIME% info: exit code is %errorlevel% >>%LOG%

popd

rem ----------------------------------------------------------------------

rem Install AutoCAD 2012 deployment

rem ----------------------------------------------------------------------

echo %DATE% %TIME% info: installing AutoCAD 2012 deployment package... >>%LOG%

pushd "%UNC%\AdminImage"

setup.exe /I /Q /W ACAD2012.ini /Lang en-US

popd

echo %DATE% %TIME% info: exit code is %errorlevel% >>%LOG%

rem ----------------------------------------------------------------------

rem The following code is only required if you don't modify the setup .INI

rem to suppress shortcuts during the installation

rem ----------------------------------------------------------------------

if exist "%Public%\Desktop\Autodesk Design Review 2012.lnk" (

                echo %DATE% %TIME% info: cleaning up desktop shortcuts... >>%LOG%

                del "%Public%\Desktop\Autodesk Design Review 2012.lnk" /f /q

                del "%Public%\Desktop\Inventor Fusion 2012.lnk" /f /q

)

if exist "%AllUsersProfile%\Desktop\Autodesk Design Review 2012.lnk" (

                echo %DATE% %TIME% info: cleaning up desktop shortcuts... >>%LOG%

                del "%AllUsersProfile%\Desktop\Autodesk Design Review 2012.lnk" /f /q

                del "%AllUsersProfile%\Desktop\Inventor Fusion 2012.lnk" /f /q

)

echo %DATE% %TIME% info: final exit code is %errorlevel% >>%LOG%

Monday, April 18, 2011

Script Tip of Day: NET HELPMSG

The next time you see an error shot back at you from a script or command console request and wonder what the number means, try using NET HELPMSG X (where X is the number)

For example: net helpmsg 1619 (one of my favorite msiexec errors)…

This installation package could not be opened.  Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package.

(by the way, the most common causes for this irritating error are either a bad path+filename request sent to the msiexec parameter inputs, or the file/folder permissions are blocking access)

No, this isn't a new feature. It's one of those things that's been around forever but everyone forgets about until it's rediscovered.  So, just in case you forgot about it, here you go. Smile

Saturday, February 19, 2011

The %ProgramFiles% Bug, Part 2 / Distrust & Uncertainty

So I already mentioned the bug with VBscript reading the "ProgramFiles" environment variable from Windows Vista and Windows 7 (and corresponding server platforms as well).  Well, I've done some more tinkering and found that it is due to WSH reading environment properties from a different place than CMD, PowerShell and KiXtart read from.  This is still pertaining to 64-bit operating system versions, not 32-bit.  This is very odd and very concerning for anyone still working with VBscript.  After all: If this is looking in the wrong place, what else is?

Examples?

In PowerShell, try both of the following and observe the output…

> dir env:"ProgramFiles"

> dir env:"ProgramFiles(x86)"

In a CMD console, try both and observe the output…

> echo %programfiles%

> echo %programfiles(x86)%

Then create a .VBS script file and drop the following code in and run it…

Set objShell = CreateObject("Wscript.Shell")
wscript.echo objShell.ExpandEnvironmentStrings("%programfiles(x86)%")
wscript.echo objShell.ExpandEnvironmentStrings("%programfiles%")

Yep - this is f***ed up.  Microsoft should release a patch to correct this, but I doubt they will.  Asking everyone to stop what they're doing and convert all of their legacy scripts to PowerShell is not only a bong-smoking delusion, it's also like drinking the bong water and then eating the bong!  I can see this causing a silent havoc on lots of systems around the world that use that environent variable in path resolution and path concatenation operations.  As I said earlier: something this esoteric and fundamental belies a worrisome distrust for VBscript in general.  How could you trust flying in an aircraft if you learn one of the key instruments has a known malfunction but is never going to be fixed?

Given that PowerShell is still not suitable for login scripting, and is still TOO DAMN SLOW to initialize, I will probably start shifting back towards KiXtart for the time being until this is either resolved (not likely) or until Microsoft beats PowerShell into a leaner, meaner execution machine.  (as an aside: my guess is that due to the significant .NET stack that has to be awoken each time the shell is initialized, their "solution" will be to autorun it in the background like so many bloatware vendors do already).

The results of this unchecked bug could be very bad.

uncertainty

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)

Wednesday, December 8, 2010

Useful CMD/BAT Path Expansion Variables

Save the following code into a .CMD or .BAT file and run it in a CMD shell console.  For some more see http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/percent.mspx?mfr=true  The lines in red are only applicable to 64-bit Windows installations.

@echo off
echo displaying various path alias values...
echo %~nx0
echo %comspec%
echo %~d0
echo %~dp0
echo %~dps0
echo %~dpp0
echo %~f0
echo --------------------------------
echo %systemdrive%
echo %systemroot%
echo %windir%
echo %programdata%
echo %programfiles%
echo %programfiles(x86)%
echo %userprofile%
echo %allusersprofile%
echo %temp%
echo %commonprogramfiles%
echo %commonprogramfiles(x86)%
echo %appdata%
echo %localappdata%
echo %public%

Saturday, December 4, 2010

Deploy Registry Updates to Multiple Computers

Challenge:

You need to modify a specific registry key or value on 50, 500 or 5,000 computers on your network.  You need to get it done in the next 15 minutes.  Your boss is standing over you with a can of pepper spray and a taser.  The HR manager has a pink slip ready, the name is not yet filled out.  They are tapping their fingers and breathing heavily.  The pressure is on. …


Solution 1: Use REG.exe and a script

Solution 1 - Steps:

  1. Create a list (text file) containing the names of computers (ex. "computers.txt")
  2. Create a script to read the text file list, and run REG ADD <params> on each computer
  3. Open CMD and run the script

Example Script for Solution 1:

@echo off
FOR /F "tokens=1 delims=," %%G IN (computers.txt) DO (
    @echo %%G
    reg add \\%%G\HKLM\Software\Test /v Vname /d 123 /t REG_SZ /f
)


Solution 2: Use PsExec.exe in a script with a script

Solution 2 - Steps:

  1. Create a list (text file) containing the names of computers (ex. "computers.txt")
  2. Export the registry data from a reference computer (ex. "regdata.reg")
  3. Open CMD and run the following (example code only):

    psexec @computers.txt reg import \\server\share\regdata.reg

Friday, December 3, 2010

REG.exe vs REG.exe

I don't know this never hit me before today, but REG.exe got a shot of steroids after Windows XP.  The changes are awesome and I have become so comfortable with how it behaves on Windows 7 that I forgot that /F wasn't available on XP.  Damn if that isn't a powerful little feature.

WINDOWS XP

C:\>reg query /?
 
Console Registry Tool for Windows - version 3.0
Copyright (C) Microsoft Corp. 1981-2001.  All rights reserved
 
 
REG QUERY KeyName [/v ValueName | /ve] [/s]
 
  KeyName    [\Machine\]FullKey
    Machine - Name of remote machine,  omitting defaults to the current machine Only HKLM and HKU are available on remote machines
    FullKey  - in the form of ROOTKEY\SubKey name ROOTKEY  [ HKLM | HKCU | HKCR | HKU | HKCC ]
    SubKey  - The full name of a registry key under the selected ROOTKEY
  /v  query for a specific registry key ValueName  - The name, under the selected Key, to query if omitted, all values under the Key are queried
  /ve query for the default value or empty value name <no name>
  /s  queries all subkeys and values
 
Examples:
 
  REG QUERY HKLM\Software\Microsoft\ResKit /v Version
    Displays the value of the registry value Version
 
  REG QUERY HKLM\Software\Microsoft\ResKit\Nt\Setup /s
    Displays all subkeys and values under the registry key Setup
 
 
WINDOWS 7
 
C:\ >reg query /?
 
REG QUERY KeyName [/v [ValueName] | /ve] [/s] [/f Data [/k] [/d] [/c] [/e]] [/t Type] [/z] [/se Separator]
 
  KeyName  [\\Machine\]FullKey
           Machine - Name of remote machine, omitting defaults to the current machine. Only HKLM and HKU are available on remote machines
           FullKey - in the form of ROOTKEY\SubKey name
                ROOTKEY - [ HKLM | HKCU | HKCR | HKU | HKCC ]
                SubKey  - The full name of a registry key under the selected ROOTKEY
 
  /v       Queries for a specific registry key values.  If omitted, all values for the key are queried.
 
           Argument to this switch can be optional only when specified along with /f switch. This specifies to search in value names only.
 
  /ve      Queries for the default value or empty value name (Default).
 
  /s       Queries all subkeys and values recursively (like dir /s).
 
  /se      Specifies the separator (length of 1 character only) in data string for REG_MULTI_SZ. Defaults to "\0" as the separator.
 
  /f       Specifies the data or pattern to search for.  Use double quotes if a string contains spaces. Default is "*".
 
  /k       Specifies to search in key names only.
 
  /d       Specifies the search in data only.
 
  /c       Specifies that the search is case sensitive.  The default search is case insensitive.
 
  /e       Specifies to return only exact matches.  By default all the matches are returned.
 
  /t       Specifies registry value data type.
           Valid types are:   REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ, REG_DWORD, REG_QWORD, REG_BINARY, REG_NONE
           Defaults to all types.
 
  /z       Verbose: Shows the numeric equivalent for the type of the value name.
 
Examples:
 
  REG QUERY HKLM\Software\Microsoft\ResKit /v Version
    Displays the value of the registry value Version
 
  REG QUERY \\ABC\HKLM\Software\Microsoft\ResKit\Nt\Setup /s
    Displays all subkeys and values under the registry key Setup  on remote machine ABC
 
  REG QUERY HKLM\Software\Microsoft\ResKit\Nt\Setup /se #
    Displays all the subkeys and values with "#" as the seperator  for all value names whose type is REG_MULTI_SZ.
 
  REG QUERY HKLM /f SYSTEM /t REG_SZ /c /e
    Displays Key, Value and Data with case sensitive and exact occurrences of "SYSTEM" under HKLM root for the data type REG_SZ
 
  REG QUERY HKCU /f 0F /d /t REG_BINARY
    Displays Key, Value and Data for the occurrences of "0F" in data  under HKCU root for the data type REG_BINARY
 
  REG QUERY HKLM\SOFTWARE /ve
    Displays Value and Data for the empty value (Default)  under HKLM\SOFTWARE

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

Monday, November 8, 2010

Chart of the Day: Raising CMD Script Errors

cmd

If I really need to explain this chart, then it means one of the following:

A) You have no idea what I'm talking about anyway

B) You don't care

C) You have no idea and don't care

D) Any of the above

Wednesday, October 13, 2010

Script to Remove AutoCAD LT 2011 (version 2.0)

Another (and better) method for scripting the removal of AutoCAD LT 2011.  Works fine with deployment products like SMS, SCCM, etc.

@echo off
rem ****************************************************************
rem Filename..: remove.bat
rem Author....: skatterbrainz.blogspot.com
rem Date......: 10/13/2010
rem Purpose...: uninstall/cleanup AutoCAD LT 2011
rem ****************************************************************
CLS
SETLOCAL
SET APPNAME=acadlt_2011
SET LOG=%TMP%\%APPNAME%_uninstall.log
echo %DATE% %TIME% uninstalling %APPNAME%... >%LOG%
echo %DATE% %TIME% computer = %COMPUTERNAME% >>%LOG%
echo %DATE% %TIME% source = %~dps0 >>%LOG%
echo %DATE% %TIME% temp = %TMP% >>%LOG%
rem -----------------------------------------------
echo %DATE% %TIME% checking if application was previously installed... >>%LOG%
if exist "c:\Program Files\Autodesk\AutoCAD LT 2011\acadlt.exe" (
echo %DATE% %TIME% application has been installed >>%LOG%
rem --------------------------------------------------
echo %DATE% %TIME% removing Autodesk Material Library 2011... >>%LOG%
msiexec /x {9DEABCB6-B759-4D52-92F8-51B34A2B4D40} /quiet /norestart
echo %DATE% %TIME% result code is %errorlevel% >>%LOG%
rem --------------------------------------------------
echo %DATE% %TIME% removing AutoCAD LT 2011... >>%LOG%
msiexec /x {5783F2D7-9009-0409-0002-0060B0CE6BBA} /quiet /norestart
echo %DATE% %TIME% result code is %errorlevel% >>%LOG%
rem --------------------------------------------------
echo %DATE% %TIME% removing AutoCAD LT 2011 Language Pack >>%LOG%
msiexec /x {5783F2D7-9009-0409-1002-0060B0CE6BBA} /quiet /norestart
echo %DATE% %TIME% result code is %errorlevel% >>%LOG%
rem --------------------------------------------------
echo %DATE% %TIME% checking if ADR 2011 is installed... >>%LOG%
if exist "c:\Program Files\Autodesk\Autodesk Design Review\DesignReview.exe" (
echo %DATE% %TIME% installation was found, removing now... >>%LOG%
msiexec /x {8D20B4D7-3422-4099-9332-39F27E617A6F} /quiet /norestart
echo %DATE% %TIME% result code is %errorlevel% >>%LOG%
) else (
echo %DATE% %TIME% Design Review 2011 was not found. >>%LOG%
)
) else (
echo %DATE% %TIME% AutoCAD LT 2011 was not found on this computer. >>%LOG%
exit /b 0
)
rem --------------------------------------------------
echo %DATE% %TIME% uninstall completed >>%LOG%
ENDLOCAL
exit /b %errorlevel%

Friday, October 8, 2010

Teaching Pigs to Sing (and scripts to make pretty logs)

Today is Friday, and OMFG the weather is increditastical here in sunny Virginia Beach, Virginia!  Going outside feels like having someone rub your scalp and sing pretty songs in your ear.  Yeah, I can’t concentrate on anything else, but… I must.

I had a walk-up request to package MapInfo Pro 10.0 today.  But not just by itself (because that would’ve been too easy, and you can’t have “easy” on a Friday), it had to include another application called “ADAM” and then copy down a whole folder tree of content files and make a shortcut to one of them on the “All Users” Start Menu as well.  It turns out the whole bundle is aimed at installing the “ADAM” software bundle for MapInfo by Deccan International.

The vendor (Pitney-Bowes, and don’t even ask why the **** Pitney-Bowes bought MapInfo.  It makes as much sense as eBay buying Skype, but oh well, onward…) provides a setup.exe, a setup.msi, and a data1.cab file.  They also provide a nice installation guide with tips on running the setup.exe unattended by feeding in arguments using the ancient InstallShield method (you know: setup.exe /v”a bunch of crap here”), but the problem with those is that they’re ill-behaved when strung together in a sequence because they often don’t wait to finish before releasing their “mutex” (go look that one up.  It’s ok.  I’ll wait…).  Are you back?  Ok, so the better option, in most cases, is using the .MSI file and making a transform (.MST) to stuff in the arguments more efficiently.

So, I fired up Wise Package Studio, consumed a coffee, ate a trail mix bar, and made a transform for the installation that inputs the “USERNAME”, “COMPANYNAME”, “PIDKEY” (serial number) and “ACCD” (activation code).  No problem.

You can use Orca instead of Wise Package Studio, or InstallShield, if you prefer.  As long as you can make and edit the .MST it doesn’t matter.

Time to setup the deployment folder structure.  I placed it on a server share named “Packages”, under a root folder named “Deccan”, then copied the MapInfo Pro 10.0 “Disk 1” files into a sub-folder called “MI_PRO\Disk1”.  Then I copied the ADAM installation files under a sub-folder under “Deccan” named “ADAM” and another folder created for “CADAnalyst” under “Deccan”.  The structure looks like this…

\Server\Packages\Deccan\…
-MI_PRO\Disk1
-ADAM
-CADAnalyst

I placed the .MST file into the “Disk1” sub-folder, next to the .MSI file it relates to.

I then created two script files:  setup.bat, and makeshortcut.vbs

Setup.bat simply runs the .MSI with the .MST transform in “quiet” mode.  Then it runs the ADAM .MSI installation, and downloads the data files and makes a shortcut using a call to the makeshortcut.vbs (vbscript) file.  But I also wanted this to generate a self-describing log file so I could backtrace any problems or check on progress, etc.  The “echo” command is simple enough, and I add %DATE% and %TIME% to make a standard “timestamped” log entry output.

However, VBScript uses a completely different format for showing a full date, so I had to beat that into shape with a hammer (see the makeshortcut.vbs script at the end).  I needed the output from both to merge into one log file and look consistent.  That’s just me.  Maybe you don’t care that much, which is fine.  I care about a lot of stupid things.

@echo off
01. rem ****************************************************************
02. rem Filename..: setup.bat
03. rem Author....: Guess Who? (skatterbrainz.blogspot.com)
04. rem Date......: 10/08/2010
05. rem Purpose...: install MapInfo 10 + ADAM + CAD Analyst files
06. rem ****************************************************************
07. CLS
08. SETLOCAL
09. SET LOG=%TMP%\mapinfo10_setup.log
10. echo %DATE% %TIME% initializing script [test_setup] >%LOG%
11. echo %DATE% %TIME% source = "%~dps0" >>%LOG%
12. echo %DATE% %TIME% target = %computername% >>%LOG%
13. echo %DATE% %TIME% tmp = %tmp% >>%LOG%
14. echo ---------------------------------------------- >>%LOG%
15. echo %DATE% %TIME% installing MapInfo Pro 10... >>%LOG%
16. msiexec /i "%~dps0MI_PRO\Disk1\MapInfo Professional 10.0.msi" TRANSFORMS="%~dps0MI_PRO\Disk1\MapInfoPro10_1.mst" /quiet /norestart
17. echo %DATE% %TIME% completed / result code = %errorlevel% >>%LOG%
18. rem ----------------------------------------------
19. if exist "c:\Program Files\Deccan International\ADAM.mbx" (
20. echo %DATE% %TIME% ADAM has already been installed >>%LOG%
21. ) else (
22. echo %DATE% %TIME% installing HPC ADAM ... >>%LOG%
23. msiexec /i "%~dps0ADAM\ADAMsetup.msi" /quiet /norestart
24. echo %DATE% %TIME% completed / result code = %errorlevel% >>%LOG%
25. )
26. rem ----------------------------------------------
27. if exist "c:\Program Files\Deccan International" (
28. if exist "c:\Program Files\Deccan International\CADAnalyst" (
29. echo %DATE% %TIME% the CADAnalyst folder was already created >>%LOG%
30. ) else (
31. echo %DATE% %TIME% downloading data files for CAD Analyst... >>%LOG%
32. xcopy "%~dps0CADAnalyst\*.*" "c:\Program Files\Deccan International\CADAnalyst\*.*" /s /y
33. echo %DATE% %TIME% download complete... >>%LOG%
34. )
35. ) else (
36. echo %DATE% %TIME% error / target folder Deccan International not found! >>%LOG%
37. exit /b 1
38. )
39. rem ----------------------------------------------
40. if exist "%allusersprofile%\Start Menu\Programs\CAD Analyst.lnk" (
41. echo %DATE% %TIME% shortcut already exists >>%LOG%
42. ) else (
43. echo %DATE% %TIME% creating a start menu shortcut for cad analyst .mbx file... >>%LOG%
44. cscript /nologo %~dps0makeshortcut.vbs "N=CAD Analyst" "T=c:\Program Files\Deccan International\CADAnalyst\CAD Analyst.MBX" "L=2" "W=c:\Program Files\Deccan International\CADAnalyst" "D=CAD Analyst" >>%LOG%
45. )
46. echo %DATE% %TIME% installation complete! >>%LOG%
47. ENDLOCAL
48. exit /b %errorlevel%


The %errorlevel% variable is used to capture and display the result code of the last-run command, which is “msiexec” in each case.  Then I force a script “exit” using the /b option and a return code that is non-zero if an error occurs.  If all proceeds along fine and finishes properly, the %errorlevel% result code at the very end should be zero (0).  Most deployment tools see zero as “good” or “no errors occurred”.



Then comes the makeshortcut.vbs script, for making the shortcut.  I could have downloaded some utility .exe to call instead, but I prefer making my own stuff so I know who to blame when it explodes or implodes.



'****************************************************************
' Filename..: makeShortcut.vbs
' Author....: Guess Who? (skatterbrainz.blogspot.com)
' Date......: 10/08/2010
' Purpose...: create a shortcut
'****************************************************************
Dim objArgs, fso, oShell, i, arg, scName, scTarget
Dim scFolder, scCaption, scLoc

Echo "info: makeShortcut.vbs --> processing inputs..."
Set objArgs = WScript.Arguments
If objArgs.Count = 0 Then
Echo "error: incorrect number of arguments supplied"
Echo "-- example..."
Echo "-- makeShortcut.vbs ""N=name"" ""T=target"" ""W=folder"" ""D=description"" ""L=loc"""
wscript.Quit(1)
Else
Echo "info: " & objArgs.Count & " arguments were provided"
Set fso = CreateObject("Scripting.FileSystemObject")
Set oShell = CreateObject("Wscript.Shell")
For i = 0 to objArgs.Count - 1
arg = objArgs(i)
Select Case Ucase(Left(arg,2))
Case "N=":
scName = Mid(arg,3)
Case "L=":
Select Case Right(arg,1)
Case "1":
' all-users desktop
scLoc = oShell.ExpandEnvironmentStrings("%allusersprofile%") & "\Desktop"
Case "2":
' all-users start menu / programs
If fso.FolderExists("C:\ProgramData\Microsoft\Windows\Start Menu\Programs") Then
' check for windows 7 client first
scLoc = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs"
Else
' default to assuming windows xp client
scLoc = oShell.ExpandEnvironmentStrings("%allusersprofile%") & "\Start Menu\Programs"
End If
Case "3":
' current user desktop
scLoc = oShell.ExpandEnvironmentStrings("%userprofile%") & "\Desktop"
Case "4":
scLoc = Mid(arg,3)
End Select
Case "T=":
scTarget = Mid(arg,3)
If fso.FileExists(scTarget) Then
Echo "info: shortcut target has been verified"
Else
Echo "error: shortcut target could not be found!"
wscript.Quit(2)
End If
Case "W=":
scFolder = Mid(arg,3)
If fso.FolderExists(scFolder) Then
Echo "info: shortcut working folder verified"
Else
Echo "error: shortcut working folder could not be found"
wscript.Quit(3)
End If
Case "D=":
scCaption = Mid(arg,3)
End Select
Next
If scFolder = "" or scTarget = "" or scName = "" or scLoc = "" Then
Echo "error: insufficient arguments provided"
wscript.Quit(4)
Else
Echo "validating shortcut parameters..."
Echo "-- name = " & scName
Echo "-- target = " & scTarget
Echo "-- path = " & scFolder
Echo "-- location = " & scLoc
Echo "-- caption = " & scCaption
On Error Resume Next
Set oShortcut = oShell.CreateShortcut(scLoc & "\" & scName & ".lnk")
If err.Number = 0 Then
Echo "info: creating shortcut..."
oShortcut.TargetPath = scTarget
oShortcut.WorkingDirectory = scFolder
oShortcut.IconLocation = scTarget & ",0"
oShortcut.WindowStyle = 1
oShortcut.Description = scCaption
oShortcut.Save
If fso.FileExists(scLoc & "\" & scName & ".lnk") Then
Echo "-- shortcut has been created"
Else
Echo "-- failed to create shortcut!"
End If
Else
Echo "-- access denied to the specified location!"
End If
End If
End If

'----------------------------------------------------------------
' comment: echo runtime progress
'----------------------------------------------------------------

Sub Echo(s)
wscript.Echo DosDate() & " " & s
End Sub

'----------------------------------------------------------------
' comment: format date stamp to match DOS %DATE% %TIME% format
'----------------------------------------------------------------

Function DosDate()
dn = Left(WeekDayName(WeekDay(Now)),3)
mo = PadLeft(Month(Now), "0", 2)
dd = PadLeft(Day(Now), "0", 2)
yr = Year(Now)
x = mo & "/" & dd & "/" & yr
DosDate = dn & " " & x & " " & FormatDateTime(Now, vbShortTime) & ".00.00"
End Function

'----------------------------------------------------------------
' comment: pad string on left side with char to requested length
'----------------------------------------------------------------

Function PadLeft(sVal, ch, max)
Dim tmp
tmp = Trim(sVal)
Do Until Len(tmp) = max
tmp = ch & tmp
Loop
PadLeft = tmp
End Function


Together, these run the installations, copy the files, make the shortcut, create a nice log, save the girl, kill the bad guy, and free the hostages, with rolling credits at the end and sweet theme music.



Now I can check the “mapinfo10_setup.log” under the %TEMP% folder path to see what’s going on.  Note that when you deploy this as a SCCM package, it runs as the local account “NT_AUTHORITY\SYSTEM” (aka “SYSTEM”), so the %TMP% and %TEMP% paths are C:\WINDOWS\TEMP (or %WINDIR%\TEMP).



I’m done.  Beer time folks.  It’s a tough job, but someone’s gotta do it.

Thursday, March 18, 2010

SCHTASKS vs AT vs WMI - We All Lose

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

Wednesday, May 13, 2009

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!

Monday, July 7, 2008

Stupid Scripting Tip 109: IF EXIST (FOLDER)

The CMD shell allows you to write some nifty script code. Not as crazy as VBscript or Kixtart or PowerShell obviously, but still, very useful. Sometimes a BAT/CMD script is the best solution. Actually more often than you'd think.

So, the IF EXIST statement is a very common tool, but you can only test for FILE objects with it, not FOLDER objects. However, thanks to the inherent NUL object, you can! Simply append \NUL on the end of a folder path to test for it using IF EXIST.

IF EXIST C:\FOLDERNAME\NUL GOTO WHATEVER

Works great.