Here is a collection of Technical Articles I've written over the last few years. To keep this list short, I've started sorting ideas under similar topics. For example, if you are looking for a script to dump AD account info, check under the script to dump AD groups.
If that isn't working, check out the tech cloud on the bottom of the right side-bar. I try to categorize all my articles by content, script type, etc. This should quickly limit your search from the 100+ items, to just what you are looking for.
I was just introduced to a cool new add-in for Firefox, called GooglePedia. This cool little tool splits Google's search results screen into two windows, the left side containing your search results, the right side containing a Wikipedia screen containing your search string.
If you use Firefox, then take a look, very cool! If you don't, what's wrong? Download Firefox now and get on the bandwagon. The inline spell check of web forms (like blog posts) alone is worth it.
After I got my new PC, the first thing I wanted to do was replace some of the functionality of my dynamic desktop. Primarily this meant a useful Google Search box. My search found 3 gadgets already in play, but to my dismay, they all link to adwords sponsored websites.
So, using Darren's example, I found that gadgets are really just little tiny websites. Hmm, I can code websites. Even really tiny ones.
I would like to present, my basic Google Search Gadget. No encoded text, no adwords, no fancy graphics, or crazy cool search technology. It simply provides a form to start searchs.
Future revisions:
Nowadays, it seems almost everyone has their own website. You can shop for your groceries, order a pizza and even buy a home all from the comforts of your arm-chair. So, why don't you have your own website all about you. I do!
What? No technical expertise? Bah. No dedicated connection to the Internet? Bah. I know, you don't have the slightest idea of where to get started. Ah! That I can help with.
Here are my very easy steps to get you running with your own website in just a couple minutes work. (The hard part comes later, when you try keeping the content fresh!)
So, there's nothing stopping you now. Get creating that new content and let the world know who you are. Leave me a comment here with a link to your site, and if I agree with your content, I'll post it here for others to review. Note: Spammers, I will just delete your comment and not look back.
I just received the latest joke/hoax/chain-mail from a family/friend/co-worker regarding some new issue, scam or sentiment. When this person forwarded along the message, I had to skim through pages upon pages of valid e-mail addresses to get to the actual message, which ended with "forward to all your family and friends".
Thanks to this person, I made $$$ with very little work. How? I extracted out all 200+ (validated and real) e-mail addresses, then forwarded the file off to a website that collects e-mail addresses. (Spammers pay extra for validated e-mail address like these, you know!!)
With a little effort, the friend/family/co-worker could have avoided the pending spam.
Working together we can help decrease the amount of spam that we all get. On the other hand, keep sending me your messages, and I'll keep making a few bucks in the process.
Now that you've been using that computer for awhile now, it is now slowing down, to a crawl, and you don't know what to do.. I've put together this short article to cover some of the basics that I check for with slow computers.
Computer slowness can often be attributed to too many applications running in memory. When you first start up Windows, it has a series of programs that automatically launch at startup. These programs add functionality to your computer, behind-the-scenes. Most are begnign like your anti-virus solution or functional like Google Desktop, but there are also programs on your computer that you might not want.
All viruses have some method of spreading themselves. Since 2000 with the ILOVEYOU virus, viruses have utilized Internet e-mail to deliver to unsuspecting recipients. A virus will attach itself to an e-mail message with friendly looking body, enticing the user to open the message. Once opened, the virus grabs their e-mail systems address list and sends a copy of itself to everyone (spoofing the FROM address on each e-mail). Inside a corporate environment, the virus may go once to everyone in the address list, then again to each of the distribution lists.
To keep from being infected, follow two simple rules. Don't open an e-mail you did not anticipate. "I never get attachments from my uncle, what is this??" and update your local virus scanner at least once a week. There is at least 10 new viruses written each week and any one of these has the potential to do harm. Only 1 each year will reach "TV News worthy".
If you think you are currently infected, check out HouseCall by Trend Micro. It can scan and clean your computer over the Internet. You will need a fairly fast Internet connection. Otherwise, download and install a desktop anti-virus solution. I've listed two in the resources section at the end of this article.
I hope this helps with your computer's slowness.
Resources:
Having recently migrated all of our department to Exchange 2007, we were excited to take advantage of the new conference room functionality! No more Outlook Resource configuration, no more faulty Auto-Accept script! Microsoft has FINALLY programmed in full-fledged resource mailbox functionality into Exchange.
Our dilemna. After migration from Exchange 2003, our resource mailboxes were showing up in the Exchange Management console as LinkedMailbox type.
get-mailbox "resource mailbox" | select RecipientTypeDetails
To convert them to UserMailbox type, Microsoft informed us that we'd need to disconnect the mailbox (from the AD account, then reconnect it. Afterwards, we can run the powershell cmdlet to assign the conference room calendar settings.
Sounds easy enough.
Disable-Mailbox -identity "Resource Mailbox"
Connect-Mailbox -identity "Resource Mailbox" -database (it's db) -user "domain\rsrmbxacct"
Right?? Nope.
My following code, will read a straight list of mailbox names, and convert them from LinkedMailbox type mailboxes, to RoomMailbox type, then configure it as a conference room based on the following criteria.
cls
#The following file contains list of mailbox display names, seperated by a carriage return.
$Rooms = Get-Content -path ".\AllConferenceRooms.txt"
#Set this variable to $true for normal conference rooms
#set this variable to $false for director conference rooms
#---------------------------
$NormalCRoom = $True
$Delegates = @("delegate1@example.com","delegate2@example.com")
#If this will be a 'director-type' conference room.
if ($NormalCRoom -eq $False){
$Delegates += "Delegate3@example.com"
}
foreach ($MyMailbox in $Rooms) {
$MyMailbox = $MyMailbox.Trim()
if (($MyMailbox -ne $null) -and ($mymailbox -ne '')){
#Gathering some information
#---------------------------
$Mbx = get-mailbox $MyMailbox
$MyMailbox = $Mbx.DisplayName
"Working on '" + $MyMailbox + "'"
"-------------------------------------"
$mbxUPN = $mbx.UserPrincipalName
$DB = $Mbx | select database| % {Get-MailboxDatabase -Identity $_.database}
if ($Mbx.RecipientTypeDetails -eq "LinkedMailbox") {
"Linked mailbox - converting to UserMailbox"
Disable-mailbox $MyMailbox -erroraction stop -outvariable Result
#Pausing script while waiting for the Disable-Mailbox command to process.
do {
$isDisabled = get-user -identity $mbxUPN
} while ( $isDisabled.recipienttype -eq "UserMailbox")
#Create a new password for the account based on the UPN field.
"Setting account password to :'"+$mbxUPN + "'"
$pwd = new-object Security.SecureString
$mbxUPN.ToCharArray() | foreach { $pwd.AppendChar($_)}
Set-QADUser -Identity $mbxUPN -UserPassword $pwd
"Reconnecting mailbox to domain account"
## found I needed to enble the AD account to get next step to work.
enable-qaduser -identity $mbxUPN
Connect-mailbox -identity $MyMailbox -database $DB -user $mbxUPN
disable-qaduser -identity $mbxUPN
#Pausing script to wait for AD replication
do {
$isDisabled = get-user -identity $mbxUPN
} while ( $isDisabled.recipienttype -ne "UserMailbox")
$Mbx = get-mailbox $MyMailbox
}
#Convert standard mailbox to conference room resource
if ($Mbx.RecipientTypeDetails -eq "UserMailbox") {
"Converting to Conference Room mailbox"
Set-Mailbox -identity $MyMailbox -Type room
do {
$isDisabled = get-user -identity $mbxUPN
} while ( $isDisabled.RecipientTypeDetails -ne "RoomMailbox")
$Mbx = get-mailbox $MyMailbox
}
#configure conference room.
if ($Mbx.RecipientTypeDetails -eq "RoomMailbox") {
if ($NormalCRoom -eq $true) {
"Configuring the conference room to auto accept meeting requests"
Set-MailboxCalendarSettings -Identity $MyMailbox -AddOrganizerToSubject $true -AllBookInPolicy $true -AllowConflicts $false -AllowRecurringMeetings $true -AutomateProcessing AutoAccept -BookingWindowInDays 366 -Confirm -DeleteAttachments $true -DeleteComments $true -DeleteNonCalendarItems $true -DisableReminders $true -EnableResponseDetails $true -EnforceSchedulingHorizon $true -MaximumDurationInMinutes 720 -AddAdditionalResponse $false -OrganizerInfo $true -RemoveForwardedMeetingNotifications $true -RemoveOldMeetingMessages $true -RemovePrivateProperty $true
} else {
"Configuring the conference room to as a 'Director-type' mailbox"
Set-MailboxCalendarSettings -Identity $MyMailbox -AddOrganizerToSubject $true -AllBookInPolicy $false -AllowConflicts $false -AllowRecurringMeetings $true -AutomateProcessing AutoAccept -BookingWindowInDays 366 -Confirm -DeleteAttachments $true -DeleteComments $true -DeleteNonCalendarItems $true -DisableReminders $true -EnableResponseDetails $true -EnforceSchedulingHorizon $true -MaximumDurationInMinutes 720 -AddAdditionalResponse $false -OrganizerInfo $true -RemoveForwardedMeetingNotifications $true -RemoveOldMeetingMessages $true -RemovePrivateProperty $true -AllRequestInPolicy $true -ForwardRequestsToDelegates $true
}
foreach ($Person in $Delegates) {
"Assigning "+$Person + " mailbox rights on this resource"
Add-MailboxPermission -AccessRights FullAccess -Identity $MyMailbox -User $Person
}
"Assiging "+$delegates + " as Delegates to this resource."
Set-MailboxCalendarSettings -Identity $MyMailbox -ResourceDelegates $Delegates
}
"Completed."
" "
" "
}
}
Sure creating a signature in Outlook is easy. Open Outlook, type in some information, change a few settings and you'll have a decent looking signature. Now, if you want to include a graphic (like a company logo), or use HTML, you have to get a bit more fancy.
That is why I developed this script. This script makes it rather easy to build a fancy signature using various colors, fonts and font sizes. In addition, I have coded functionality to include the graphic (if it is on an external webserver). To make it easier, I have even coded the script to pull information from Active Directory (thanks to a script I found). Drawback being it doesn't work if it cannot access a local domain controller. I am going to work on that. I'd like to use the script at home..
One of my first contract jobs was for a small IT company in the area. I worked with them for about 4 months before a permanent, salaried position came along. (Sorry, I like having someone else pay for the benifits) One of the most memorable things the owner shown me was his background.
Now, he was a very private person, so I don't mean his personal background. I mean the 'desktop' image that he had on his computer. It consisted of virtually hundreds of links to websites and resources that he used regularly. Over the last few years, I have attempted various methods to recreate it. It's a really easy concept, create a webpage, add links to it, and set it as your background.
That brings me to my latest script. First it reads the contents of a special folder, containing several desktop sized images. I have been enjoying the art by this artist.
(These are especially great because he has one specific to my widescreen monitor resolution 1680x1050. ) The script randomly picks one for the background. Next it reads a short CSV file on my local computer containing URLs and titles for various resources I connect to regularly. The script creates links to these resources in the HTML. (They can be several layers deep if you want too! Personally, I put similar resources on the same line (gmail, yahoo mail, etc.) The final step overwrites an existing HTML file on my computer. I run the script as part of my logoff routine, so it's available next time I logon.
Variants of this background file have included live feeds from web cameras, weather maps and even graphs of system performance. I have also added portions to this script to have it set the background to the newly created HTML file, but since it never changes, it isn't quite necessary. Since I am still using a table to layout the page, the latest revision of this script defines the left and right columns using the screen's actual width, not percentages.
My wife and I were recently considering the purchase of a new home in our area. We went through a million what-if scenarios about the new home, laying out the pros and cons of the house, or current place and all the financial concerns involved.
While running all the numbers, I was constantly faced with numerous calculations. Going out to a mortgage calculator wasn't always the favorable solution. That is why I worked up this Excel spreadsheet to calculate various scenarios (what if our house sold of 10% more? What if we qualified for a 45year fixed loan? what if we paid off the car? )
The attached spreadsheet is the result of that process. You will need to populate the various 'yellow' fields with:
Our final deal-breaker on this house was a suggestion from a Realtor that we trust. She suggested waiting for the local market to improve so that our house would sell for a bit more. That would help us put down a larger down payment on the new place and lower the monthly bills.
One thing every corporate employee has to deal with is email. Go on vacation, and you get tons of it. Stay home with a sick kid, and you get a ton more. The work does not stop, even if you can't go. In turn, we either don't take time off, even when needed, or we read our email all the time (Curse you Blackberry!).
As an IT administrator, I would get alerts. Lots of them. Backup alerts, server alerts, email slowness alerts, spam filter alerts, and even alerts that the alerts didn't work! To combat this ever growing corporate 'spam', I created a simple set of rules in Outlook to manage them.
Using Outlook's own auto-archiving functionality to clean-up messages, I was able to maintain a mailbox well within the corporate standards. The attached PDF document details how to duplicate these settings.
Note: A good portion of performance of Outlook in an Exchange environment depends on the local configuration and settings of the Outlook client. Check out this wonderful document created by a former co-worker regarding various settings Microsoft Support recommended.
It can be found here.
I had an interesting request. A user wanted to search their mailbox for all read/not read/delivery/non-delivery receipts in their mailbox. You would think that it should be as easy as searching for "READ:" in the subject line. Unfortunately, Outlook does a wild-card type search on what you enter. This means you may get read:, bread: and read:s.
What about using forms? Based on my previous Outlook-form-coding experience, I knew that Outlook used different standard forms for the various types of messages. IPM.Note for email messages, IPM.Contact for (well, gee) contacts, etc.. As long as the message didn't traffic the Internet, the default form should be used. So, what is the default form for Read Receipts??
What form? Opening up a read receipt, I checked the properties tab. Here I found that the message type was report. Cool, now I went back to my query and tried to search on the form type of "report", but when you add the form type "report" to your search query, it returns NONE as the options. Now I know that this means there are no readable fields on this form.
Final solution: Doing a quick Google search on Message Class, I determined that this was the field I needed. Common message classes are IPM.Note and IPM.Contact. So, I created a query for all messages, where the message class contains report.
My search folder returned several read receipts and an undeliverable response in my Inbox. I have not tested it with other response type messages, but believe it will find all message responses. Meeting responses will probably require a slightly different query.
During my work day, I probably send a 20-30 email messages. Each time I typically look up someone's email address, copy it to the (keyboard) buffer (CTRL+C), click on a shortcut I have to open a new email, then paste the email address into the new message. I thought, what if my new email shortcut, would test the keyboard buffer and then automatically paste the address for me?
Save the following VBS to your local computer. Drag it into your quick launch bar, then change the icon to something useful (like Outlook's new message icon)! One click new emails that will be pre-populated with the last email address you copied.
'==========================================================================
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 3.1
'
' NAME: Send_Email.VBS - Read the keyboard buffer (aka clipboard)
' and send an email if read email address.
'
' AUTHOR: eric Woodford
' DATE : 2/13/2008
'
' COMMENT:
' clipboard idea: http://forums.vandyke.com/showthread.php?t=597
' mailto: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2149800&SiteID=1
'==========================================================================
Set objHTML = CreateObject("htmlfile")
ClipboardText = trim(objHTML.ParentWindow.ClipboardData.GetData("text"))
Set objHTML = Nothing
if instr(ClipboardText,"@") and instr(clipboardtext," ")=0 Then 'possibly a email address
Set objShell = WScript.CreateObject("shell.application")
str = "mailto:" & ClipboardText
objShell.ShellExecute str, "", "", "open", 1
Else
Set objShell = WScript.CreateObject("shell.application")
str = "mailto:"
objShell.ShellExecute str, "", "", "open", 1
End If
set objshell = nothing
Back when my daughter was born, we were taking pictures of everything. "What a cute smile!" (click) "Oh she is biting that toy" (click) "She's finally asleep!" (click) (click) (click) (click) (click). This ended up to be one roll of standard 35mm film each day. Two months later, we were spending more on film developing, than on diapers! We started doing research on the multitude of options. The day before my sister-in-law's wedding in June 2002, we bought a Sony digital camera.
This camera, along with a 128mb memory stick (MS) allowed us to take approximately 60 pictures at a time. During the wedding, one of us would run upstairs in the hotel, download pictures, and come back. If I remember right, we took close to 600 pictures this way. According to family members, some of our pictures turned out better than the professional photographers. :) We were able to share our pictures out on Ofoto, and anyone could view them within a few days after the wedding. (The photographer took almost 2 months.) Plus, they could order copies for themselves, and we did not have to pay for the processing!!
Now 3 years later, we still have that camera, and have invested in second, smaller Sony camera (DSC-W5). This camera, has 32mb on-board and can use the Sony PRO Memory Sticks! With our 4GB PRO memory stick, we can take 1,660 (5 megapixel) pictures on a single outing! We never transfered pictures to the laptop the entire 8-days in Maui (over 750 pictures and videos). Additionally, the camera and 4Gb MS allow us to record up to 3 hours of continuous DVD quality video! No longer need to bring along our bulky Mini-DV video camera either!!
Unfortunately with digital cameras there are a number of technical considerations. If my computer where I am storing all the images dies, catches fire, or is stolen, I will lose 3 years of pictures. Also, with the simplicity of taking pictures and considering the low cost of viewing them, we now take multiples of each image (horizontal, vertical, with smiles, with a pose, candid, standing in front of the car, behind the car, etc.). In essence, there is virtually no limitations to how many pictures we can take on a single outing.
Technically, what does all this mean?
To use the attached file, you will need a unZIPing program like WinZip or 7-Zip. Open the file and extract the two files to your My Pictures folder. I'd suggest creating a shortcut on your desktop. (Hold down right-mouse button, drag to your desktop, release file and select "Create Shortcut".)
Copy and paste the following code into a file called MoveFiles.BAT. You will need to edit the script to reference the drive your memory stick connects to. Change the line Set MyMS= to point to your drive.
@echo off
Set MyMS=I:
call :GETDATE
Echo Transfering Pictures to:
Set FILEPATH="c:\
If %OS%==Windows_NT Set FILEPATH="%HOMEDRIVE%%HOMEPATH%\My Documents\
Echo ------ %FILEPATH%
c:
cd\
cd %FilePath%\My Pictures"
IF Not exist %YEAR%\ MD %YEAR%
CD %YEAR%
If not exist %TODAY%\ Md %TODAY%
cd %TODAY%
Start explorer %FILEPATH%My Pictures\%YEAR%\%TODAY%\"
dir %MyMS%\*.jpg /b /s > c:\pictures.txt
for /f %%a in (c:\pictures.txt) DO move %%a c:
if not exist c:index.htm copy "%HOMEDRIVE%%HOMEPATH%\My Documents\my pictures\index.htm" c:
for /f "delims=\ tokens=3,4,5" %%a in (c:\pictures.txt) DO Echo %%b >> index.htm
GOTO :VIDEOS
:GETDATE
REM Function to set TODAY equal to todays date in MM.DD.YYYY format
set GETDATE=%DATE%
set DOTDATE=%GETDATE:/=.%
set TODAY=%DOTDATE:~4,10%
Set YEAR=%TODAY:~6%
goto :EOF
:theend
cls
echo nothing to move
echo.
Goto :Done
:VIDEOS
Set Movies=%FILEPATH%\My Videos\"
cd %Movies%
IF Not exist %YEAR%\ MD %YEAR%
CD %YEAR%
Md %TODAY%
cd %today%
REM Also need to modify this line to use your correct memory stick location.
dir %MyMS%\*.mpg /b /s > c:\videos.txt
for /f %%a in (c:\videos.txt) DO move %%a c:
Echo Complete
:finished
explorer %FILEPATH%My Pictures\%YEAR%\%TODAY%\
notepad %FILEPATH%My Pictures\%YEAR%\%TODAY%\index.htm"
:Done
The attached VBScript is a work in progress. It needs a file set up on your removable media to read from. Then you can run the script to move files either direction. I use the same script to move MP3s onto my SD Ram chip.
I was just reading through another news article about the fires in Southern California. The article was discussing the displaced families that were returning home for the first time. The focus family found that they had unfortunately returned to a burnt out shell of a home.

Among the possessions the a 56-year-old photographer lost were his transparencies, melted inside a fire-resistant box, and a photograph of his father.
"I've lost my history," Sanders said. "All the work I've done for the past 30 years, it's all destroyed."
- Yahoo News
This got me thinking. First, my computer has every digital picture (over 5 years and 20gb) ever taken of my kids on the hard drive. Secondly, a fire-safe box is not going to protect a stack of DVDs. What if my house burnt down while I was at work. I have visions of heroically racing into the den, grabbing my PC and running out of the house, but it is unlikely that my house will be burning while I am at home. "Hey, what's that burnning smell and why are the fire alarms going off? I am not cooking..."
Digital Disaster Recovery for the Home. With this, I have been implementing a backup methodology similar to those implemented in a corporate environment. This includes backing up all my digital files to removable media then shipping them to relatives. I currently have two stacks of 8 DVDs with 4GB of pictures and videos on each. Approximately every 2 months, I create 2 new DVDs containing the new pictures taken (one for me, one for off-site).
Next steps? A digital inventory of the house. As I stated earlier, digital images are virtually free to take and easy to keep. I plan to start taking pictures of household items, and their serials. I plan on starting with the electronic items (like the TV, TiVo, and digital camera). Then take pictures of the kitchen table and chairs (all wood), the bedroom set (also wood), the sofa and love seat, and well just about everything.
Insurance will cover replacing the material items. I can buy myself a new computer, stock full of all the applications I use. Unfortunately, there is no amount of money that can replace the happy moments we've recorded.
In the last 10 years, email has evolved from the uber-geeky tool into the corporate mainstream solution that no one can live without. At the base, Internet email is a 40+ year old technology that was originally designed to provide a reliable (not time sensitive) solution for communications between colleagues. As it has gained popularity corporations have started to rely on it for business critical communications with their customers and vendors. Nowadays, IT departments learn about their outages by how well the email solution is performing.
In the rest of this article, I plan on covering a few of the troubleshooting techniques I use to resolve Internet email issues and what may have caused them.
The Parts of an Email
Every email message delivered across the Internet has 2 basic parts, a header and data. The header consists of the delivery information (To, From) and routing information (what servers processed the message when). The data portion consists of the subject, message body and any attachments. (Note: Since Internet email cannot directly handle files, these attachments are [w:uuencoding|converted] to text and placed inline with the message body. This encoding process can add an additional 30% to the attachment size when completed.)
Message Delivery
The delivery of an email message is not obvious. A message may bounce around a dozen times (or more) before it is delivered to the intended recipient. This entire transaction can be found in the message headers.
Here is a great graphic from Wikipedia:

1: Delivered-To: bob@b.org
2: Received: by 10.1.2.15 with SMTP id ;
Mon, 29 Jan 2007 13:55:16 -0800 (PST)
3: Return-Path:
4: Received: from a.org (a.org [123.45.67.89])
by b.org with ESMTP id ;
Mon, 29 Jan 2007 13:55:16 -0800 (PST)
5: Mon, 29 Jan 2007 16:56:18 -0500
6: Subject: Dear Bob,
7: Date: Mon, 29 Jan 2007 16:56:18 -0500
8: From: Alice
9: To: Bob
If you read the header from bottom to top, you see that Alice sent the message at 16:56 -5hrs (or EST) (line #7) and it was delivered at 13:55 -8hrs (or PST) (line #2), meaning it officially delivered before it was sent! (or the two email servers are using different time synchronization servers. You'll also notice that this header includes the IP Address of the servers it touched (line 2 & 4). This is helpful when trying to troubleshoot errors later on.
c:\Telnet 10.1.1.1 25
Here are the possible SMTP Response codes that you may see. Please keep in mind that in general a 2XX indicates success, a 3XX indicates that the server has understood the request but requires further information to complete it, a 4XX error is temporary and a 5XX error is fatal. Float over the
for my take on the error.
| SMTP Code | SMTP / ESMTP Message Description |
|---|---|
| 211 | System status, or system help reply |
| 214 | Help message |
220![]() |
Service ready |
| 221 | Service closing transmission channel |
250![]() |
Requested mail action okay, completed |
251![]() |
User not local; will forward to (other address) |
354![]() |
Start mail input; end with . |
421![]() |
Service not available, closing transmission channel |
450![]() |
Requested mail action not taken: mailbox unavailable |
451![]() |
Requested action aborted: local error in processing |
452![]() |
Requested action not taken: insufficient system storage |
500![]() |
Syntax error, command unrecognized |
501![]() |
Syntax error in parameters or arguments |
502![]() |
Command not implemented |
503![]() |
Bad sequence of commands |
504![]() |
Command parameter not implemented |
550![]() |
Requested action not taken: mailbox unavailable |
551![]() |
User not local; please try |
552![]() |
Requested mail action aborted: exceeded storage allocation |
553![]() |
Requested action not taken: mailbox name not allowed |
554![]() |
Transaction failed |
One of my peer departments is working on spamming the corporation. This internal spam, aka Corporate Newsletter, is to go out to everyone with a mailbox in the corporation. Unfortunately, a number of these people have abused their rights and their mailboxes are limited to receiving e-mail from only their managers.
In Exchange 5.5, you could create a simple CSV file and run the Admin tool to gather this information. Unfortunately, in Exchange 2003, mailbox settings are now integrated into Active Directory (AD). AD maintains all the mailbox information and settings, whereas the Exchange Admin tool is simply for server management (mail routing and global limits, etc.).
Then, how do you gather this information? I being the lazy, programmer-at-heart, that I am, wrote a VBScript to export the information. This worked great as I could set variables in the script to export the information I needed. This only worked for about a month, as I wanted pass this tool along to the internal employee for use (no way I was going to run this each time someone new was hired!!). That's why I put an HTA front-end on it!
To Use The Script
All OUs
Users OU Only
Select Case statement to include each of the OU's.
Select Case ClassesPulldown.Value
Case "LDAP://ou=users,dc=corp,dc=ent"
strExcelPath="Mailbox_Export_"&today&"_Users.xls"
Case else
strExcelPath="Mailbox_Export_"&today&"_All.xls"
End Select
NOTE: We have set Custom Attribute 1 (aka ExtensionAttribute1) to a value (1, 2 or 3) for group or resource mailboxes. For example, the Postmaster mailbox has a value of 1,Conference rooms have a value of 2. Line 121 of the code is designed to filter on these values. Remove the line completely to get all mailboxes, or modify it for your environment.
Update August 16, 2007: I've modified the code to query the local AD for it's structure, so the above modifications are not necessary. After I made the changes, it started timing out when running the query, which shouldn't be related to the script changes. I suspect slowness in my AD.
The company I am working for has a slightly different security model than I've seen before. It is probably because the largest Exchange server I've supported previously only had 3,000 mailboxes. This one supports 10x that number, with plans to grow to over 100,000 mailboxes by this time next year.
With that, they use a seperate "security" domain for logon and authentication than the "resource" domain where their mailboxes reside. This means that the account you logon to your computer, may not be the same account you read your email from. That account will need to be granted secondary permissions to that mailbox, aka "Associated Exernal Account".
Along that same strain, you can no longer simply assign permissions to a user via the Managed By tab. Their mailbox is assigned permissions, but you need to grant their security account permissions also if the group owner wants to add/remove members.
That's why I developed this script. This script will assign Manager permissions to a distribution list. It assigns the selected name rights on the Managed By tab, then assigns the "Send As" and "Write Members" permissions on the Security tab to the selected user's Associated External Account.
There are a few limitations/warnings:
I've cleaned up the code, so it no longer asks for a specific OU. It will query your entire domain for the specified DL. To run, double-click on the downloaded HTA and have fun!
Note: I have recently (3/26) updated this script to filter on the entries, instead of search each one. This makes the overall process rather quick.
Update (6/6/2008): I have added code to allow you to remove people from the security settings on a DL also. Currently working on an issue that generates an error when dealing with an apostrophe in the distinguished name (for example ldap://cn=Eric's Big List,ou=groups,ou=ericwoodford,ou=local). VBScript is taking it as the end of string and breaking..
This HTA applet allows you to grant extended AD permissions to a specific user. I use it to assign permissions to the Associated External Account of an AD user rights to modify their own delegates.
I found what values I needed by configuring a single user with permissions, then using Richard's DACL export script to dump that user. I then modify the script (see line 248) to match the permissions I want to grant.
' Template: AddAce(TrusteeName, gAccessMask, gAceType, gAceFlags, gFlags, gObjectType, gInheritedObjectType)
The applet runs faster on the DC, but is usable on my local workstation.
Today I had the honor of working with a company after a catastrophic failure of their Exchange server. One day their hardware was working just fine, the next the server will no longer boot and Exchange is down.
As part of the repair, I installed Exchange 2000 on a new server, and attempted to mount the databases from the mirrored drive. After a few manual uninstalls of Exchange and the the typical passes with ESEUTIL to repair the dirty shutdown the database was coming up clean, but still would not mount.
Checking the Application event log, I found that the server was built in the wrong Exchange Organization. Ugh! As Microsoft states repeatedly, to change the Org, one must reinstall Exchange. In Exchange 5.5 it was so bad, that we would worry about the case of the Organization name when adding new servers to an Exchange site.
That was COMPANY and not Company for the Organization name, right??
In 2000 and 2003, I've always understood that this had not changed. Plan ahead and do it right the first time. So, I started an uninstall of Exchange 2000 off this client's server while scouring Technet and Google for a better solution. Of course, as soon as I started the uninstall, I found that Exchange will NOT uninstall because it sees mailboxes are still on the server. Despite not successfully mounting, it must have partially mounted the database and Exchange now thinks they are on the server.
Using the Manual uninstall procedure again, I remove Exchange one more time, do another reinstall, but this time it also fails. Exchange never asked me to change the Organization as it is still registered in AD. Oh well. That's when I found this tool. LegacyDN.
Like RegEdit, this tool has tons of disclaimers:
Never use [name of tool] on a production database, never use unless you have backups, never use on unless you have PSS on hold on the phone and we tell you to run it..
So, I closed my eyes, crossed my fingers and double clicked on it. By default it comes up in read-only mode. I immediately see that it found the First Administrative Group on the server. Clicking on the fields do nothing, but I can see I have hope. So I close it, and reopen it in /FORCEWRITE mode. Click and the Organization field populates. I replace the text with the correct entry, click Update, wait 30 seconds and "The Organization has been updated successfully.". I imagine that my 30 second wait was in part to only having 8 mailboxes in this information store. If this had been a store with a couple hundred or thousand mailboxes, this process may have taken much longer.
Now the database mounted successfully and all is happy. The users can send and receive email without issue. Next, migrate them to Exchange 2003...
Some useful references.
When running through the latest version of the Exchange calendar update process, it has you assign permissions to all mailboxes on the server. To modify all the mailboxes, you need to provide a list of distinguished names (DN) for each mailbox. Unfortunately this is not simply the DistiguishedName field that you would query from AD, but the LegacyDN entry. You need to query the Exchange environment to get this information.
The following script, I pulled from various sources (primarily my Advanced VBScript book off my desk). It echos the DN to the screen, and also creates a CSV file in the folder it was run from. I did have to run it from the Exchange server, but it may also run from a workstation running the Exchange system admin tools.
dim strComputer
dim objwmiservice
dim propvalue
dim SWBEMLocator
Dim UserName
Dim password
Dim ColItems
strLog="MailboxReport.csv"
Set objFSO=CreateObject("Scripting.FileSystemObject")
Set objFile=objFSO.CreateTextFile(strLog,True)
strComputer = inputbox("What is the name of your Exchange server?")
wscript.echo "Querying " & strComputer
username=""
password=""
Set SWBemlocator = CreateObject("WbemScripting.SWbemLocator")
Set objWMIService = SWBemlocator.ConnectServer(strComputer,"\root\MicrosoftExchangeV2",UserName,Password)
Set colItems = objWMIService.ExecQuery("Select * from Exchange_Mailbox",,48)
for each objitem in colitems
wscript.echo objitem.legacydn
objFile.writeline objitem.legacydn
next
objfile.close
Scenario: I was given a list of 15,000 email addresses and asked if they were still valid in our Exchange environment.
Easy method: I ran a simple VBScript that does an LDAP query against each email address. This worked great, except that it took close to 5 seconds per email address to query our environment. (~20 hrs!) The over-all run time was going to be too extreme.
Next method: Read all entries into a datalist using the tricks from Microsoft Scripting Guys. The script would read each object (primary;proxy) into seperate records, then search each one. This ran for 2 hours before the server logged me off. Even if I deleted entries that I found, it still took too long.
Final method: Read each email address and append it to a string for each letter (for example "administrator@example.com;author@example.com" went in A) accessible via an array. Now I have a 50ish (a..z,0..9,!#$%^) row array containing unsorted lists. The script just needs to find the correct row, and see if the email address exists there.
The entire process took approximately 14-15 minutes. This inclides 12 minutes to read in all 250k email addresses from Active Directory(AD) and then sort them. Processing it creates a NEW csv containing the original line, then appends TRUE or FALSE. If it finds the email address, it puts a TRUE, otherwise it puts FALSE. It does very little clean-up of the email addresses it reads from the CSV, but that could be improved rather easily.
We recently finished a project updating the naming standard for all our Windows AD accounts. Once completed, I found that approximately 3% of the accounts had the alias field set to this old naming standard. Googled high-and-low, I could not find a simple script to set the alias field. So...
This script reads a text file of account distinguished names, then modifies the mailbox alias (aka mailnickname) field to match the SAMAccountName field. Added protection for accounts without mailboxes associated with them. Found that by setting a mailnickname, mail-enabled the account.
' Script to modify the mailbox alias of all accounts in text file to match SamAcct.
' Written by Eric Woodford
' Date 11/27/2007
'
Dim mbxAlias
dim samAcct
Dim objfso, tf, ef
Dim strUser, arUser
Dim objUser, strDN
Dim testrun
set objfso = CreateObject("Scripting.FileSystemObject")
'reads from this file
set tf = objfso.OpenTextFile("c:\useraccounts.txt",1)
' creates this log file
set ef = objfso.CreateTextFile("c:\User-Updates.log",vbtrue)
'set this boolean value to TRUE to it will not modify accounts. Good for testing results.
testrun = true
if testrun Then
ef.writeline "Starting TEST RUN at: " & Now()
Else
ef.writeline "Starting Account Conversion at: " & Now()
End If
' Loop through file
While tf.AtEndOfStream <> True
strDN= tf.readline
' cleanup values from export.
strDN = trim(replace(replace(Replace(strDN,"\\","\"),"""",""),"''","'"))
If Left(strDN,1) = "'" then strDN=Mid(strdn,2,Len(strDN))
If Right(strDN,1) = "'" then strDN = Mid(strdn,1, len(strdn)-1)
'don't bother processing if not a distinguished name.
If instr(strDN,"CN=")>0 Then
ef.writeline now() & ":" & strDN
'ef.writeline "Wanting to set to:" & arUser(1)
Set objUser = GetObject("LDAP://"&strDN)
samacct = objUser.sAMAccountName
mbxAlias = objUser.mailnickname
' skip if already set, or no mailbox associated with it
If (LCase(trim(mbxAlias))<> LCase(samacct)) And (mbxAlias<>"") Then
' If these don't match, then the account needs updating.
' a little formatting for the report.
sp = vbtab
If Len(mbalias) < 6 Then sp = sp & vbtab
If Len(mbalias) < 12 Then sp = sp & vbtab
ef.WriteLine "Alias: "&mbxAlias&sp&" should be: "& LCase(samacct)
' This means that the Alias is still set to the old account information.
If Not testrun Then
objUser.mailnickname = samacct
objUser.SetInfo
Else
ef.WriteLine "testrun - values not changed."
End If
Else
' don't bother updating as it is already correct.
ef.WriteLine "Alias correct: " & mbxAlias &sp&" is "& LCase(samacct)
End if
Set objuser=Nothing
End If
Wend
tf.Close
ef.Close
Attending a class this week for Exchange 2007. The question came up to export all SMTP email addresses for all mailboxes. In the lab environment, I worked up this script.
$data = get-mailbox | %{ $dname = $_.displayname;$em=$_.emailaddresses -replace("smtp:",",");$dname+$em}
I have not tested this in a production environment, so I am not sure what will display for users with other than SMTP email addresses.
The migration process used to move a number of mailboxes created a couple hundred dead mailboxes objects in our environment. Their mailbox information needed to be cleared before their mailboxes could be migrated to the server for the final time. To fix this, I looked into a script. First I found the script at TelnetPort25, here. This script goes through the entire environment and purges all deleted and non-system mailboxes. This would work great, but querying an environment with literally thousands of mailboxes is a daunting task.
That's when I found this script that would simply dump all the deleted-not-purged mailboxes on a specific server. Running this script generates a CSV file with their displayname, and some additional information. I copied the displayname field out of Glen's script and pasted to a text file to use and re-wrote Telnet's to fit my purpose.
Note: uncomment the line ' objExchange_Mailbox.Purge when you feel the script is working correctly.
'===================================================='
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 3.1
'
' NAME: PurgeSelectedMailboxes.VBS
'
' AUTHOR: Eric Woodford - EricWoodford@gmail.com
' DATE : 3/10/2008
'
' COMMENT: Purge listed mailboxes on this Exchange server.
' Must be ran from the server.
'
'====================================================
Const cWMINameSpace = "root/MicrosoftExchangeV2"
Const cWMIInstance = "Exchange_Mailbox"
strComputerName = "."
strPathToCSV = "c:\"
strListofNames = "names.txt"
Set objfso = CreateObject("scripting.filesystemobject")
Set infile = objfso.opentextfile(strPathtoCSV&"\"&strListofNames,1)
Set wmiConn = GetObject("WinMgmts:{impersonationLevel=impersonate}!\\" & strComputerName & "\root\microsoftexchangev2")
If Err.Number <> 0 Then
WScript.Echo "Cannot connect to the Exchange WMI Namespace"
Wscript.Quit
End If
haltloop = false
DO While (not inFile.atendofstream)
strUserDisplayName = inFile.readline
WScript.Echo strUserDisplayName
strWQL = "SELECT * FROM Exchange_Mailbox WHERE MailboxDisplayName = '" & strUserDisplayName & "'"
' filter collection to only selected names
WScript.Echo " Searching... Please wait"
Set wmiColl = wmiConn.ExecQuery(strWQL)
WScript.Echo "Found "&wmiColl.count & " matching mailboxes"
For Each objExchange_Mailbox In wmiColl
WScript.Echo "Processing: " & strUserDisplayName
' objExchange_Mailbox.Purge
next
Loop
infile.close
Set wmiColl = Nothing
Set objWMIExchange = Nothing
set objfso = Nothing
Wscript.Echo "Script Completed"
Here is a small HTA applet that I have built to export the membership of various groups in Windows Active Directory. It provides some basic filtering (owner, specific OU, name contains), plus 3 modes of export, XLS, CSV, or HTML to screen. Beware, if you have office 2007, XLS is XLSX format.
To run this in your environment, it will need to be modified:
line 27 & 28 strHTML=strhtml&"All OUs"
strHTML=strhtml&"This SUB OU Only"
line 55 & 56strHTML=strhtml&"All OUs"
strHTML=strhtml&"This SUB OU Only"
CSV mode gives the best detail as XLS has a 255 character limit on single fields.
Just fixed issue where it required you to pick a subOU in order to continue.
Friend is working on the dirty deed of removing a series of accounts from AD. As part of that clean-up he is to find all the distribution lists that each account is assigned manager on. Using the Quest ActiveRoles Powershell tools, you can quickly find these unfortunate people using:
$selectUser = Get-QADUser "John Doe"
Get-QADGroup -SizeLimit 0 -ldapFilter '(&(mail=*)(managedby=*))' | where {$_.managedby -eq $selectUser} | select displayname, managedby
I am working on a project to recover 30 individual days of email from a clients mailbox. This requires me to restore 30 days worth of backups to the Recovery Storage Group(RSG) on a specific server. I have quickly grown tired of selecting the name from the giant Exmerge list and worked out how to use a batch file to run Exmerge against the RSG.
Part 1: Exmerge.ini
[Exmerge]
MergeAction=0
RestoreDB=1
FileContainingListOfDatabases=database.txt
SourceServerName=
FileContainingListOfMailboxes=Mailboxes.txt
CopyDeletedItemsFromDumpster=1
DataDirectoryName=D:\EXMERGEDATA
LogFileName=ExMerge.log
Part 2: database.txt
CN=SG1-PRIV1,CN=RECOVERY STORAGE GROUP
Part 3: mailboxes.txt
/o=/ou=First Administrative Group/cn=Recipients/cn=
/o=/ou=First Administrative Group/cn=Recipients/cn=
Part 4: the Batch file (exportMbx.bat)
exmerge.exe -b -f exmerge.ini
I've now put a shortcut to the bat file on my desktop and double-click it after the restore completes. If I could only find a script that would then dismount the database, mark it for overwrite and start a new restore from the server, I'd be done! Unfortunately, I've been told by Microsoft you can't script against the RSG, so still got a bunch of clicks for each day.
Back to work...
I needed a quick script to create a series of distribution lists on an Exchange environment. I thought, "Cool, a chance to flex my PowerShell muscles!" From that I found new-qadgroup from the quest tools set and new-distributiongroup from the Exchange tools. After quite a bit of muddling around, I was never able to recreate my script.
Requirements I was trying to meet:
import-csv)| %{new-distributiongroup -name $_.name}Unfortunately, after working on the first 3-4 items, I was stumped. VBScript failed me on a number of these, just in the fact that it's not widely published and a few answers varied. So, here is what I developed. The script fails when adding more than 1 additional proxy address, otherwise it worked for all my other tests.
(Sorry for the wrappage..)
'=====================================================
'
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 3.1
'
' NAME: CreateDL.VBS
'
' AUTHOR: Eric Woodford
' DATE : 7/25/2008
'
' COMMENT: Script is designed to create fully populated distribution lists.
' The data is pulled from a CSV file.
' Assigned fields are:
' Display Name,
' Alias,
' SMTP Email address,
' Proxy SMTP addresses,
' Accept Messages from,
' Maximum Accepted message size (KB - integer format please)
' Members of the group
'
' Known issue: Accounts with more than 1 proxy address mail fail to load correctly. Still working out the details.
' ProxyAddresses - http://www.eggheadcafe.com/forumarchives/scriptingVisualBasicscript/Aug2...
'=====================================================
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Const DLGroupEA3 = "Finance DLs"
Const GroupOU = "OU=Finance Users,"
Const CSVFilePath = "C:\Finance_DistributionLists.csv"
Const LogFIlePath = "C:\CreateDL.LOG"
Set fso = CreateObject("scripting.filesystemobject")
Set myCSV = fso.opentextfile(CSVFilePath, ForReading)
Set MyLogFile = fso.opentextfile(LogFIlePath, ForWriting, True)
Do While Not MyCSV.AtEndOfStream
strCSVLine = myCSV.Readline
If InStr(strCSVLine,"@")>0 Then ' if it contains a SMTPEmail Address, it must be valid (not a header).
arrStrUser = Split(strCSVLine,",")
strAlias = arrStrUser(1)
strDisplayName = arrStrUser(0)
strSMTP = arrStrUser(2)
strMembers = arrStrUser(3)
strAcceptFrom = arrStrUser(4)
IntMaxMsgSize = cint(arrStrUser(6))
CreateDistGroup strAlias,strDisplayName, strSMTP ,strAcceptFrom, IntMaxMsgSize
End If
Loop
MyLogFile.Writeline "Adding Users"
Set myCSV = fso.opentextfile(CSVFilePath, ForReading)
Do While Not MyCSV.AtEndOfStream
strCSVLine = myCSV.Readline
If InStr(strCSVLine,"@")>0 Then ' if it contains a SMTPEmail Address, it must be valid (not a header).
arrStrUser = Split(strCSVLine,",")
strAlias = arrStrUser(1)
strMembers = arrStrUser(3)
AddGroupMembers strAlias, strMembers
End If
loop
MyLogFile.close
MyCSV.Close
Sub CreateDistGroup(strAlias, strDisplayname, strSMTP, strAcceptMsgsFrom, intMaxSizeKB )
Dim strGroup, strDNSDomain
Dim objOU, objGroup, objUser
Const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &h8
Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000
Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2
Const ADS_PROPERTY_CLEAR = 1
Const ADS_PROPERTY_UPDATE = 2
Const ADS_PROPERTY_APPEND = 3
Const ADS_PROPERTY_DELETE = 4
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("DefaultNamingContext")
Set objOU = GetObject("LDAP://" & GroupOU & strDNSDomain )
strNewGpLong = "CN=" & strAlias
Err.Clear
On Error Resume Next
Set testGroup = GetObject ("LDAP://"&strNewGpLong&","& GroupOU & strDNSDomain)
If Err <> 0 Then
MyLogFile.Writeline "creating: " & strDisplayname &"("&strAlias&")"
Set objGroup = objOU.Create("Group",strNewGpLong)
objGroup.Put "sAMAccountName", strAlias
Else
MyLogFile.Writeline strDisplayname & " already exists"
End If
objGroup.put "Name", Replace(StrDisplayName," ","")
objGroup.Put "displayname", strDisplayname
objGroup.Put "groupType", ADS_GROUP_TYPE_GLOBAL_GROUP
objGroup.put "extensionAttribute3", DLGroupEA3
if intMaxSizeKB > 0 then objGroup.put "delivcontlength", intMaxSizeKB
objGroup.mailenable
objGroup.setInfo
objGroup.put "dLMemSubmitPerms", "cn="&strAcceptMsgsFrom&","&GroupOU & strDNSDomain
if instr(strSMTP,";")=0 Then
strNewSMTP = mid(strSMTP,6)
'strNewSMTP = strSMTP
MyLogFile.Writeline "Adding : " & strNewSMTP
objGroup.Put "mail", strNewSMTP
objGroup.put "targetAddress", strNewSMTP
objGroup.PutEx ADS_PROPERTY_UPDATE, "ProxyAddresses", array(strSMTP)
objGroup.SetInfo
Else
arrSMTP= Split(StrSMTP,";")
strNewSMTP = mid(arrSMTP(0),6)
MyLogFile.Writeline "Adding : " & strNewSMTP
objGroup.Put "mail", strNewSMTP
'http://support.microsoft.com/kb/q260251/
x = 0
For Each sAddress In arrSMTP
MyLogFile.Writeline "+" & sAddress
If x = 0 Then
objGroup.PutEx ADS_PROPERTY_UPDATE, "ProxyAddresses", array(sAddress)
objGroup.put "targetAddress", sAddress
x = x + 1
Else
objGroup.PutEx ADS_PROPERTY_APPEND, "ProxyAddresses", array(sAddress)
End if
objGroup.SetInfo
Next
End If
objGroup.setInfo
Set objGroup = Nothing
End Sub
Sub AddGroupMembers (strAlias, strMembers)
On Error Resume next
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("DefaultNamingContext")
Set objGroup = GetObject ("LDAP://cn="&strAlias&","& GroupOU & strDNSDomain)
If strMembers <> "" Then
arrMembers = Split(strMembers,";")
For Each struser In arrMembers
Err.Clear
objGroup.Add("LDAP://cn="&strUser&","&GroupOU & strDNSDomain)
If Err = 0 Then
MyLogFile.Writeline "Successfully added: " & strUser & " -> " & strAlias
Else
MyLogFile.Writeline "Failed : " & strUser & " -> " & strAlias
End If
next
End if
End Sub
When decommisioning an older server in the environment, it is essential that all the former services are moved off it. This includes the existing applications (including databases), file shares and printer shares. Unfortunately there is no easy way to capture who is mapped to each of the pritner shares. Outside simply monitoring printer traffic, or visiting each desktop, Windows (AFIK) has no method of tracking this.
Hence another script. This script queries the active computer for all printers, then exports the 'resource name' (port) and share name to a CSV in a specific location.
' Source: http://www.devguru.com/Technologies/wsh/quickref/wshnetwork_EnumPrinterC...
' Compiled by Eric Woodford
'change this path to match your current environment.
strLogPath = "\\servername\admin\printermappings"
Const forReading = 1
Const ForWriting = 2
Const ForAppending = 8
'get environment variable username
set shell = WScript.CreateObject( "WScript.Shell" )
username = shell.ExpandEnvironmentStrings("%USERNAME%")
Computername = shell.ExpandEnvironmentStrings("%COMPUTERNAME%")
set shell = nothing
auditfile = strLogPath &"\"&username&"_"&computername&".csv"
set fs = CreateObject("Scripting.FilesystemObject")
Fs.CreateTextFile(AuditFile)
set f = fs.OpenTextFile (AuditFile, ForWriting, True)
strText = "Resource name,Printer name" & vbcrlf
Set WshNetwork = WScript.CreateObject("WScript.Network")
Set clPrinters = WshNetwork.EnumPrinterConnections
For i = 0 to clPrinters.Count -1
if i mod 2 <> 0 then
strtext = strtext & clPrinters.Item(i) & vbcrlf
else
strtext = strtext & clPrinters.Item(i) & ","
end if
Next
f.writeline strText
f.close
set fs=nothing
Expanding on my code to query single printer information, I found I could pull all networked printers from Active Directory(AD). This will help in cleaning up the naming standard for all our printers. Hopefully making DNS cleaner by using a single standard for host(A) records. Final file is written to c:\printer-info.csv. The attached script will return the following:
To get the script to run, you will need to modify this line (#19) to match your current environment.
Const MyDomain = "DC=my_domain_goes_here,DC=com"
Sources: Microsoft, Microsoft, and Geek Speak
My wife is a seventh grade science teacher. One of the many difficulties she has to deal with, especially on large written projects is student plagiarism. The Internet is just too tempting for some students to avoid. Here they have searched and found the perfect article that answers all the questions that the project asks of them. Why not just print it out and turn it in??
Fortunately for seventh graders, school does not expell kids for plagiarism, they simply have to do the project over again or take a zero on the assignment. Unfortunately for the students, their plagiarism is typically very easy to spot. Not many 12 year olds can typically write at the same level of a college graduate (who would typically be publishing papers on scientists, or science topics). This makes locating sources of plagiarism very easy to locate.
How?
Take the typical class project, a report on a famous scientist and pull segments of phrases that the student used.
Take for example this segment:
Benjamin Franklin
Franklin was a prodigious inventor. Among his many creations were the lightning rod, the harmonica, the Franklin stove, bifocal glasses, and the flexible urinary catheter. Although Franklin never patented any of his own inventions, he was a supporter of the rights of inventors and authors and was responsible for inserting into the United States Constitution the provision for limited-term patents and copyrights. My source..
When reading a paragraph like this on a student's report, a red flag often goes up when they use phrases like Franklin was a prodigious inventor or uses words like "among" or "although". (If not plagiarisd, it is likely Mom or Dad wrote it.)
Using Google
In the search field of Google, I do an explicit search for the scientist's name.
"Benjamin Franklin"
13,400,000 hits Here
Now, I try by adding a phrase from their assignment.
"Benjamin Franklin" "Franklin was a prodigious inventor."
15 hits - 5 shown Here
From here, 5 sources are not that difficult to follow-up on. Typically, you can find the student's source within a few hits. Print out their source, staple it to the assignment, then mark it with a 0! OOOO What fun!!
4 years ago I invested in my first MP3 player. I purchased online a 1GB micro-drive player from RIO. This device was perfect for what I needed. I could load up 100+ songs on the device and listen to commercial-free music where ever I went.
Early last year, that microdrive quit working. I guess dropping it on the ground a few times was a really bad idea. This was unfortunate, because I found several authors had started podcasting their books for free. Now I had a better understanding of what I wanted in a player.
For a replacement, I had these requirements:
Based on this criteria, I started shopping. I checked Target, Wal-Mart and Best Buy to see what was available. I found that seldom do MP3 manufacturers advertise 'bookmark' functionality on their devices (like the RCA Lyra). As this was key to my search, I went to searching on the web for brands sold locally.
Personally, I like using PriceGrabber.com for their search, and review options. I can filter down all my options in one search field and get the best reviewed products. Plus they often have links to sites with free shipping!! (Almost as good as buying it locally!).
Decision Time
I ended up purchasing another RIO device. I knew the RIO devices had the bookmark functionality, plus the Forge 256mb Sport, has a SD memory slot. I also loved the idea that it used standard AAA batteries, so I wouldn't need to purchase a separate charger for my car. I did purchase a bunch of rechargable AAA batteries and so far, the device has performed wonderfully. I download files to my laptop, use the internal SD slot to put them on the SD chip, then listen!
As you may already know, I use my MP3 player simply to play audio-books (aka podcasts) while commuting to work.
I use Juice (fka iPodder) to capture these files. It does an excellent job of downloading content and placing it on my machine. Once downloaded, I drag and drop the files to my MP3 player.
Unfortunately, my cheap ($5) MP3 player (Sansa 100?) sorts files using an odd technique. It appears to sort them by track number, then title. So if I have 2 active stories on my player, it will play track 1 - story 1, then track 1 - story 2, track 2 - story 1, etc. I think this is because sometimes the track number is a 'string' value?? To fix this, I cleanup the ID3 tags values.
This script utilizes the CDDBControl (from Roxio) to get access to the ID3 tags. If you have a Roxio product installed, you may already have this file. Otherwise, I've downloaded it from here. Register it like you would any new DLL on your computer.
Dim id3: Set id3 = CreateObject("CDDBControlRoxio.CddbID3Tag")
Dim FS: Set FS = CreateObject("Scripting.FileSystemObject")
argCount = WScript.Arguments.Count
If argCount = 2 Then
FileName = WScript.Arguments.Item(0)
strTitle = WScript.Arguments.Item(1)
set File = fs.getfile(FileName)
id3.LoadFromFile File.Path, False
track = id3.TrackPosition
If (track <> "") And Not(isnumeric(mid(id3.Title,3,2))) Then
If not IsNumeric(track) and len(track)>0 Then
do while Not(IsNumeric(track)) And Len(track)>0
track = left(track,Len(track)-1)
Loop
End if
If CInt(track)<10 Then track = "0"&track
newtitle = ucase(Left(strTitle,2)) & Track & "-" & strTitle
id3.Title = newTitle
id3.SaveToFile File.Path
End If
End If
Inside Juice, I setup an advanced option to call this script when a download finished. Cool? Go into Juice, select File, then Preferences, and click on the Advanced tab. Click the checkbox for Run this command after each download, then put in a fully qualified path to the script. Mine is:
c:\bin\scripts\JuiceUpdate.vbs "%f" "%n"
You see a short popup each time a file finished downloading and the script runs. The Title name changes to the first two characters of the name(track number) - Full name of podcast.
For example (for the Max Quick Part 2):
Title before = PB-Max Quick 2: Two Travelers - Episode 1
Title after = MA01-Max Quick 2: Two Travelers
In recent months, I have noticed quite a few new local wireless hot-spots appeared in my neighborhood. These hots-spots are my neighbors (within 1000 feet of my house), who are using the out-of-the-box configuration and no security measures. This means anyone driving by my house can access the Internet while sitting in their car (war driving).
Using a free tool (Netstumbler), I can gather information about all my neighbor's wireless routers, including name and encryption settings.
Now that I know their router settings, I can become a member of their network. In addition to using their connection to the Internet for free, I could connect to shared network resources, like printers, and shared drives. Additionally, since they haven't enabled encryption on their router, they probably haven't changed the router's admin account password.
With little effort, I logon to their router, and check the DHCP table for a list of currently active devices, including computer names and IP addresses. I can try PINGing the IP address and see if it responds. If it responds, I can try mapping a drive to the default admin shares like C$ or browse the computer by simply typing in the IP address in the Windows START - RUN line, "\\192.168.1.100", and see what comes up. By default, Windows will have a "Shared Doc" folder available, but sometimes you will also see a printer listed here.
What can you do about it?
Like security for your automobile, doing the basics of locking the doors and closing the windows is enough to deter most (wireless) thieves. They can always simply travel next door and find a much easier target. With the security now configured on your wireless router, you should be able to easily move your wireless devices between home, work and the coffee shop wireless networks with very little difficulties.
I was recently tasked with the process of setting up a fairly automated report to display each time one of our Executive Management Team's mailboxes had been accessed by internal users.
We had already turned on the log generation on the server according to the Microsoft tech article 867640. This generates a 1016 event in the server's application log each time a mailbox is accessed.
According to MS this includes:
In addition to malicious intent, each time someone books a meeting with another person, a backup is ran that uses a MAPI connection, or services like Blackberry Enterprise Server, accesses a mailbox they will also be annotated in the app logs. Plus, this report is destined for Sr. Management, so sending a dump of the Application logs was out of the question.
LogParser should already be your best friend. You can use this versatile tool to query any ASCII log file or server Event logs to pull out information.
Setup
The attached batch file, runs a logparser query against a mailbox server and generates a SUMMARY.CSV file.
Details
We run this batch 6 times a day (using a Windows Scheduled task), creating a 1kb file for the 20 something Executives we monitor. After a month, I have almost 1mb of log files. The script is designed to pull information only from the last time it ran, so no overlap. This batch creates a new file with updates since the last run, then rebuilds the SUMMARY.CSV file.
The summary contains the display name of the executive, date of mailbox access, the domain account that accessed the mailbox, and how many times on that day.
I've expanded this by exporting all active mailboxes in the domain (see HTA coming soon) and import that into an Access database. I then created a 'linked table' connection to the CSV. Using a simple query to correlate the domain account, to a display name from Active Directory. (I only need to update the AD Export, when a account does not resolve correctly in the query.) Then I use Crystal Reports, pulling from the Access Query, to filter the information, generate summaries, etc.
Future
To expand upon this, we've considered porting the data collected to a SQL server (which Logparser handles nicely). Until then I have a simple query I can run anytime, and get relatively up-to-date access reports for these users.
We are working to decommission our existing Exchange 5.5 environment and looking to migrate all services to Exchange 2003. As part of the decom, we need to re-direct the Internal SMTP traffic off our Exchange 5.5 bridgeheads. This meant, determining which servers were connecting to the server. First we changed all MX records in our DNS to point to the new server. Then we watched the Application logs on the server for MSExchangeIMC - ID: 2000 events.
This provided some 2,000 hits on our server. Ouch! Using LogParser from Microsoft, I was able to generate a quick query to pull the information.
| Server | Hits |
|---|---|
| citrixserver.example.local | 373 |
| othersmtpserver.example.local | 214 |
| appserver.example.local.local | 150 |
| 192.168.0.28 | 25 |
| webserver.example.local | 1 |
With this data, I was able to contact the server owners and have them change their relay information.
logparser "select trim(substr(strings,0,index_of(strings,'|'))) as Server, count(*) as hits into LogFile.csv from \\ExchangeServer\application where Sourcename like 'MSExchangeIMC' and TimeGenerated >= SUB(TO_LOCALTIME(SYSTEM_TIMESTAMP()), TIMESTAMP('0000-01-01 00:00:00', 'yyyy-MM-dd hh:mm:ss')) and eventid=2000 group by strings order by hits DESC"
Let me break this down a bit.
trim(substr(strings,0,index_of(strings,'|'))) as Server, count(*) as hits
into LogFile.csv from \\ExchangeServer\application
Sourcename like 'MSExchangeIMC' and
eventid=2000 and TimeGenerated >= SUB(TO_LOCALTIME(SYSTEM_TIMESTAMP()), TIMESTAMP('0000-01-02 00:00:00', 'yyyy-MM-dd hh:mm:ss'))
group by server order by hits DESC
This query runs in about 20 seconds on 2 servers with 3 days of data. Putting this into a BATCH file, I was able to make the script rather user friendly.
In my office, we have over 60 different resources that can be booked online. These resources (including conference rooms, laptops and LCD projectors) can all be scheduled via Outlook and Microsoft Exchange, using Microsoft's latest Auto-Accept Agent. The main draw back is that you can't really tell who has what room for how long. With the previous version of Exchange (5.5) there where issues with users having permissions to the calendars. Outlook would corrupt the conference room calendar and cause the free/busy information to become corrupted.
I developed this script to query the resource calendars and not conflict with the free/busy info. This latest version uses WebDAV to query the calendar (similar to if the user is accessing via OWA!). I have scheduled the script to run every 15 minutes using a Windows Scheduled Task. To export a calendar with 270 items take around 5 seconds.
To configure the script:
Note: Try to avoid putting system variables (? * and \) in the Display Name field.
a href= tags to point to each resource.
Building A
That's it! Fairly easy to create and maintain. In a future release I plan to have the script update the local Application Event Logs. This would allow products like NetIQ to monitor the script. Currently a log file is written locally.
Let me know what you think. I won't provide any support for your environment, but can help troubleshoot via e-mail.
6-20-2006: Finally uploaded revision including modification for day-light savings. This version reads the Day light settings from the local server, calculates the difference from DLS and modifies the time accordingly. This has been tested on Exchange 2003 successfully in Pacfic Time Zone.
Revision 3.3: If you downloaded the script, check out my article on the changes to Daylight Savings calculation here in the US. It may cause problems with entries booked on your calendar.
'==========================================================================
' NAME: WebCalendarHTML.VBS
' AUTHOR: Eric Woodford - mailto:Eric@EricWoodford.com
' DATE: 11/11/2005
'
' COMMENT: Generates a HTML file containing calendar entries for the next 7 months (no FREE type meetings).
' : Uses WebDAV to connect to mailbox and pull data.
' SOURCE: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wss/wss...
' http://msdn2.microsoft.com/en-us/library/ms879286.aspx
' SOURCE: http://www.alistapart.com/articles/zebratables/
'
' -------------------------------------------------------------------------
' Revisions to 3.2:
' Found issue with meetings occuring after Daylight Savings.
' Added 4 new functions to process DST information
' Modifying script to use WMI to pull DST information
' is server currently participating in DST (DSTEnabled)
' is server currently in DST mode (isDST)
' DST Offset in minutes (offsetmin)
' Adding code to calculate time based on difference to DST Date.
' Calculates DSTStartDate and DSTEndDate based on First Sunday / Last Sunday rule.
' -------------------------------------------------------------------------
' Revisions to 3.1:
' Modified script to read from a SETTINGS.TXT file
' File can contain blank lines & comments
' SETTINGS.TXT should contain:
' FSUSERNAME=User name to map a drive to web server
' FSPASSWORD=Password to map network drive
' MAPDRIVEPATH=Open drive letter to use for script.
' WEBUNCEPATH=UNC Path to network share (like \\server\sharename)
' MAILBOX=servername(tab)account(tab)password(tab)mailbox alias(tab)Mailbox Display name
' MAILBOX=servername(tab)account(tab)password(tab)mailbox alias 2(tab)Mailbox Display name 2
' -------------------------------------------------------------------------
' Revisions to 3.0:
' Converted from Exchange 5.5 Event Script to use CalDAV
' Modified script to utilize CSS for table back ground colors vs BGCOLOR code, encoded in script
' Modified script to utilize mouseover and css to highlight active row, and make it clickable. Clicking turns composes new message.
' -------------------------------------------------------------------------
Option Explicit
Main
Sub Main()
' This sub-routine processes the Settings.txt file and generates a calendar for each entry.
' It will fail out if it reaches a mailbox entry, before all the drive mapping entries are not defined.
Dim strUser, strDevice, strUncPath, strPassword
Dim objNetwork, objFile
Dim fso
Dim strReadLine
Dim arrLine,arrMailbox
Dim intMBXCount
Dim blnMapDrive, blnGotMailbox
Dim intLLevel
intMBXCount=0
blnMapDrive = False
Set fso = CreateObject("Scripting.FileSystemObject")
Set objNetwork = CreateObject("WScript.Network")
Set objFile = fso.OpenTextFile("settings.txt",1) ' for reading
'generate log file & place in current directory.
GenLogFile CStr(Now()) & " Starting Processing"
Do Until objFile.AtEndOfStream
if blnGotMailbox Then
genlogfile CStr(Now()) & "Error: Not enough parameters completed before first MAILBOX parameter." & vbcrlf & "Need to map Drive first."
genlogfile "SETTINGS.TXT should contain:"
genlogfile vbtab & "FSUSERNAME=User name to map a drive to web server"
genlogfile vbtab & "FSPASSWORD=Password to map network drive"
genlogfile vbtab & "MAPDRIVEPATH=Open drive letter to use for script."
genlogfile vbtab & "WEBUNCEPATH=UNC Path to network share (like \\server\sharename)"
genlogfile vbtab & "MAILBOX=servername(tab)account(tab)password(tab)mailbox alias(tab)Mailbox Display name"
Exit Do
End if
strReadLine = trim(objFile.ReadLine)
If (Left(strReadLine,1)<> "'") and (len(strReadLine)>0) then
arrLine = Split(strReadLine,"=")
Select Case ucase(arrLine(0))
Case "FSUSERNAME"
strUser = trim(arrLine(1))
Case "FSPASSWORD"
strPassword = trim(arrLine(1))
Case "MAPDRIVEPATH"
strDevice = trim(arrLine(1))
Case "WEBUNCPATH"
strUncPath=trim(arrLine(1))
Case "LOGGINGLEVEL"
intLLevel=cint(trim(arrLine(1))) '0 = none, 1=basic, 2=verbose
Case "MAILBOX"
arrMailbox=Split(arrLine(1),vbtab)
blnGotMailbox = true
End Select
if Not(blnMapDrive) then blnMapDrive = mapDrive(fso, objNetwork, strUser, strPassword, strDevice, strUncPath)
If blnMapDrive And blnGotMailbox Then
GenerateHTML arrMailbox(0), arrMailbox(1), arrMailbox(2), arrMailbox(3), arrMailbox(4), strDevice, intLLevel
blnGotMailbox = false
End if
End If
Loop
GenLogFile CStr(Now()) & " Completed Processing"
objNetwork.removenetworkdrive strDevice
'cleanup log files
Dim strFolder
Dim objFolder
Dim colfiles
Dim ext
'Dim objFile
strFolder = ".\"
Set objFolder = FSO.GetFolder(strFolder)
Set colFiles = objFolder.Files
For Each objFile In colFiles
ext = right(objfile.path,3)
If (DateDiff("d",objFile.DateLastModified,Now()) > 30) And (ext="log") Then
GenLogFile "Cleanup: Deleted "&objFile.path
fso.deletefile(objfile.path)
End If
Next
Set objfile = nothing
Set objNetwork = nothing
Set fso=Nothing
End Sub
Sub GetTimeOffSet(TimeZone, isDST, DSTEnabled, llevel)
' NOTES: This routine returns the time-zone off-set (in minutes) for the computer running the script.
' The information is pulled from the system using WMI instead of a registry pull.
'
' SOURCE: http://www.windowsitpro.com/Articles/Index.cfm?ArticleID=26030&DisplayTa...
Dim objOS
Dim curTZ
Dim CurDST
for each objOs in getobject("winmgmts:").InstancesOf("Win32_ComputerSystem")
curTZ = objOs.CurrentTimeZone
If llevel = 2 Then GenLogFile "Current Time Zone:" & curTZ
curDST = objOs.DayLightinEffect
If llevel = 2 Then GenLogFile "Currently DST:" & curDST
DSTEnabled = objOS.EnableDaylightSavingsTime
If llevel = 2 Then GenLogFile "Daylight Savings Enabled:" & DSTEnabled
If IsNull(DSTEnabled) Then DSTEnabled = False
isDST = CurDST
Next
'if curDST then
' curTZ = curTZ - 60
'end if
Set objOS = Nothing
TimeZone = CurTZ
End Sub
Function GetDSTEndDate(llevel)
' In the US, Daily Savings Ends at 2AM on the First Sunday in November.
' This function is to determine the next date.
Dim DSTYear
Dim DSTDate
DSTYear = CStr(Year(Now))
DSTDate = dateadd("d",(8-weekday(cdate("11/1/"&DSTYear))),cdate("11/1/"&DSTYear & " 02:00:00"))
If now() > DSTDate
DSTYear = CStr(Year(DateAdd("yyyy",1,Now)))
DSTDate = dateadd("d",(8-weekday(cdate("11/1/"&DSTYear))),cdate("11/1/"&DSTYear & " 02:00:00"))
End If
If llevel >0 Then GenLogFile "Daylight savings ends:" &DSTDate
GetDSTEndDate = DSTDate
End Function
Function GetDSTStartDate(llevel)
' In the US, Daily Savings starts at 2AM on the Second Sunday of March.
' This function is to determine the next date.
Dim DSTYear
Dim DSTDate
DSTYear = CStr(Year(Now))
DSTDate = dateAdd("d",7,dateadd("d",(8-weekday(cdate("3/1/"&DSTYear))),cdate("3/1/"&DSTYear & " 02:00:00")))
If now() > DSTDate
DSTYear = CStr(Year(DateAdd("yyyy",1,Now)))
DSTDate = dateAdd("d",7,dateadd("d",(8-weekday(cdate("3/1/"&DSTYear))),cdate("3/1/"&DSTYear & " 02:00:00")))
End if
If llevel >0 Then GenLogFile "Next Daylight savings is:" &DSTDate
GetDSTStartDate = DSTDate
End Function
Function AdjustForDST (isDST, DSTStart, DSTEnd, meetingTime, llevel )
' Adjusts for meetings that happen on other side of DST boundary than current time.
If Not(IsDST) And (meetingTime >= DSTStart) And (meetingTime < DSTEnd) Then
' Now() is before April, but dtmStart is before Last of October
If llevel >0 Then GenLogFile "Adjusting meeting forward 60 minutes"
meetingTime = DateAdd("n",60,meetingTime)
ElseIf IsDST And (meetingTime < DSTStart) and (meetingTime >= DSTEnd) Then
' Now is after April, but dtmStart is after End of October
If llevel >0 Then GenLogFile "Adjusting meeting backward 60 minutes"
meetingTime = DateAdd("n",-60,meetingTime)
Else
If llevel >0 Then GenLogFile "No adjustment made to meeting"
End If
AdjustForDST = meetingTime
End Function
Function mapDrive(fso, objNetwork, strUser,strPassword,strDevice,strUncPath)
' Maps network drive if has all four parameters - read from settings file.
mapDrive = false
If strUser = " Then Exit Function
If strpassword = " Then Exit Function
If strDevice = " Then Exit Function
If strUNCPath = " Then Exit Function
'remove mapping for drives that already exist.
If fso.DriveExists(strDevice) Then objNetwork.removenetworkdrive strDevice
Set objNetwork = CreateObject("WScript.Network")
objNetwork.MapNetworkDrive strDevice, strUncPath, false, strUser, strPassword
strPassword = vbEmpty
strUncPath = strDevice
If fso.DriveExists(strDevice) Then mapDrive=True
End Function
Sub GenerateHTML(mailboxserver, userAcct, userpswd, mailboxalias, MailboxDisplayname, strDevice, LLevel)
' Reads mailbox information and outputs to HTML webpage as defined by DisplayName and UNC path.
' Mailbox Server: NetBIOS name of the server
' UserAcct: AD account, with (at least) REVIEWER level permissions to the mailbox. "domain\account"
' UserPswd: Password for UserAcct
' MailboxAlias: Either the account name or the SMTP address for the resource calendar to be read.
' MailboxDisplayName: Display Name to put at top of generated Webpage.
' strDevice: Name of drive letter mapped for the web content
' Variables.
Dim strCalendarURI ' As String
Dim reqDoc ' As Msxml2.DOMDocument
Dim resDoc ' As Msxml2.DOMDocument
Dim pi ' As IXMLDOMProcessingInstruction
Dim strPassword ' As String
Dim strUserName ' As String
Dim searchrequestNode ' As IXMLDOMNode
Dim sqlNode ' As IXMLDOMNode
Dim strQuery ' As String
Dim queryNode ' As IXMLDOMText
Dim req ' As MSXML2.XMLHTTP
Dim objSubjectNodeList ' As IXMLDOMNodeList
Dim objStartTimeNodeList ' As IXMLDOMNodeList
Dim objEndTimeNodeList ' As IXMLDOMNodeList
Dim objBusyStatusNodeList ' As IXMLDOMNodeList
Dim objInstanceTypeNodeList ' As IXMLDOMNodeList
Dim objOrganizerNodeList ' As IXMLDOMNodeList
Dim objNode ' As IXMLDOMNodt
Dim i ' As Integer
Dim strInstanceType ' As String
Dim strDateNow ' As string
Dim strDateEnd
Dim StrDate ' used as temporary variable. Reads from NODE and processes into
dim myCalendar ' array for building side calendar
dim counter ' # of meetings found in calendar
dim WebHeader ' Contains Header to put in Item Count at end of building form
dim WebContent ' string container for web page content
dim bgcolor ' place holder for bgcolor tag in code
dim strOrganizer ' organizer for meeting string holder
dim pdm,pdd ' Values for previous month, day - transitition on colors
dim cdm, cdd, cdy ' current working day, month values
Dim edd, edm ' end date day value
Dim objFileSystem ' connect to fso to create file
dim objFile ' current file connection
dim colorstep ' container for current color value on table
dim dtmStart ' DTM container for Start Date
dim dtmEnd ' DTM container for End Date
dim strSubject ' Subject of current calender item
dim strStartTime, strEndTime ' formatted time string value for HTML output.
dim x ' variable for place holders..
dim dow ' day of week for when to start new week
dim workingdate ' start of week date for buidling small selection calendar, = Saturday of current week
dim monthstr ' 3 letter value for current month JAN, FEB, MAR, etc. small calendar
dim ConferenceRoomName ' conference room name for file output.
dim strDayOfWeek ' String Value for Day of Week.
Dim strDayOfWeekEnd ' String Value for Day of Week for End Date
Dim blnAllDayEvent ' int value for determining where in multiday event 0= not, 1=start, 2=middle/end
Dim strCurrentUser ' resource mailbox name.
Dim oshell ' connect to system for time-offset for daylight savings
Dim atb ' registry key containing value
Dim offsetmin ' time off-set in minutes from daylight savings.
Dim IsDST, DSTEnabled
Dim DSTStartDate
Dim DSTEndDate
'MsgBox TimeZoneOffSet & isdst
'const strUNCPath="\\sacwebsrv001\ConferenceRoomReservations\" ' Path to folder containing web pages. INDEX should point to reside here
Const strUncPath="z:\"
Dim ScriptVersion ' string variable to put value into HTML
ScriptVersion = "3.2 WebDAV"
'genlogfile "Starting "& Mailboxdisplayname&" : "& Now()
' Initialize variables.
strUserName = useracct
strPassword = userpswd
strCalendarURI = "http://"&mailboxserver&"/exchange/"&mailboxalias&"/calendar/"
strCurrentUser = mailboxdisplayname
ConferenceRoomName = RemoveSpaces(strCurrentUser, LLevel) ' remove wild-cards and spaces for filenames
GetTimeOffSet offsetMin, IsDST, DSTEnabled, LLevel
DSTStartDate = GetDSTStartDate(llevel)
DSTEndDate = GetDSTEndD