Recently, I've been challenged to add a network share to a Documents library, to essentially mimic the folder redirection the were familiar with in Windows XP, on a Windows Server 2008 Terminal Server.
Using a terminal server is the problem. To add a folder, local or remote, to a library, it must be indexed. Try it for yourself and you'll get an error stating that there's no index. Your recourse is to index it, or if it's a remote share, enable Offline Files. Offline Files? On a Terminal Server? Nope, by design.
Okay, then I'll enable Windows Search Service on the server hosting the share. It already has the File Services role enabled, so I'll just add the feature. And don't be confused if you see Windows 2003 Indexing Service, it's not the same! You'll want the Search Service feature. Then it's just a matter of adding your volume or folder via the Indexing Options in Control Panel.
Back to the Terminal Server and... what do you mean I can't add it? It's indexed! Well, you need the Search Service feature installed on the Terminal Server. But, when you go to Add Feature, it's not there. Rather, you've got to pretend you're going to install the File Server role, uncheck File Server, and check Search Service.
Now your Terminal Server can read the remote index on that remote share and you can add it to the Documents library. It all seems a little confusing to install a File Server role on a Terminal Server, doesn't it?
Friday, June 24, 2011
Tuesday, June 7, 2011
Dude, Where's My Maintenance Plan?
I just finished an in-place, cross-version, cross-SKU upgrade of SQL Server 2005 Express with Advanced Services to SQL Server 2008 R2 Standard. It was actually very easy and required about an hour's downtime for this SMB client.
However, when I went to make a quick Maintenance Plan from SSMS in order to schedule some basic tasks, I discovered I couldn't create a new plan and edit it, but I could create it via the wizard. A few clicks and about 22MB of installers later, and I realized I hadn't updated the one requirement: Management Tools - Complete. Management Tools - Basic won't let you create SSIS packages via SSMS because you've got no SQL Agent to run them!
However, when I went to make a quick Maintenance Plan from SSMS in order to schedule some basic tasks, I discovered I couldn't create a new plan and edit it, but I could create it via the wizard. A few clicks and about 22MB of installers later, and I realized I hadn't updated the one requirement: Management Tools - Complete. Management Tools - Basic won't let you create SSIS packages via SSMS because you've got no SQL Agent to run them!
![]() |
| I need all of those tools, please! |
Wednesday, May 25, 2011
Exchange Item-Level Manipulation with PowerShell
Shameless Plug
Mike Pfeiffer (blog | twitter) is a recently christened MCM for Exchange 2010. His blog is awesome. You should read it. And then when you have a problem like I have, you should leverage what you've learned.
The Problem
Building on yesterday's Linked Mailbox creation issue for an Exchange server in a resource forest, I now need to perform item level manipulation for Contacts and Calenders. We've been working on migration to Exchange 2010 from an obscure messaging system designed by a company in Redwood City, who shall not be named. My co-worker, Andrew Healey (blog | twitter), has done an excellent job solving mail item synchronization. But we're still stuck doing manual PST exports of non-mail items.
After completing our testing phase, we want to clear out all the non-mail items from user's mailboxes then perform fresh PST imports, thus avoiding any chance of item duplication. In terms of native capabilities, Exchange 2010's lowest level of granularity using PowerShell is the mailbox. So, knowing a little about EWS from another project I was considering to sync Out-of-Office replies to a Public Folder Vacation calendar, I found Mike Pfeiffer's article on this very topic. His EWSMail module is phenomenal and incredibly well documented, but he did leave some things out on purpose.
First, we had to patch Exchange 2010 to SP1. This saved some time (and was best practices) so that I didn't have to add version detection. Incidentally, you can see an example where Mike instantiated an object with that exact property.
Secondly, I needed to extend his PowerShell module to allow me to pass the EWS URL as a parameter. Adding the following snippets in the appropriate places did the job.
In the param() declarations, add the following snippet, updating the Position value accordingly:
Notes on the other hand were unique. In terms of data structures, they're the same as Email Messages, using the EmailMessage class, but they'll sit in the "Notes" folder. Go figure!
Final Details
I had to enable impersonation for my executing account:
Edit the text of that script to remove the Where-Object {$_.Name –like “Test*”} and it’ll do every mailbox. See the text of that script below.
Now, run all of this at your own risk. And if you can do better, please do so and let me know. More importantly, let Mike know. Thanks to his template, I've learned a great deal about:
And I hope the rest of you never have to migrate between systems that don't provide vendor neutral protocols for all supplied services.
I do have one apology and that is that I haven't attached my scripts here as my blogging platform doesn't support attachments and to past ~500 script inline would be beastly to read.
Mike Pfeiffer (blog | twitter) is a recently christened MCM for Exchange 2010. His blog is awesome. You should read it. And then when you have a problem like I have, you should leverage what you've learned.
The Problem
Building on yesterday's Linked Mailbox creation issue for an Exchange server in a resource forest, I now need to perform item level manipulation for Contacts and Calenders. We've been working on migration to Exchange 2010 from an obscure messaging system designed by a company in Redwood City, who shall not be named. My co-worker, Andrew Healey (blog | twitter), has done an excellent job solving mail item synchronization. But we're still stuck doing manual PST exports of non-mail items.
After completing our testing phase, we want to clear out all the non-mail items from user's mailboxes then perform fresh PST imports, thus avoiding any chance of item duplication. In terms of native capabilities, Exchange 2010's lowest level of granularity using PowerShell is the mailbox. So, knowing a little about EWS from another project I was considering to sync Out-of-Office replies to a Public Folder Vacation calendar, I found Mike Pfeiffer's article on this very topic. His EWSMail module is phenomenal and incredibly well documented, but he did leave some things out on purpose.
The cmdlets in this module could use some enhancements though. They run under the security context of the logged on user, the EWS endpoint is set using autodiscover, and the Exchange version will default to Exchange 2010 SP1. If you want to extend this code, it might be useful to add parameters for specifying alternate credentials, manually setting the EWS URL, and specifying the Exchange version.The Solution
First, we had to patch Exchange 2010 to SP1. This saved some time (and was best practices) so that I didn't have to add version detection. Incidentally, you can see an example where Mike instantiated an object with that exact property.
Secondly, I needed to extend his PowerShell module to allow me to pass the EWS URL as a parameter. Adding the following snippets in the appropriate places did the job.
In the param() declarations, add the following snippet, updating the Position value accordingly:
[Parameter(Position = 4, Mandatory = $false)] [string] $WebServicesUrlThen in the EWS Autodiscover section, I created some conditional logic:
#Determine the EWS end-point using AutodiscoverIt was after this that I discovered my biggest challenge, discovering what data structure each item used. Mike's script handles Email Messages using the EmailMessage class without problems. Typically you'll find those in the "Inbox". Calendar items use the Appointment class and reside in "Calendars", of course. Contacts are the most logical and use the Contact class and reside in the "Contacts" folders. Same with Tasks, using Task, and residing in "Tasks".
if ($WebServicesUrl -eq $null) { $service.AutodiscoverUrl($Mailbox)}
else { $service.Url = $WebServicesUrl }
Notes on the other hand were unique. In terms of data structures, they're the same as Email Messages, using the EmailMessage class, but they'll sit in the "Notes" folder. Go figure!
Note: Don't forget to update the default folder if none is specified in the params() block.Rather than creating conditional logic, I chose to duplicate the main Get-* and Remove-* scripts and update the class binding line accordingly, e.g., for Contacts, I changed:
$email = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($service, $_.Id, $emailProps)to
$email = [Microsoft.Exchange.WebServices.Data.Contact]::Bind($service, $_.Id, $emailProps)Now after adding the additional Get-* and Remove-* scripts into the EWSMail.psm1 file, and dropping the whole module folder into my $env:PSModulePath, I was ready to go!
Final Details
I had to enable impersonation for my executing account:
New-ManagementRoleAssignment –Name:impersonationAssignmentName –Role:ApplicationImpersonation –User:CONTOSO\AdministratorThen launch the EMS, and run the script below and it’ll kill 2000 items at a time for all Mailboxes with a name like “Test*”. There will be no visual confirmation of success in EMS.
Edit the text of that script to remove the Where-Object {$_.Name –like “Test*”} and it’ll do every mailbox. See the text of that script below.
Import-Module EWSMailLearning Points
$MBX = Get-Mailbox -ResultSize unlimited | Where-Object {$_.Name -like "Test*"}
$EWS = "https://exchange.corp.contoso.com/EWS/Exchange.asmx"
$Limit = 2000
$MBX | ForEach-Object {
Get-EWSCalendarItem -Mailbox $_.WindowsEmailAddress -WebServicesUrl $EWS -ResultSize $Limit | Remove-EWSCalendarItem -WebServicesUrl $EWS -Confirm:$false
Get-EWSContact -Mailbox $_.WindowsEmailAddress -WebServicesUrl $EWS -ResultSize $Limit | Remove-EWSContact -WebServicesUrl $EWS -Confirm:$false
Get-EWSTask -Mailbox $_.WindowsEmailAddress -WebServicesUrl $EWS -ResultSize $Limit | Remove-EWSTask -WebServicesUrl $EWS -Confirm:$false
Get-EWSMailMessage -Mailbox $_.WindowsEmailAddress -WebServicesUrl $EWS -ResultSize $Limit -Folder Notes | Remove-EWSMailMessage -Mailbox $_.WindowsEmailAddress -WebServicesUrl $EWS -Confirm:$false
}
Now, run all of this at your own risk. And if you can do better, please do so and let me know. More importantly, let Mike know. Thanks to his template, I've learned a great deal about:
- PowerShell Modules
- Exchange Web Services
- Object Binding and calling .NET methods from PowerShell
- PowerShell Impersonation
And I hope the rest of you never have to migrate between systems that don't provide vendor neutral protocols for all supplied services.
I do have one apology and that is that I haven't attached my scripts here as my blogging platform doesn't support attachments and to past ~500 script inline would be beastly to read.
Subscribe to:
Posts (Atom)

