IsConnectible: My vbScript Ping Method

Whenever I am doing large sweeps of the network that require connecting to a large number of workstations (e.g. file copy, wmi query, etc.), I prefer to check to see if I can even see the system. This avoids waiting for (WMI) timeouts and also aids in troubleshooting failures. If the file copy failed, why? Well, if I can’t ping it or it can’t be resolved, I would like to know right away and move on to the next host.

Of course, there are a couple downsides to this method. It does add overhead to the script because it too has a timeout. However, depending on the purpose of the script, this may be acceptable for the flexibility you gain. The other caveat is that the systems you run this against must allow ICMP on their local firewall or the script will just ignore them and move on to the next host.

There are several methods for pinging hosts but I’ve found this to be the most reliable since it works against any system that allows ICMP, even Linux or Macs. This is adapted from Richard Mueller’s ping script. This method will return three possible values: “Online”, “No Ping Reply”, or “No DNS/WINS Entry”. You can also tweak the ping command options to your liking.

Here is an example of how to call the function:

Dim computers(2), computer, pingable
computers(0) = "pc-100.domain.local"
computers(1) = "pc-200.domain.local"
comptuers(2) = "pc-300.domain.local"
For Each computer In computers
	Select Case IsConnectible(computer)
		Case "Online"
			wscript.echo computer & " is online"
		Case "No Ping Reply"
			wscript.echo computer & " is offline or firewall blocks ICMP"
		Case "No DNS/WINS Entry"
			wscript.echo computer & " cannot be found in DNS/WINS"
		Case "Host Unreachable"
			wscript.echo computer & " is unreachable"
	End Select
Next

Here is the function:

Private Function IsConnectible(ByVal strComputer)
	' Uses ping.exe to check if computer is online and connectible.
	' Adapted from http://www.rlmueller.net/Programs/Ping1.txt
	Dim objShell, objExecObject, strText
	Set objShell = CreateObject("Wscript.Shell")
	
	' use ping /? to find additional values for ping command; see -n and -w
	Set objExecObject = objShell.Exec("%comspec% /c ping -n 2 -w 750 " & strComputer)
	Do While Not objExecObject.StdOut.AtEndOfStream
		strText = strText & objExecObject.StdOut.ReadLine()
	Loop
	
	If InStr(strText,"could not find host") > 0 Then
		IsConnectible = "No DNS/WINS Entry"
	ElseIf (InStr(strText,"Reply from ") > 0) And (InStr(strText,": bytes=") > 0) Then
		IsConnectible = "Online"
	ElseIf InStr(strText,"Destination host unreachable") > 0 Then
		IsConnectible = "Host Unreachable"		
	Else
		IsConnectible = "No Ping Reply"
	End If
End Function

Microsoft Releases Windows 8 Developer Preview

Microsoft has released the Windows 8 Developer Preview.  This download is a full version of the pre-beta Windows 8 build and is chock full of disclaimers regarding its stability.  Needless to say, I had to download it and give it a shot.  The download (2.8GB to 4.8GB) can be found linked off the front page of the Windows Dev Center.  I decided to download the full version with all the Metro development goodness though there is a lighter version without all the developer tools.

The Windows 8 Preview Guide (PDF) is pretty impressive.  It is a nice, clean overview of Windows 8.  Of course, the net is going to be saturated with info in just a few days now that it is publicly available.  I also highly recommend checking out the Build Keynote which provides some of the eye candy you can look forward to.

I wasn’t as surprised to see ARM support as Microsoft has made it clear it was coming.  I was surprised to see a 32-bit version for download.  I suppose it might be a bit lighter weight (at .8GB < the x64 version) for those just wanting to pull it up in a VM to give it test run.

I was excited when I saw the Live Connect technical preview (site doesn’t work w/ Chrome) until I realized it is only the SDK.

Installing Exchange 2010 Service Pack 1 Fails At Mailbox Role: Database is mandatory on UserMailbox.

In a recent incident, an Exchange server had a complete volume failure during testing. Exchange 2010 was reinstalled but when installing Service Pack 1, it failed upgrading the Mailbox Role. Upon reviewing the log, I found the following line:

Database is mandatory on UserMailbox. Property Name: Database

The error doesn’t explain the problem very well but it is basically saying that there is a UserMailbox without a database, which should never happen. The failure of the volume and subsequent reinstall of 2010 left the arbitration mailboxes (and one or two user mailboxes) orphaned. Most of the suggestions to resolve this problem list doing things like deleting the system mailboxes and running “setup.com /PrepareAD”. After looking around, I was able to parse together a few other options and find a fix.

First, do a search in AD for the System mailboxes and make sure they exist in AD. (If they do not exist, check out this blog.)The three mailboxes are:

  • Discovery – SystemMailbox{e0dc1c29-89c3-4034-b678-e6c29d823ed9}
  • Message Approval – SystemMailbox{1f05a927-xxxx-xxxx-xxxx-xxxxxxxxxxxx} (where x is a random number)
  • Federated E-mail – FederatedEmail.4c1f4d8b-8179-4148-93bf-00a95fa1e042

Next, check out the status of their mailboxes:

Get-Mailbox –Arbitration

For this client, their Discovery and Message Approval mailboxes spat out error messages:

WARNING: The object XXXXXXXX.XXXXX/Users/SystemMailbox{e0dc1c29-89c3-4034-b678-e6c29d823ed9} 
has been corrupted, and it's in an inconsistent state. The following validation errors happened:
WARNING: Database is mandatory on UserMailbox.

The fix is a lovely, one-line powershell. It won’t do anything without prompting you first. To verify it fixes the issue after you run it, take the first half of the command (get-mailbox -arbitration) and run that again to confirm they are online and okay.

Get-Mailbox -Arbitration | Set-Mailbox -Arbitration –Database "Mailbox Database XXX"

Hopefully this saves somebody from causing a bigger mess than necessary. After running this, I was able to install Service Pack 1 just fine. YMMV.

Edit: I found the DiscoverySearchMailbox{D919BA05-46A6-415f-80AD-7E09334BB852} was orphaned as well. To fix this mailbox, I just ran the following PoSH.

Get-Mailbox -Identity "DiscoverySearchMailbox{D919BA05-46A6-415f-80AD-7E09334BB852}" | Set-Mailbox –Database "Mailbox Database XXX"

Exchange 2007+: Aliases have invalid data

Twice in the past two weeks, I have come across Exchange 2003 to Exchange 2007 migrations which went uncompleted. In both cases, I received the following error(s) when trying to view the properties of a recipient with spaces in its alias or when viewing the properties of the offline address book:

  • The properties on have invalid data. If you click OK, default values will be used instead and will be saved if you do not change them before hitting Apply or OK on the property page. If you click cancel, the object will be displayed read-only and corrupted values will be retained. The following values have invalid data: Alias.
  • WARNING: Object has been corrupted and it is in an inconsistent state. The following validation errors have been encountered: WARNING: is not valid for Alias.
  • Set- : is not valid for Alias.

Here is a screenshot of the error:

Exchange 2003 would allow an administrator to put spaces in the Alias attribute. That poses a problem for 2007 which is strict about the characters it allows in this attribute. In Exchange 2007 the following characters are considered valid: Strings formed with characters from a to z (uppercase or lowercase), digits from 0 to 9, !, #, $, %, &, ‘, *, +, -, /, =, ?, ^, _, `, {, |, } or ~. But, no spaces.

Going through you recipients one by one is a daunting task. Here is some code to automate this cleanup. Once you take care of this, you shouldn’t run into it again since the tools in Exchange 2007 won’t let you make the same mistake.

Clean up mailboxes:

Get-Mailbox | Where {$_.Alias -like "* *"} | ForEach-Object {Set-Mailbox $_.Name -Alias:($_.Alias -Replace " ","")}

Clean up public folders:

Get-PublicFolder | Where {$_.Alias -like "* *"} | ForEach-Object {Set-PublicFolder $_.Name -Alias:($_.Alias -Replace " ","")}
Get-PublicFolder -Identity "" -Recurse -ResultSize Unlimited | Foreach { Set-PublicFolder -Identity $_.Identity -Name $_.Name.Trim()}

Clean up contact objects:

Get-MailContact -ResultSize unlimited | foreach {$_.alias = $_.alias -replace 's|,|.'; $_} | Set-MailContact
Get-Contact | Where {$_.Alias -like "* *"} | ForEach-Object {Set-Contact $_.Name -Alias:($_.Alias -Replace " ","")}

Clean up distribution groups:

Get-DistributionGroup | Where {$_.Alias -like "* *"} | ForEach-Object {Set-DistributionGroup $_.Name -Alias:($_.Alias -Replace " ","")}

Check for any objects that still throw errors:

Get-PublicFolder | findstr "Warning"
Get-Contact -resultsize unlimited | findstr "Warning"
Get-Mailbox -resultsize unlimited | findstr "Warning"
Get-DistributionGroup -resultsize unlimited | findstr "Warning"

Rebuild your address lists:

Set-AddressList "All Users" -IncludedRecipients MailboxUsers
Set-AddressList "All Groups" -IncludedRecipients Mailgroups
Set-AddressList "All Contacts" -IncludedRecipients MailContacts
Set-AddressList "Public Folders" -RecipientFilter {RecipientType -eq "PublicFolder"}
Set-GlobalAddressList "Default Global Address List" -RecipientFilter {(Alias -ne $null -and (ObjectClass -eq 'user' -or ObjectClass -eq 'contact' -or ObjectClass -eq 'msExchSystemMailbox' -or ObjectClass -eq 'msExchDynamicDistributionList' -or ObjectClass -eq 'group' -or ObjectClass -eq 'publicFolder'))}

vbScript: Adding and Removing a Domain Group to a Local Group

Yes, I still use vbscript. Someday, I’ll get to work in an environment where everything is upgraded. Until then, I have to rely on the tried and true vbscript.

One of the most common uses of a Group Policy startup script is for adding users to the local admin group. Just google it and you will find hundreds of scripts doing just that, batch files, posh, vbscript, perl, etc. I wrote the script below because I wanted the flexibility to reuse this script at any client and for any group (not just Administrators but Remote Desktop Users or Power Users).

The config section takes three arguements: Action, strLocalGroup, strDomainGroup.

  • Action: Can be either “Add” or “Remove”. It will either add the domain group to the local group or remove it.
  • strLocalGroup: The name of the local group (e.g. Administrators, Power Users, etc.). I tested w/ all the standard built-in groups.
  • strDomainGroup: The name of the domain group to add to the local group. Note: The workstation has to be a member of the same domain the group resides in.

Download the script (zipped): add-remove-domain-group-to-local-group

'======================================================
' VBScript Source File
' NAME: Add/Remove Domain Group to Local Group
' AUTHOR: Andrew J Healey
' DATE  : 2011.07.08
' COMMENT: Will add or remove the domain group specified 
'	  to/from the local group specified.
' USAGE: Modify the config section to match your env. 
'	The "Action" can be "Remove" or "Add"
'======================================================

Option Explicit
Dim strDomainGroup, strLocalGroup, Action

'--------- START CONFIG SECTION ---------
Action = "Add" ' or Remove
strLocalGroup = "Administrators"
strDomainGroup = "Local-Workstation-Admins"
'--------- END CONFIG SECTION ---------

' Enable error handling routine to ensure startup
' script doesn't throw error at users
On Error Resume Next

Dim strDomain, strComputer
Dim objNetwork, objLocalGroup, objDomainGroup

Set objNetwork = CreateObject("WScript.Network") 
strDomain = objNetwork.UserDomain
strComputer = objNetwork.ComputerName

Set objLocalGroup = GetObject("WinNT://" & _
		 strComputer & "/" & strLocalGroup) 
Set objDomainGroup = GetObject("WinNT://" & _
		 strDomain & "/" & strDomainGroup)

' Do Work
Select Case Action
	Case "Remove"
		objLocalGroup.Remove(objDomainGroup.ADsPath)
	Case "Add"
		objLocalGroup.Add(objDomainGroup.ADsPath)
End Select

' Clean up objects
Set objDomainGroup = Nothing
Set objLocalGroup = Nothing 
Set objNetwork = Nothing

Microsoft IT Environment Health Scanner

It's essentially a server for business.
It's essentially a server for business.
Most people have a fear of taking their vehicle to the mechanic for even the simplest tasks just to find out they need their transfunctioner adjusted because of wear to the driver’s side steering widget.  For many in the SMB market, that same fear can be found in their technology investments.  “Did that last IT guy wrap our SQL server in duct tape and tie it to the web server with shoe string?”  The reality is often not that bad, but it can be unsettling not knowing.

The first thing I want to check when preparing any SMB (or large scale Enterprise) for a project is the overall health of their environment.  I typically use a few custom scripts, the invaluable tools from Joeware and Sysinternals, plus a few others offered by various vendors to collect the data for analysis.  The standard services are checked, like DNS, DHCP, WINS (ugh), Exchange, AD, NTP, Group Policy, CAs, etc. for overall health and misconfigurations.  Even with the rich ecosystem of tools, this process can be time consuming so I am always looking for ways to streamline the process.

It seems a lot of others were looking for a quick method as well; at least one that can get a quick assessment of whether you are dealing with a disaster or limited pieces to fix.  The Microsoft Essential Business Server runs a check on the environment to assess its health before installation.  From this Microsoft Technet blog, “[the EBS team] noticed that Preparation Wizard was widely used, not just by customers who were deploying EBS, but anyone with Active Directory in their network who wanted to verify the health of their environment.” This gave birth to the Microsoft IT Environment Health Scanner in the summer of 2009.

This free tool does the basic checks I’m going to do anyway but it gives that quick assessment that often tells me whether I need to dig further. Microsoft’s download page states:

When run from a computer with the proper network access, the tool takes a few minutes to scan your IT environment, perform more than 100 separate checks, and collect and analyze information about the following:

  • Configuration of sites and subnets in Active Directory
  • Replication of Active Directory, the file system, and SYSVOL shared folders
  • Name resolution by the Domain Name System (DNS)
  • Configuration of the network adapters of all domain controllers, DNS servers, and e-mail servers running Microsoft Exchange Server
  • Health of the domain controllers
  • Configuration of the Network Time Protocol (NTP) for all domain controllers

Whether you have a small Microsoft IT environment or are Enterprise large, this tool is great for catching those things often overlooked during setup or changes or misconfigured. Download link: Microsoft IT Environment Health Scanner.

Office 365 Deployment Readiness Tool Beta Released

Office365 Migration Readiness Tool
Office365 Migration Readiness Tool

On Friday (May 6th), Microsoft released the Enterprise Beta Readiness Tool for Office365. As the name implies, this tool is meant to do a quick readiness check on your environment.  Upon execution, it will automatically extract the files to C:office365reskit and launch the app within IE. If you system has IE locked down, this may be problematic or at least require the acknowledgement of a few warnings. As it’s running, it collects information (be patient) from your organization’s network into its temp directory (c:office365reskittmp). The files within this directory are plain text and offer an interesting look into the environment. Unfortunately, once the test completes, the utility analyzes and consolidates these temp files and deletes the originating files, leaving a technical person with much to be desired.

 

The report is long and not filled with a whole lot of technical details. It gives basic information about your organization like sip domains, AD functionality levels, Exchange org, object counts and more. It then compares that to known requirements to measure your readiness to migrate to Office365 and gives a pass/fail grade in each category. For an organization looking at Office365, this info will give a good base to start a discovery process towards Office365 readiness.

My overall impression is the tool is a bit rough around the edges but a useful for quick discovery. The information can even be helpful for doing quick, limited Active Directory, Exchange and OCS/Lync discovery (although there are better tools for this job). Microsoft is showings its commitment to what will likely become a pretty aggressive push to business migration to their cloud offerings.

vbScript: Tweaking Power Settings (disabling hibernate and standby)

As is often the case in IT, when you need to push out that software package or migrate that computer to a new domain, it isn’t on the network.  This has come up several times in the past year and I wanted to share my solution.  Now, this isn’t the “greenest” solution because this will ensure your clients never go into a power saving mode.  However, it can be a temporary fix for a project.  It can also be adapted to force standby or hibernate at specific thresholds.

While Windows 7 and Vista make this task simple, XP is still a reality in most enterprises and SMB’s and therefore, must be taken into account.  The script below does just that.  Some additional examples can be found at the US Government’s Energy Star website.

Note: Copying the script from the webpage may cause formatting issues.  You can download the script here: tweak-power-settings.vbs
Read More

.Net Classes within VBScript: Doing Randomness and Arrays the Easy Way

.Net and VBScript can play together
Back in 2007, the Microsoft Scripting Guys posted a article titled “Hey, Scripting Guy! Be Careful What You Say.” This article changed everything in the way I scripted because it should how simply you can access some .Net classes through COM callable wrappers. The two they focus on are “System.Random” and “System.Collections.ArrayList”.

Set objRandom = CreateObject("System.Random")
Set objArrList = CreateObject("System.Collections.ArrayList")

Read More