Showing posts with label powershell. Show all posts
Showing posts with label powershell. Show all posts

Tuesday, 7 April 2009

Putting Shay's Powershell Registry Functions To Use

Recently I needed to check some registry key values on a bunch of servers. There were far too many servers to make this a manual task and in addition if they weren't what I was expecting then I needed to change them.

Shay Levy has very helpfully published a Stand Alone Registry Functions Library which I made use of. It allows you to query and set registry values for things such as DWords, Strings, Binary Values on remote machines very easily. In my case I was particularly interested in some configuration settings for the ICA protocol, HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\ICA-tcp, all of them DWords.

First of all we use Shay's Get-RegDWord function at the top of the script which contains some .NET code to query a remote registry


function Get-RegDWord{
param(
[
string]$server = ".",
[
string]$hive,
[
string]$keyName,
[
string]$valueName,
[
object]$defaultValue="Your default value"
)

$hives = [enum]::getnames([Microsoft.Win32.RegistryHive])

if($hives -notcontains $hive){
write-error "Invalid hive value";
return;
}
$regHive = [Microsoft.Win32.RegistryHive]$hive;
$regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($regHive,$server);
$subKey = $regKey.OpenSubKey($keyName);

if(!$subKey){
write-error "The specified registry key does not exist.";
return;
}
$subKey.GetValue($valueName,$defaultValue);
}
Then we specify the list of servers to query. You could either use something like:

$servers = Get-Content servers.txt
or in my case they were all virtual servers in VMware so I used a couple of VI Toolkit commands to specify the list

# Connect to Virtual Center
Connect-VIServer vc

# Get a list of all the powered on APP servers
$servers = Get-Folder 'Servers' | Get-VM | Where-Object {$_.Powerstate -eq 'PoweredOn'}

Then we loop through each server, call Shay's Get-RegDWord function for each of the DWords we are interested in and store the results in the $myCOl variable. At the end we export the $myCol variable into a CSV file for handy viewing.

# Create an empty array to store the results in
$myCol = @()

foreach ($server in $servers){

$hive = 'LocalMachine'
$keyname = 'System\CurrentControlSet\Control\Terminal Server\WinStations\ICA-tcp'

# Find MaxIdleTime
$valuename = 'MaxIdleTime'
$MaxIdleTime = Get-RegDWord $server $hive $keyName $valueName

# Find fInheritMaxIdleTime
$valueName = 'fInheritMaxIdleTime'
$fInheritMaxIdleTime = Get-RegDWord $server $hive $keyName $valueName

# Find MaxDisconnectionTime
$valueName = 'MaxDisconnectionTime'
$MaxDisconnectionTime = Get-RegDWord $server $hive $keyName $valueName

# Find fInheritMaxDisconnectionTime
$valueName = 'fInheritMaxDisconnectionTime'
$fInheritMaxDisconnectionTime = Get-RegDWord $server $hive $keyName $valueName

# Add the results to the $MYInfo variable, then $myCol
$MYInfo = "" | select-Object Name,MaxIdleTime,fInheritMaxIdleTime,MaxDisconnectionTime,fInheritMaxDisconnectionTime
$MYInfo.Name = $server
$MYInfo.MaxIdleTime = $MaxIdleTime
$MYInfo.fInheritMaxIdleTime = $fInheritMaxIdleTime
$MYInfo.MaxDisconnectionTime = $MaxDisconnectionTime
$MYInfo.fInheritMaxDisconnectionTime = $fInheritMaxDisconnectionTime
$myCol += $MYInfo

}

# Export the results to a csv file
$myCol | Export-Csv citrixservers.csv -NoTypeInformation

Now that we can view the results I was able to see that I needed to set a lot of these values to something new. Using Shay's Set-RegDWord function it is a pretty straightfoward task to extend the above to do that. First of all add the function to the top of the script.


function Set-RegDWord{
param(
[
string]$server = ".",
[
string]$hive,
[
string]$keyName,
[
string]$valueName,
[
double]$value
)

$hives = [enum]::getnames([Microsoft.Win32.RegistryHive])

if($hives -notcontains $hive){
write-error "Invalid hive value";
return;
}
$regHive = [Microsoft.Win32.RegistryHive]$hive;
$regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($regHive,$server);
$subKey = $regKey.OpenSubKey($keyName,$true);

if(!$subKey){
write-error "The specified registry key does not exist.";
return;
}
$subKey.SetValue($valueName, $value,[Microsoft.Win32.RegistryValueKind]::DWord);
if($?) {$true} else {$false}
}

Then for each of the values you want to change use an if statement to check whether it needs changing and if so call Shay's Set-RegDword to make the necessary changes.



    # Find fInheritMaxIdleTime
$valueName = 'fInheritMaxIdleTime'
$fInheritMaxIdleTime = Get-RegDWord $server $hive $keyName $valueName

# Check the value and change if necessary
if ($fInheritMaxIdleTime -eq 1){

$value = '0'
Set-RegDWord $server $hive $keyName $valueName $value

}

Thanks again to Shay for publishing these functions, it made this particular task very easy to complete and I'm sure I'll be using them again in the future.

Thursday, 12 March 2009

Get-Scripting Guys Take Over the March UK Powershell User Group

Myself and Alan Renouf from the Get-Scripting Podcast will be presenting this month at the UK Powershell User Group on Thursday 26th March at Microsoft in Reading.

First up on the night will be Richard Siddaway talking to us about using Regular Expressions in Powershell. This was requested at a previous event and I know that Richard is really looking forward to talking about that subject ;-)

Then the Get-Scripting guys will take over:

I will be talking about some of the features that are part of Active Directory in Server 2008 R2 , currently in beta, in particular the native AD Powershell cmdlets which will ship as part of that product and what I have learned about them so far.

vExpert Alan Renouf will then tell us about the VI toolkit, which for those of you not in the know are the cmdlets shipped by VMware to enable you to manage their ESX product.

A good mixture I think of general scripting skills, third-party added value to your Powershell learning and future technologies.

If you can't make the event in person then as usual it will be broadcast by LiveMeeting (details here). It wouldn't be a UK Powershell event if the LiveMeeting part doesn't go 100% to plan, but we will endeavour to ensure we avoid the usual trick of the microphone going on mute.

If you wish to attend the event in person you should contact Richard via his blog so that he can have a badge for you at the welcome desk. If the prospect of us presenting isn't enough to attract you then as always there will be pizza at half-time :-)

Tuesday, 10 March 2009

Today's UK VMware User Group

Today's UK VMware User Group was a great community content event. Of course there was a sponsor presentation (Veeam) without whom these type of events can't be put on, but there were also a lot of contributions from people in the group.

We had:

1) Veeam talking about their reporting and backup products.

2) Mike Laverick from RTFM education talking about Site Recovery Manager and not the VMware view of it, rather real world struggles - warts and all as he put it. Its great to see this kind of content because you get to find out how these products really function in reality. He also gave away a few copies of his book on Site Recovery Manager which I'm sure is a must read for this topic.

3) Get-Scripting's very own vExpert Alan Renouf gave us a debrief from VMworld Europe in Cannes. By the sounds of it a great event, but possibly could be extended to include an extra day and maybe needs a bigger venue given the issues some people had getting into sessions.

4) A presentation from another community member about technology futures, 'the cloud', advances in mobile devices, stuff like that, and how they may affect the corporate world. This is now the second event I have been to which has talked about employee owned IT, i.e. you turn up to a job with your own laptop and corporate IT supply you either with a VM to run corporate applications or all delivered through a browser. The demand coming from cool, young, hip people (like me :-) ) who won't stand for the constraints of a corporate environment.

"Why can't I run facebook / second life / quake / twitter on my macbook / ipod/ pink netbook and just run your apps on the same machine too?"

5) Steve Bruck and Alan Renouf (again) with an update about the recently released VI toolkit V1.5 . Some good examples, also lots of interest in Alan's getting started guide.

6) Finally finished off with more community content, this time about the upcoming vCenter Heartbeat product.


It was great to see slightly more audience participation than normal this time, which I think might have come from the slightly smaller room and more, but shorter sessions. I really enjoyed it anyway.

If you want to hear REAL people talking about how they really use VMware based products then this is the event for you.

Next one 14th May! More details can be found here.

Wednesday, 4 March 2009

Counting the Number of Sessions Per Citrix Server

Whilst monitoring some newly provisioned Citrix servers running on VMware hosts today, I soon became very bored with manually checking how many sessions were on each Citrix VM as the load on each one increased, whilst trying to get it to the optimum level.

I knew it was possible to use Powershell to connect with Citrix servers, but had never really looked into it before. Not surprisingly it turned out to be very straightforward.

By using some technology known as MFCom we can connect with the Citrix farm and get some cool information out.

In the below example we create a new com object using MFCom, then initialise the connection. We are then able to access some methods and properties of that object. In this case we are looking at the Sessions property, we group all of the results by ServerName and then produce some output with the name and number of sessions on that Citrix box.

$farm = New-Object -com "MetaframeCOM.MetaframeFarm"
$farm.Initialize(1)
$farm.Sessions | Group-Object ServerName | Sort-Object name | Format-Table Name,Count -auto
which will give you something like:

Name Count

CitrixServer01 38
CitrixServer02 45
CitrixServer03 41

This would return all of the servers in the farm. In this particular instance I only wanted a particular selection of servers, so I stored them in a text file, got PS to read that file and then filter the query by only looking at servers in that list.


$servers = Get-Content c:\scripts\servers.txt

$farm = New-Object -com "MetaframeCOM.MetaframeFarm"
$farm.Initialize(1)
$farm.sessions | Where-Object {$servers -contains $_.ServerName} | Group-Object ServerName | Sort-Object name | Format-Table Name,Count -auto

Once again Powershell very easily gets rid of a really dull manual task.

If you wish to take this a step further check out Powershell MVP Brandon Shell's blog where he has loads of Powershell / Citrix examples.

http://bsonposh.com/archives/tag/citrix

Sunday, 22 February 2009

UK Powershell User Group Live Meeting - 7pm GMT on Thursday February 26th 2009

Don't forget 7pm GMT on Thursday February 26th sees Rolf Masuch presenting a Live Meeting to the UK PowerShell User group. Rolf runs the German PowerShell User Group.

Session abstract:

  • PowerShell as Active Directory Login Script
  • Loginscript, why?
  • The Script
  • Draft in the form editor
  • Start with PowerGUI Editor
  • The script skeleton
  • The script details
  • Get in running
  • Output of information
  • Formatting of the information
  • Putting the script on the server
  • The path on the domain controller
  • Setting the users login script
  • Running .ps1-files as login scripts
  • Running loginscripts visible
  • Possible errors

Live Meeting Details:

You are invited to attend an online meeting using Live Meeting.
Join the meeting.
Audio Information
Computer Audio
To use computer audio, you need speakers and microphone, or a headset.
First Time Users:
To save time before the meeting,
check your system to make sure it is ready to use Microsoft Office Live Meeting.
Troubleshooting
Unable to join the meeting? Follow these steps:

  1. Copy this address and paste it into your web browser:
    https://www.livemeeting.com/cc/usergroups/join
  2. Copy and paste the required information:
    Meeting ID: 2C4W7Q
    Entry Code: FbH&3S\&C
    Location: https://www.livemeeting.com/cc/usergroups

If you still cannot enter the meeting, contact support

Notice
Microsoft Office Live Meeting can be used to record meetings. By participating in this meeting, you agree that your communications may be monitored or recorded at any time during the meeting.

Saturday, 21 February 2009

Slides from MMMUG presentation

As promised to those who attended the MMMUG on Wednesday night my slides from that evening are available on my SkyDrive.

Enjoy.

Sunday, 15 February 2009

The Noble Array?

At last week's UK Powershell Usergroup Jonathan Noble was showing us some Powershell examples and at one point demonstrated something similar to the below.

Essentially you create an empty array, but then somewhat surprisingly (well to me anyway) you can select some elements even though they don't exist! You are then able to add to the array using the names you have selected.


$MYInfo = "" | select-Object Name, CPUUsage,Owner, ProcessID
$MYInfo.Name = $proc.name
$MYInfo.ProcessID = $proc.IdProcess
$MYInfo.CPUUsage = [Math]::Round($proc_perct, 0)
$MYInfo.Owner = $process.GetOwner().user
$myCol += $MYInfo


My friend and colleague Alan Renouf had given me the above example a week or so previously to use in a script. I was puzzled to how it worked since I hadn't seen it used in any of the Powershell books I have read or other sources I use and neither had he, so when I saw Jonathan using it I was curious to find out more.

Jonathan explains in more detail how this works on his blog in a posting he made last year.

OK, it doesn't really matter if it wasn't him who come up with the original idea, but heh that's how I'm going to remember it. :-)

Monday, 2 February 2009

Upcoming for the UK Powershell User Group

Coming up in February are two events for the UK Powershell User Group.

1) Following the Technet Event Managing Windows Servers with Powershell V2 on Feb 10th at Microsoft London there will be a Powershell UserGroup meeting.

My good friend Jonathan Noble will be travelling down all the way from the North of England to present for us about using Powershell to automate tasks in the large University environment he works in - well worth turning up for. Let Richard Siddaway know if you wish to attend.

Sometime between the afternoon Technet Event and the evening User Group I shall be interviewing Jonathan for the Get-Scripting Podcast.

2) February 26th Rolf Mausch, who runs the German Powershell User Group, will be presenting a live webcast for us about using Powershell in login scripts - an interesting topic since not a lot of people are doing that yet from what I have seen. Early details here.

Wednesday, 28 January 2009

Using Powershell to Find Free Space in Exchange 2003 databases

One regular task for Exchange admins can be reclaiming free space within Exchange mailbox stores either after a large amount of data has been removed or just a significant amount has built up over time for various reasons. For those of you who don't know, to reclaim the space the mailbox store has to be taken offline and the database defragged to get the space back on the disk.

Finding good candidates for defragging especially in a large environment with multiple databases and Exchange servers can be a pretty tedious task. Exchange records an entry in the Application Event Log (Event ID 1221) after online maintenance has taken place which tells you how much free space is in the database - this means trawling through the event logs on each server and recording which databases you think are worth defragging. So naturally I wrote a Powershell script to save wasting time on this task!

Using WMI the script queries the Application log on each of the Exchange servers you choose, looking for 1221 events in the last day. It sucks out the name of the mailbox store and the amount of free space in the database from the message field of the event and if the free space is greater than a particular figure (in the example below 3GB) adds the info to a csv file.

You could obviously change the figure to meet your needs and also if you remove the if statement you could get it to report on every database so you have a report of free space across all databases.

Tip: If you have clustered mailbox servers then you only need to point the script at one of the servers in the cluster since it will contain all of the event log entries for each server in the cluster.




#Check to see if csv exists and if so remove it
If (Test-Path "FreeSpaceGreaterThan1GB.csv")
{
Remove-Item "FreeSpaceGreaterThan1GB.csv"
}
Else
{
}

#Set the columns for the csv file
$rows = "Servername," + "Mailbox Store," + "Free Space (MB),"
Add-Content FreeSpaceGreaterThan1GB.csv $rows

$ExchServer = 'server1','server2'
foreach ($Server in $ExchServer){

#Get the time 1 day ago in the right format for WMI query
$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::Now.AddDays(-1))
#Perform WMI query of Event 1221 in Application log in the last day
$1221 = Get-WmiObject -computer $ExchServer -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1221' and TimeWritten >='" + $WmidtQueryDT + "'")

foreach ($event in $1221){

#Get the name of the Mailbox Store
$MBXStoreLocationStart = $event.Message.IndexOf("Storage Group") + 16
$MBXStoreLocationFinish = $event.Message.IndexOf("has") - 2
$MBXStoreLocation = $event.Message.SubString($MBXStoreLocationStart, $MBXStoreLocationFinish - $MBXStoreLocationStart)

#Get the free space figure and convert it to an integer
$MBLocationStart = $event.Message.IndexOf("has") + 4
$MBLocationFinish = $event.Message.IndexOf("megabytes") - 1
$MBLocation = $event.Message.SubString($MBLocationStart, $MBLocationFinish - $MBLocationStart)
$result = [int]$MBLocation

$ComputerName = $event.ComputerName


#If free space > 3GB, add the details to the csv file
if ($result -ge 3072){

$rowline = "$ComputerName," + "$MBXStoreLocation," + "$MBLocation,"
Add-Content FreeSpaceGreaterThan1GB.csv $rowline
}
else
{
}

}
}

Sunday, 25 January 2009

Using Powershell to Monitor VMware Guests - on a Budget...

...i.e. a budget of £0.

(Update 28/01/09 - FYI..got some feedback about this post and the reason we are not using the built in alerts in the VI client is because the CPU alerts in this case were not granular enough for us.)

So this all stemmed from trying to track down which process was causing particular servers' CPU to hit 100% for a period. So first of all my colleague and Get-Scripting co-host Alan Renouf traded a script back and forth which ended up as the CheckHighCPU function - it is now pretty cool and comes back with a list of processes sorted by how much CPU they are using and very importantly for our circumstance who is the owner of each process.

The servers in question all belong to a particular cluster in ESX. Rather than constantly having to monitor the console, wait for a VM to turn red and then run the script to track down the process we decided to try and monitor them with a Powershell script, kick off the CheckHighCPU function when a server's CPU hit 100% for a significant enough period and email a warning through with the process details - and so the below script was born.

OK, its not Operations Manager and to be honest its not really production quality, but it does the job we need it to.

The VI Toolkit from VMware is a great set of cmdlets you can plug into your Powershell console to manage your VMware environment. You can use the Get-Cluster and Get-VM cmdlets to return a list of all the VM's in that cluster as objects. You can then use the very handy Get-Stat cmdlet to retrieve performance data for each VM.

In this case we check the cpu.usage.average statistic of a period of the last few minutes (watch out for the -IntervalMins parameter it can produce a period slightly different to what you would expect) and if its over 99% run the CheckHighCPU function and send the results by email.

We then make the script sleep for a short time period so that we are not constantly bombarded with alerts if a warning is triggered.

Obviously if we wanted to make it production quality we would add in some error checking and testing to see if an alert had recently been sent, but for the time being its doing a great job for the requirements that exist.

You could obviously re-use the below to monitor for different statistics offered by Get-Stat like disk or memory.



Function EmailWarning ()
{
param ($ServerName,$Attachment)
#Email warning
Write-Output "Creating E-Mail Structure"

$smtpServer = "servername"

$msg = new-object Net.Mail.MailMessage
$att = new-object Net.Mail.Attachment($attachment)
$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$msg.From = "sender"
$msg.To.Add("recipient")
$msg.Subject = "Server Warning - High CPU on $Servername"
$msg.Body = "$Servername has a CPU value of $HighCPU %"
$msg.Attachments.Add($att)

Write-Output "Send E-Mail"
$smtp.Send($msg)

$att.Dispose();

}

Function CheckHighCPU ()
{
param ($Target)

$procs_total = Get-WmiObject -Class Win32_PerfRawData_PerfProc_Process -Filter 'name="_total"' -ComputerName $Target
$procs = Get-WmiObject -Class Win32_PerfRawData_PerfProc_Process -Filter 'name<>"_total"' -ComputerName $Target

[
int64]$totalpercentuser = 0
foreach ($proc in $procs_total)
{
$totalpercentuser = $totalpercentuser + $proc.PercentUserTime}

[
decimal] $perc = [System.Convert]::ToDecimal($totalpercentuser)

$myCol = @()
Foreach ($proc in $procs){
$proc_perct = (($proc.PercentUserTime / $perc) * 100)
if ($proc_perct -gt 1){
$Process = Get-WmiObject win32_process -ComputerName $target | where {$_.ProcessID -eq $proc.IDProcess}
$MYInfo = "" | select-Object Name, CPUUsage,Owner, ProcessID
$MYInfo.Name = $proc.name
$MYInfo.ProcessID = $proc.IdProcess
$MYInfo.CPUUsage = [Math]::Round($proc_perct, 0)
$MYInfo.Owner = $process.GetOwner().user
$myCol += $MYInfo
}
}

$myCol | Sort-Object CPUUsage -Descending | Out-File $file
EmailWarning $VMname $file
}

Connect-VIServer servername
$vms = Get-Cluster clustername | get-vm
$time = Get-Date

do {

foreach ($vm in $vms){

$VMname = $vm.name
$filename = $VMname + '.txt'
$file = "C:\Scripts\$filename"
$stats = Get-Stat -entity $vm -IntervalMins 2 -stat cpu.usage.average -MaxSamples 1
write-host $VMname
$stats

if ($stats.value -ge 99){
$HighCPU = $stats.value
Write-Host "Warning!" -ForegroundColor red
CheckHighCPU
$VMname
}

else
{
}
}

Start-Sleep -Seconds 30
}

until ($time.hour -ge 17)

Presenting at MM&M User Group UK - Wednesday 18th February

So I was lucky enough to receive an invite from Nathan Winters who runs the MM&M User Group UK (aka Exchange) to present at their next meeting on Wednesday 18th February at Microsoft in London.

It will be an evening around using Powershell to manage Exchange, the agenda is as below:

18:15 - 18:40 Arrival

18:40 - 18:45 Introduction to speakers and the aims of the group

18:45 - 19:30 1st session; Jonathan Medd, Introduction to PowerShell and Using PowerShell to manage Exchange 2003!

19:30 - 19:50 Food!

19:50 - 20:45 2nd session; Will Rawlings, Causing no harm with PowerShell, and using PowerShell on a large Exchange environment

20:45 - 21:00 Summing up and suggestions for future meetings.

21:00 The End!


If you want to attend you can sign up here http://www.mmmug.co.uk/forums/thread/26352.aspx

Tuesday, 13 January 2009

UK Powershell User Group - January Meeting

The January meeting of the UK Powershell User Group takes place Wednesday 21st Jan 2009 6.30pm GMT.

Memphis Room

Building 3

Microsoft Campus TVP

Reading UK

We have a Live Meeting with Jeffrey Snover talking about PowerShell v2

Pizza break

Then Jeremy Pack from HP will then be doing a PowerShell demo - exact topic to be confirmed


It should be a great evening. Jeffrey is obviously the man to ask if you have any burning questions about Powershell, particularly V2 for this event. Jeremy is a long time member of the user group and is incredibly knowledgeable about Powershell so I'm really looking forward to seeing what he is going to talk about.

If you want to turn up please contact Richard Siddaway at the below website, you can also find details of the webcast if you wish to watch from afar.

http://richardsiddaway.spaces.live.com/blog/cns!43CFA46A74CF3E96!1987.entry

Sunday, 11 January 2009

Modifying AD accounts with Powershell after an Exchange 2003 dial-tone restore

Recently I've been testing out some different disaster recovery scenarios for Exchange 2003, one of which involved a dial-tone method - i.e. create some new mailbox servers with blank databases to get users up and running quickly and then merge the restored data back in later. One of the types of dial tone method we used was to create new server names rather than re-use existing Exchange server names.

So for example to re-create a four node (3 active, 1 passive) cluster with new names, instead of

ExchangeServer1
ExchangeServer2
ExchangeServer3

you would now use something like

ExchangeServer1New
ExchangeServer2New
ExchangeServer3New

Then you would need to amend the AD user accounts for users on those Exchange Servers to point to the new locations - the following properties need to be changed.

homemdb
msexchhomeservername
homemta

None of these properties can be changed through ADUC, you would need to use ADSIEdit if you wanted to use a GUI. Of course those smart people among you would choose to user Powershell anyway.

So naturally I turned to my trusty friend the Quest AD cmdlets to help me out.

First of all we get all the users who have a mailbox based on one of the original servers; depending on your naming convention you may need to adjust this filter to make sure you are matching the correct people. The three properties mentioned are not returned by default from Get-QADUser so we have to specify them.

We then loop through each user and using the Switch statement if we match ExchangeServer1, 2 or 3 we amend the text of each variable to be the new Exchange servername (note: homemta will be the same for all of these users) and then user the Set-QADUser cmdlet to change these properties on the account.

$users = Get-QADUser -ldapFilter '(msExchHomeServerName=*ExchangeServer*)' -IncludedProperties homemdb,msexchhomeservername,homemta -sizelimit 0

foreach($user in $users){

$homemdb = $user.homemdb
$msexchhomeservername = $user.msexchhomeservername
$newhomemta = 'CN=Microsoft MTA,CN=ExchangeServer1New,CN=Servers,CN=Exchange,CN=Administrative Groups,CN=Springfield,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=springfield,DC=local'

switch -wildcard ($homemdb)

{
"*ExchangeServer1*" {$newhomemdblocation = $homemdb.replace("ExchangeServer1","ExchangeServer1New"); $newmsexchhomeservername = $msexchhomeservername.replace("ExchangeServer1","ExchangeServer1New"); Set-QADUser $user -objectAttributes @{homemdb=$newhomemdblocation;msexchhomeservername=$newmsexchhomeservername;homemta=$newhomemta}; break}
"*ExchangeServer2*" {$newhomemdblocation = $homemdb.replace("ExchangeServer2","ExchangeServer2New"); $newmsexchhomeservername = $msexchhomeservername.replace("ExchangeServer2","ExchangeServer2New"); Set-QADUser $user -objectAttributes @{homemdb=$newhomemdblocation;msexchhomeservername=$newmsexchhomeservername;homemta=$newhomemta}; break}
"*ExchangeServer3*" {$newhomemdblocation = $homemdb.replace("ExchangeServer3","ExchangeServer3New"); $newmsexchhomeservername = $msexchhomeservername.replace("ExchangeServer3","ExchangeServer3New"); Set-QADUser $user -objectAttributes @{homemdb=$newhomemdblocation;msexchhomeservername=$newmsexchhomeservername;homemta=$newhomemta}; break}
default {"Nothing for this user"}
}


}

I was also interested to see the resulting performance of this script and was pleasantly surprised to see it change 6000+ accounts in only 10 mins.

A sidenote to this method is that you won't actually see the mailboxes appear in Exchange System Manager until either they receive an email or a user logs on to them. To prove that this method had worked I created a quick Distribution Group, used the below one-liner to populate it with all of the above users and then sent an email to this group.

Get-QADUser -ldapFilter '(msExchHomeServerName=*ExchangeServer*)' -sizelimit 0 | Add-QADGroupMember TestGroup


There are of course many different ways to carry out Exchange DR, but this proved a useful exercise.