Search Exchange for mobile devices with IMEI

At work I have a bunch of HTC phones lieing around. Quite often I don’t have the password for the phone so normally I would have to send them to the vendor to get them unlocked. This is both time consuming and costly. With Exchange you have the posibility to use a recovery password to reset the phones password. So I developed this script that search for phones with a specific string in the IMEI no. The script displayes all matches, mailboxes associated with the phone and the recovery password if possible. Something like this:

Recovery password for ’35575701146176702′ belonging to John Wayne (jw) is: 81689180324104937573

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Add-PSSnapin -name Microsoft.Exchange.Management.Powershell.Admin
 
$searchString = Read-Host "Enter IMEI no. to search for:"
"Searching for IMEI containing '" + $searchString + "'..."
 
$mailboxes = Get-Mailbox
 
foreach( $mailbox in $mailboxes )
{
    $deviceStats = Get-ActiveSyncDeviceStatistics -Mailbox $mailbox -ShowRecoveryPassword $true
    foreach( $deviceStat in $deviceStats )
    {
        if( $deviceStat.DeviceIMEI -like "*" + $searchString + "*" )
        {
            "Recovery password for '" + $deviceStat.DeviceIMEI + "' belonging to " + $mailbox.DisplayName + " (" + $mailbox.SamAccountName + ") is: " + $deviceStat.RecoveryPassword
            break
        }
    }
}

Find orphaned accounts in Active Directory

As an administrator I would of course never forget to delete a computer account ;) , but if someone else should forget it quite a lot of orphaned accounts would exist in the Active Directory over time. So how would one detect accounts that hasn’t been used for a while? Well, again the answer is PowerShell. With this simple, little script below you are able to choose the age of the accounts you want displayed. Of course you could easily edit the script so it deleted the accounts right away by using Remove-QADObject. You could also use the Get-QADUser cmdlet to search for user accounts instead of computer accounts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Add-PSSnapin -name Quest.ActiveRoles.ADManagement
 
$searchRoot = "domain.local/Workstations"
$age = 60 # in days
 
$accounts = Get-QADComputer -IncludedProperties LastLogonTimeStamp -SearchRoot $searchRoot | Sort-Object -Property LastLogonTimeStamp -Descending
$today = Get-Date
 
foreach( $account in $accounts )
{
    if( $account.LastLogonTimeStamp -eq $null )
    {
        Write-Host $account.Name "last logon: Never"
    }
    else
    {
        $res = $today - $account.LastLogonTimeStamp
        $days = $res.Days
 
        if( $days -gt $age )
        {
            Write-Host $account.Name "last logon:" $days "days ago"
        }
    }
}

Network troubleshooting – Colasoft Ping Tool

I was experiencing some VPN problems between our main site and a store. Ocassionally the VPN connection would be dropped without any obvious reason when taking a look in the network monitoring system. Troubleshooting on the router didn’t show any problems at all either. I was recommended to give Colasoft Ping Tool a try.

Colasoft Ping Tool is a great, freeware application for sending ICMP packets to one or more hosts. It has a simple and intuitive interface and offers what you need like saving overall statistics, graphs and logs. This is very good when you have to convince somone else (like an ISP) that there is a problem they need to take care of.

 Well, back to my problem. After ruling out problems with the router itself, I fired up Colasoft Ping Tool. I started to ping the LAN (over VPN) and WAN interface on the connection in question. At the same time I also wanted to ping another location without any problems in the same area. If you look at the chart below you’ll easily notice that theres a big difference even though the configuration was exactly the same.

Ping Chart

I called the ISP, presented the graphs and the logs, and they accepted to monitor the connection for a couple of days. After that they accepted they had a problem and fixed it quickly.

Colasoft Ping Tool is a great application which I’ve used several times since. It always gives you an idea of what to rule out or shows you if there’s a problem.

Display large mailboxes in Exchange Server 2007

One thing is certain when you assign space to mailboxes. If the user is assigned 100MB she will use it, if the user is assigned 1000MB, she will use it. Unlike in Exchange Server 2003 you are not able to retrieve a list that displays the mailbox size from the Exchange Server 2007 management console. The script below displays the 50 largest mailboxes in your organization in descending order. Simply edit the script to reflect your needs.

1
2
3
Add-PSSnapin -name Microsoft.Exchange.Management.Powershell.Admin
 
Get-MailboxStatistics -Server "server"  | Sort-Object -Descending TotalItemSize | ft displayname,totalitemsize,ItemCount,StorageLimitStatus | Select-Object -first 50

Windows 7 keeps logging on with temporary profile

As an administrator you often experience different kinds of problems with user profiles. It might get corrupted for some reason, users might experience problems with an application caused by settings in the user profile, the profile is very big which increases logon/logoff time etc. When you delete or rename the profile locally on the client and also on the server on which you might store roaming profiles you keep getting the below message.

temporaryprofile

The solution is actually pretty simple. Simply open the registry edit (start –> run –> regedit.exe) and browse to “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList”.  The subfolders are a list of profiles so you simply need to locate the profile in question and delete the entire subfolder. Look at the value of “ProfileImagePath” or “CentralProfile” to recognize the profile you need to delete.

Automatic Computernames with Microsoft Deployment Toolkit

Environment: The server is Windows Server 2008 Standard Edition x64 with WDS installed. Microsoft Deployment Toolkit 2010 is installed on the server and Windows Automated Installation KIT (AIK) aswell. MDT 2010 can be downloaded here.

If you are used to RIS or WDS, you know that these services gives you the opportunity to set a “New client naming policy” and also where to place the client account for the workstation during deployment. When you work with MDT things are a little different since you generate a Lite Touch WIM image and add that as a boot image in WDS. By doing this you only use WDS for PXE boot and download the WIM image. After that the client moves on and WDS is no longer needed by the client. At this stage deployment hasn’t really begun yet so setting a client naming policy and/or the OU in which you want the new computer account to be created does not have any affect at all. Chances are that if you have already tried to deploy a workstation using MDT, you are already familiar with the random computernames that are generated by MDT. I have googled quite a lot wihtout any luck finding a good solution. I wanted a solution which named the workstations automatically say WKS001, WKS002 etc. If a computeraccount would be removed from Active Directory the computername should be available for a new workstation.

The computernaming can be handled quite easily with a PowerShell script. However the default security configuration of PowerShell will not let you run the script from a network share. Also in order to be able to rename the workstation in question I would have to get around UAC.

To get around UAC during deployment I had to be a little creative. I created an Organizational Unit in Active Directory and blocked inheritance. Besides linking my WSUS policy to the OU (since I want to be able to install Microsoft patches during deployment), I also created a GPO that would deactivate UAC and lower the PowerShell security settings so I could easily execute my scripts and other commands/programs during the process. The following GPO settings were the ones I used:

GPO Settings

GPO Settings

And just to visualize how it could look like in the Group Policy Management MMC:

Organizational Unit and GPO structure Organizational Unit and GPO structure

So now that script and program execution is ready, the next step is to configure MDT to deploy the computers into the “Deploying” Organization Unit we just created. To do this open the ”Deployment Workbench” and open the Task Sequence you are using. Under the “OS Info” tab click  ”Edit Unattended.xml” button. This will open the answer file for your OS installation and here you will be able to configure which OU in which you want computer account to be created. In the “Answer File” windows browse to Unattend –> Components –> 4 specialize –> x86_Microsoft-Windows-UnattendedJoin_neutral –> Identification. In the “Identification Properties” window there is a setting called “MachineObjectOU”. Enter the “distinguishedName” of the Organizationl Unit. To find the “distinguishedName” just open “Active Directory Users and Computers” –> right clik the Organizational Unit and choose “Properties”. Next open the “Attribute Viewer” tab.

Now it is time to introduce the script that does all the work. I know it could be a bit more sofisticated, but it works. Remember to change the prefix of computer account and the OU to search in so it fits your needs. The script simply browses through the OU and looks for computer account with a specific prefix, in this case “PC”. Lets say 3 computer accounts are found: PC001, PC002, PC004. The script will discover that PC003 is not in use and will assign this name to the new computer. Of course this will be unsuccessfull if PC003 is located somewhere else in Active Directory. In that case you will have to modify the script. If the script doesn’t fine any available account names it will just select the next in line e.g. PC005. In this case I saved the script as “ComputerRename.ps1″ in the “Script” directory of my deployment share.

function GetComputerList($TargetDn)
{
# Locates all computer accounts that begins with "PC"
# Change the value for your needs
 ${tFilter} = ‘(&(objectClass=computer)(cn=PC*))’
 ${Searcher} = New-Object System.DirectoryServices.DirectorySearcher $tFilter
 ${Searcher}.SearchRoot = “LDAP://${TargetDn}”
 ${Searcher}.SearchScope = [System.DirectoryServices.SearchScope]::Subtree
 # Page size default in Active Directory is 1000
 ${Searcher}.PageSize = 1000
 $Results = $Searcher.FindAll()
 return $Results
}

# Change OU path for you needs
$computers = GetComputerList("OU=Workstations,DC=yourdomain,DC=local")
$current  = 0
$previous = 0
$new_name = $null

# Run through search result
foreach( $computer in $computers )
{
    $cn = "" + $computer.Properties['cn']
    $current = [int]$cn.Substring(6)

    # Check for avialable computername
    if( $current.CompareTo($previous+1).Equals( 1 ) )
    {
        $new_name = $previous+1
        break # no need to search anymore
    }

    $previous = $current
}

# If no available computername use next in line
if( !$new_name )
{
    $new_name = $current+1
}

# Prefix number with 0's if needed
if( $new_name.CompareTo(10).Equals(-1) )
{
    $new_name = "00" + $new_name
}
elseif( $new_name.CompareTo(100).Equals(-1) )
{
    $new_name = "0" + $new_name
}

# Prefix name with constant
# Change the constant "PC" for your needs
$new_name = "PC" + $new_name
$new_name

# Do rename
$oComputerSystem = Get-WmiObject win32_computersystem
$oComputerSystem.Rename( $new_name )

The last thing to do is to make sure the script will be executed as part of the Task Sequence. Again open the “Deployment Workbench” and the Task Sequence you are using. Click “Add” –> “General” –> “Run Command Line”. Give the task a name e.g. “Computer Rename”. In the “Command Line” textbox enter the following command and set it up so it runs under an account that has the appropiate rights to rename a computer account:

powershell.exe "%SCRIPTROOT%\RenameComputer.ps1"

Hope this was usefull.

Funny Chat Session on ICQ

This actually has nothing to do with system administration, but I’m gonna post it anyways.

One day when I got home from work I turned on the laptop, while the wife was making dinner. At that time I was still on ICQ but were getting tired of all the spam messages. Suddenly someone not in my contact list types “hi” and I’m thinking this is a spam bot and I just wanna check how good it is and sends some replies. I realise that this isn’t a spam bot but an actual person who thinks I’m someone else (event though she can see my nickname). I decided to mess with her a bit ;) .

05-06-2009 17:53:10 her: oh...i wasn't..DTM was so amazing..i hope you liked it too
05-06-2009 17:53:31 me : no i hated it
05-06-2009 17:53:38 her: really?
05-06-2009 17:53:47 her: oh why didn't you tell me?
05-06-2009 17:54:01 me : because i thought you would get mad
05-06-2009 17:54:12 her: we could have gone somewhere else
05-06-2009 17:54:23 me : really... .maybe to a strip club?
05-06-2009 17:54:35 her: where to??
05-06-2009 17:54:40 me : a strip club
05-06-2009 17:54:47 her: what would your pierre say to me then...
05-06-2009 17:55:16 her: i hope you remember me correctly..
05-06-2009 17:55:31 me : of course i do
05-06-2009 17:55:36 her: oh nice nice..
05-06-2009 17:55:54 her: cause i was afraid that i have written down the wrong icq-number
05-06-2009 17:56:05 her: it was all so fast..
05-06-2009 17:56:20 me : how's your boyfriend
05-06-2009 17:56:45 her: my boyfriend?i've told you i'm single for 3 years..
05-06-2009 17:56:50 her: you forgot...ok :-)
05-06-2009 17:56:59 me : no sorry... for someone else
05-06-2009 17:57:10 her: ok, no problem
05-06-2009 17:57:33 her: so you are not coming to lemans next weekend?
05-06-2009 17:57:45 me : would you meet me there?
05-06-2009 17:58:13 her: you said you maybe would come with TC-fan club, didn't u?
05-06-2009 17:58:27 me : maybe you could wear a hot bikini
05-06-2009 17:58:43 her: me??stina??
05-06-2009 17:59:03 me : yes yes
05-06-2009 17:59:07 her: you know how i am..
05-06-2009 17:59:13 me : how are you
05-06-2009 17:59:25 her: i'm not a kind of a "paddock girl"
05-06-2009 17:59:44 her: i take it serious, i'm a journalist
05-06-2009 18:00:12 her: so i can't walk in the paddock in bikini, my god..
05-06-2009 18:00:48 her: how was your german exam then?
05-06-2009 18:01:30 me : not good... didn't pass but i kissed the teacher
05-06-2009 18:01:54 her: =-O what??you kissed him??
05-06-2009 18:01:58 me : yes
05-06-2009 18:02:13 her: oh my god...is he young then?i think you didn't tell me
05-06-2009 18:02:23 me : yes, he's 21
05-06-2009 18:02:30 her: 21??the TEACHER?
05-06-2009 18:02:32 me : a little hottie
05-06-2009 18:02:34 me : yes
05-06-2009 18:02:45 her: younger than you?my god...
05-06-2009 18:03:00 me : yeah, he's a little hottie... you should try it some time
05-06-2009 18:03:05 me : how old are you ... i forgot
05-06-2009 18:03:10 her: my teachers are all over 35..
05-06-2009 18:03:24 her: i'm 24 stina...you forgot quite much
05-06-2009 18:03:49 me : at least i remember you'r blond
05-06-2009 18:03:52 her: i only hope you really do know who i am still
05-06-2009 18:03:59 her: blond :-@
05-06-2009 18:04:04 her: i'm black
05-06-2009 18:04:19 her: stina??is it really you?
05-06-2009 18:04:20 me : sorry, just messing with you
05-06-2009 18:04:49 her: i hope i don't have the wrong number
05-06-2009 18:05:15 me : actually you have
05-06-2009 18:05:24 her: =-O

After that she actually laughed at it and said I seemed like a nice guy despite of what I just did. I found out that she was actually in a fan club of the Dane Tom Kristensen and she was given my ICQ number from some Danish woman (since I’m also Danish she keept thinking I was that woman), who was also in this fan club. It was a bit hectic when they seperated, so guess she didn’t write down the correct number.

Exchange migration influences on Internet Connection Speed in Branch Office

OK, here’s the first funny story I want to publish.

In the summer 2009 I had prepared two new, great servers – HP DL380 G6 with 2 Xeon E5520 @ 2.26GHz, 32GB RAM and disk enclosure filled with 146GB 15K SAS disks for each server. They were going to run Exchange 2007 in a CCR configuration for high availability. Since the old configuration was a 32 bit environment with 2 DL380 G4 servers in an old Veritas cluster configuration I was expecting outstanding performance and that’s what I got but one of my users wasn’t so satisfied.

I spent more than 14 hours (no sleep) on migrating data and were quite satisfied when it was over. I didn’t get any errors or ran into any unexpected problems. Of course I instructed my users to contact me directly if they were experiencing any problems that would be related to the recent migration. Of course I got a few calls because OWA had changed etc., nothing big. However after a day or two a not so satisified user called me. Here’s the conversation.

User: Hi. Since you made those changes to our system my Internet is very slow.
Me: What changes are you referring to (surely he couldn’t be referring to the exchange migration).
User: Well, you made those changes to the e-mail system the other day. www.krak.dk is taking more than 7 minutes to load on my computer.
Me: OK, that sounds strange.
User: Yeah, it started right after you made the change so I’d like you to change it back as this is unacceptable.
Me: I’m pretty sure this has nothing to do with the changes I made to the e-mail system.
User (insisting): Well, I’m pretty sure that it has. You must admit that it is strange this happens right after you have made those changes.
Me: Yeah, you know what. I’m gonna have to look into this and we’ll call you back within the hour. OK?
User: OK, thank you.

I asked a supporter to call back and let him know the background of the call. We had a good laugh, but how should the user know any better? :) . I didn’t hear from the user again, so I guess they figured it out.

Extract Operating Systems from Active Directory

Prerequisites: ActiveRoles Management Shell for Active Directory must be installed on the host running this script.

On a forum I saw someone in need of a script that could display the operating systems of all the computers in Active Directory. Also it could help when you have to tell Microsoft how many licenses you need to pay for. This is an easy one so why not? :)

First I want to load the snappin so I don’t have to open the ActiveRoles Management Shell everytime I want to run the script.

Add-PSSnapin -name Quest.ActiveRoles.ADManagement

Next I’m going to fetch all computer accounts from Active Directory. For that I’m using the ‘Get-QADComputer’ cmdlet. You can add the attribute ‘-SearchRoot’ if you don’t want to search the entire Active Directory.

'Fetching computer accounts from Active Directory. This may take a while...'
$computers = Get-QADComputer | Sort-Object Name
'' + $computers.length + ' computer accounts fetched from Active Directory.'
'-----------------------------'

The abvious thing to do now is to run through the computers array and check their operating system, but I need a structure to save the statistics. For that I’m going to use a hashtable. In that way I will be able to use the name of the operating system as the key. First I declare the hashtable.

$stats = @{}

And then I collect the stats.

foreach( $computer in $computers )
{
    $OSName = $computer.OSName
   
    if( $OSName -eq $null )
    {
        $OSName = "Unknown OS"
    }
   
    $stats[$OSName]++

    #$computer.Name + "`t" + $computer.OSName + "`t" + $computer.OSVersion
}

As I run through the array I’m checking to see if the operating system value is $null. This is the case for e.g. the virtual names of a cluster service. I’m just going to list those as an unknown operating system. The physical nodes themselves will have the operating system property set. You can uncomment the last line if you want to display information about every single computer account.

That last thing to do is to display the collected statistics. You can just echo the $stats array, but if you want to sort the output you need to use ‘GetEnumerator()’.

You should get an output similiar to this:

Name                           Value                                          
----                           -----                                          
Windows Vista™ Ultimate        1                                              
Windows 7 Ultimate             1                                              
Windows 2000 Server            2                                              
Windows Server® 2008 Enterp... 3                                              
Windows 7 Professional         3                                              
Windows Server® 2008 Standard  6                                              
Windows Vista™ Business        9                                              
Windows Vista™ Enterprise      11                                             
Windows 7 Enterprise           11                                             
Unknown OS                     24                                             
Windows 2000 Professional      26                                             
Windows Server 2003            52                                             
Windows XP Professional        321                                            

Please note that Windows Server 2003 isn’t devided into several categories like Windows Server 2008. So you don’t know how many is Standard or Enterprise Edition.

Here’s the entire script: Display Operating Systems

Bulk Attribute Change of Users in Active Directory

Prerequisites: ActiveRoles Management Shell for Active Directory must be installed on the host running this script.

As mentioned in my post “Detect Orphaned HomeDirectories and Roaming Profiles” I was given the task to migrate data from one file server to the other. Since the paths to homedirectories and roaming profiles would change I needed to come up with a solution to change the paths of hundreds of users. Of course the solution would be a powershell script.

First I want to load the snappin so I don’t have to open the ActiveRoles Management Shell everytime I want to run the script.

Add-PSSnapin -name Quest.ActiveRoles.ADManagement

Next I want to fetch the users from Active Directory. This is done with the ‘get-QADUser’ cmdlet.

$users = Get-QADuser -SearchRoot 'mydomain.local/Users' -SizeLimit 0 | Sort-Object Name

This will return an array of all the users in the searchroot (it also searches in subcontainers). I’m piping the result to the ‘Sort-Object’ cmdlet as I want to sort the array by the names of the users.

Now that the users have been fetched from Active Directory all that is left to do is to run through the array, change the different paths, and commit the changes to Active Directory.

for( $i = 0; $i -lt $users.length; $i++ )
{
    $oldHomeDirectory = ""
    $oldProfilePath = ""
    $oldTsHomeDirectory = ""
    $oldTsProfilePath = ""

    $user = $users[$i]

    if( $user.HomeDirectory -ne $null ) # only if HomeDirectory is filled in
    {
        $oldHomeDirectory = $user.HomeDirectory
        $user.HomeDirectory = $user.HomeDirectory -replace "\\\\server01\\homedirectories\$", \\server02\homedirectories$
    }
    if( $user.ProfilePath -ne $null ) # only if ProfilePath is filled in
    {
        $oldProfilePath = $user.ProfilePath
        $user.ProfilePath = $user.ProfilePath -replace "\\\\server01\\profiles\$", \\server02\profiles$
    }
    if( $user.TsHomeDirectory -ne $null ) # only if TsHomeDirectory is filled in
    {
        $oldTsHomeDirectory = $user.TsHomeDirectory
        $user.TsHomeDirectory = $user.TsHomeDirectory -replace "\\\\server01\\homedirectories\$", \\server02\homedirectories$
    }
    if( $user.TsProfilePath -ne $null ) # only if TsProfilePath is filled in
    {
        $oldTsProfilePath = $user.TsProfilePath
        $user.TsProfilePath = $user.TsProfilePath -replace "\\\\server01\\tsprofiles\$", \\server02\tsprofiles$
    }
    ...
}

The loop runs through the array and does a ‘search and replace’ on the user attributes I want to change. If you want to change anything else, here’s where you want to add it. Just type “get-QADUser -?” to see all the attributes you can change. Also I’m saving the paths in a new set of variables before I change them. The only reason for this is that I want to use them for output e.g. to a log file. The last thing to do is simply to commit the changes and write some output lines for logging purposes.

$user.commitchanges()
$user.DisplayName + " (" + $user.sAMAccountName + ") updated"
"HomeDir:`t"       + $oldHomeDirectory +   " >> " + $user.HomeDirectory
"ProfilePath:`t"   + $oldProfilePath +     " >> " + $user.ProfilePath
"TsHomeDir:`t"     + $oldTsHomeDirectory + " >> " + $user.TsHomeDirectory
"TsProfilePath:`t" + $oldTsProfilePath +   " >> " + $user.TsProfilePath
"----------------------------------------------"

Here’s the entire script: Bulk Attribute Change of Users in Active Directory