Scripting Host File Changes

The Windows host file is both a powerful tool and a pain in the posterior. It's powerful because you can do so much with it. Build a poor man's site filter, create shortcuts to local resources, that sort of thing. It's a pain because Windows doesn't want you to edit it. If you've ever done so, then you know the hoops you have to jump through because Windows wants to protect you from dangerous changes to system files. How kind of them.

That said, I have a server, let's call it "server1", that is essential to my work process. Sadly, the address by which I need to reach it changes depending upon why I need it, and where I'm sitting. I've been manually changing the hosts setting for it for a few weeks, but that finally got old and I decided I needed to script that puppy.

I have batch files I use when making the environmental changes necessary for each of my varied tasks. I can now insert a line into them that calls a VBS script to make the host changes I need. I can pass a variable to indicate which setting I need to change. Here is the script in it's entirety.

First, lets assume you have a hosts file that looks something like this:

# Server1 private address
#          server1
# Server1 public address     server1

Your goal with this script is to toggle between the two possible value for this mapping..

The first thing I do is parse the arguments I pass it. For reasons that will make more sense in a moment, I can have either one or two. For purposes of the posting, let's assume I'm passing:

ToggleServer1.vbs enable

The second one is wrapped in an IF clause to avoid an out-of-bound error.

'Read in passed argument(s)
Set args = WScript.Arguments
  arg1 = args.Item(0)  'the parameter from batch comes in here to arg1
If WScript.Arguments.length > 1 Then
  arg2 = args.Item(1)  'the parameter from admin mode is set here
End If

As noted above, the host file is protected, so you need to be in "Administrative" mode to edit. This section checks to see if you have one argument or two. If one, as you would at the start, it adds another one (to keep you from looping through here forever) and then recursively (once) calls this same script, but running as an admin.

'Set to Administrator mode
 Set WshShell = WScript.CreateObject("WScript.Shell")
  If WScript.Arguments.length = 1 Then
    Set ObjShell = CreateObject("Shell.Application")
    ObjShell.ShellExecute "wscript.exe", """" & _
    WScript.ScriptFullName & """" &_
    " " & arg1 & " RunAsAdministrator", , "runas", 1
  End If

This section does exactly what the comment says.

Const ForReading = 1, ForWriting = 2, ForAppending = 8, ReadOnly = 1
Set fso = CreateObject("Scripting.FileSystemObject")
Set WshShell=CreateObject("WScript.Shell")
WinDir =WshShell.ExpandEnvironmentStrings("%WinDir%")

HostsFile = WinDir & "\System32\Drivers\etc\hosts"

These variables correspond to commented and uncommented versions of the host mapping for the two possible addresses.

'Possible map values.  "Remote" means "On VPN or On site". "Off" means commented.
LocalOn = "          server1"
LocalOff = "#          server1"
RemoteOn = "     server1"
RemoteOff = "#      server1"

This part opens the current host file and reads the whole thing into a string variable.

'Open host file for reading
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(HostsFile, ForReading)

'Adds all text in text file to "FileContent" string
FileContents = objFile.ReadAll

This section reads the argument you passed and replaces strings as appropriate. Note the part where check to make sure the "Off" (uncommented) strings are not already present before adding them. They shouldn't but I'm paranoid and I can imagine a scenario where the states get of sync. This just prevent something like a double-comment, which would then forever leave that line commented.

'"disable" = disable local IP, enable remote mapping
'"enable" means enable localIP, disable remote mapping
If arg1 = "enable" then
  'Toggle to RemoteOff and LocalOn
 NewContents = Replace(FileContents, LocalOff, LocalOn)
  If Instr(FileContents,RemoteOff) = 0 then
    NewContents = Replace(NewContents, RemoteOn, RemoteOff)  
  End If       
End If
'make second statemnt independent, in case they get out of sync
If arg1 = "disable" then
  'Toggle to LocalOff and RemoteOn
 If Instr(FileContents,LocalOff) = 0 then
    NewContents = Replace(FileContents, LocalOn, LocalOff)
  End If
    NewContents = Replace(NewContents, RemoteOff, RemoteOn)    
End If
If arg1 <> "disable" AND arg1 <> "enable" Then
  NewContents = FileContents
End If  

That thing above is another bit of paranoia. If you got this far with the wrong argument sent, it would otherwise blank out your host file.

Finally, you write the full contents of your changes into the host file, replacing the previous values.

'Write the change
Set objFile = objFSO.OpenTextFile(HostsFile, ForWriting)
 objFile.WriteLine NewContents
 result=Msgbox("Server set to "&arg1&".",0, "Process Complete!")

Hope that works for you!