Restoring user data of moved users

After moving users from a Lync Server 2010 pool to a newly created Lync Server 2013 pool, some of them lost their contacts. This symptom happens usually when selecting the “Force” option when executing the move. Note the warning which is displayed when selecting the “Force” option:

clip_image001

Of course you should have created a back-up before executing a (bulk) move of users. If you did this using DBIMPEXP.EXE you are saved! The steps to restore the data are as follows:

  • Convert the Lync 2010 export to a Lync 2013 format
  • Update the user data
Converting the user data

Because DBIMPEXP.exe is deprecated from Lync 2013 we have to use another mechanism to import the data. To export, import or restore data, Lync 2013 uses the following cmdlets:

  • Export-CsUserData
  • Import-CsUserData
  • Update-CsUserData

These cmdlets, however, do not accept the data exported with DBIMPEXP.exe. To convert the data, the Convert-CsUserData cmdlet has to be used. Because a typical export contains a lot of data, it’s recommended to filter out only the data of the affected user out of the export. This can be done straight away during the conversion or later during the import.

To convert the data, execute the following:

Convert-CsUserData -InputFile D:\Setup\Backup_Restore\dbimpexp.xml -OutputFile D:\Setup\Backup_Restore\dbimpexp.zip -UserFilter "<UserSipUri@somedomain.com>" -TargetVersion current
 
Update the user data

The above converts the DBIMPEXP XML file to a ZIP file that can be read by the Lync 2013 cmdlets. To import this data, the Import-CsUserData or Update-CsUserData cmdlets can be used. Because the user is already moved, still functional and only lacking data, the Update-CsUserData is the cmdlet of choice.

Execute the following:

Update-CsUserData -FileName D:\Setup\Backup_Restore\dbimpexp.zip –UserFilter "<UserSipUri@somedomain.com>"
 

After execution, the user’s contacts are restored.

clip_image003

Restoring the voicemail service

We are in the middle of migrating our Lync Server 2010 infrastructure to Lync Server 2013, our Exchange servers are already up to par. After having deployed the new pool we moved some pilot users to it. Initially everything seemed to be working fine but after some time few users started to notice they couldn’t call their voicemail. Mmm…

After a first, quick, examination of the Eventlog of both the Exchange and Lync servers showed the following errors:

Exchange:

clip_image002

clip_image004

Lync:

clip_image005

clip_image006

Exchange and Lync seem not to be able to establish a MTLS session because there is something wrong with the security certificate of the Lync servers. After examining all certificates and verifying they were trusted by all hosts involved, I couldn’t get my finger behind the problem. Two days of additional research didn’t yield any further results…

A colleague, who is one of the few Exchange 2013 MCM’s, took a look at the problem and suggested running ExchUCUtil.ps1 with the “verify” switch. This immediately revealed (and corrected!) a problem:

clip_image008

I had forgotten to create a new IP Gateway object in Exchange for the new Lync 2013 server pool. Shame on me…

After having been the subject of ridicule for a short time, I went on testing but again, no joy. Voicemail still wasn’t working. Exchange UM produced the same warnings every time a voicemail call was executed. Especially the events with ID 1079 (source “MSExchange Unified Messaging”) caught my attention:

clip_image010

clip_image012

These events indicated a problem with the Speech Engine which seems to be missing something (“The given key was not present in the dictionary.”). Closer examination of one of the events pointed me in the direction of a language problem: “…was not found for the locale with ID: 1043”. 1043 is the ID of the Dutch language and since the company I am working for is located in the Netherlands…

After changing the locale of my mailbox to English (which was indeed Dutch), the voicemail service started working as expected. The conclusion the problem was related to language settings sounded justified.

A closer look at the Exchange UM servers indeed showed that the Dutch UM language pack was not installed. An even closer look also revealed that the dial plan used had the default audio language set to Dutch and, because this language was not installed, displayed it as ‘1043’.

clip_image014

As it turned out, the installation of the Dutch language pack was overlooked during the migration of Exchange Server 2010 to Exchange Server 2013. Since the dial plan was created using Exchange Server 2010, which did have this language pack installed, “1043” was still the language of choice for Exchange UM although “The given key was not present in the dictionary”.

The question was still why users homed on the Lync 2010 pool were able to use voicemail? After asking them it turned out they weren’t. Because only a minority of our users is enabled for Exchange UM, and the users who are hardly use it (they rely mostly on the voicemail offered by their mobile phone provider), no one had noticed.

Installing the Dutch language pack solved the voicemail problem.

Reporting on mailbox moves

When migrating a (large) batch of mailboxes, either in a (cross-forest) migration or database-to-database move in Exchange 2013 or 2010, it can be time consuming to obtain the status of the entire batch. You need to enter several Powershell commands to find out how many moves have finished and how many are still running. For this purpose, I have created a script which makes monitoring mailbox moves a lot easier by pulling together information about these mailbox moves and creating a nice HTML report with that information and some useful statistics. The HTML report can be saved locally on disk, but even more useful is to have it e-mailed to you so you can monitor the status on your phone or tablet :-)

The resulting HTML file/message contains 5 sections:

- Mailbox moves, grouped by status

- Active mailbox moves + statistics

- Statistics for completed moves

- Fastest 20 statistics

- Slowest 20 statistics

 

The script source code is provided below. Feel free to use it or modify it to your needs. If you think you have a great idea for this report, please feel free to contact me!

Examples

  • Generate a report for all mailbox move requests in a batch which have a name starting with ’201306′ and e-mail the report every 5 minutes to an administrator and project manager. The e-mail is sent using the local SMTP service which of course needs to be running.
.\Report-MoveRequestStatus -SMTPTo 'admin@contoso.com,projectmanager@contoso.com' -BatchName '201306*' -SendMailEveryxMinutes 5
  • Generate a report for all mailbox move requests in a batch which have a name starting with ’201306′, save it to a unique file "MailboxMoveReport.html" with a timestamp in the current folder every 5 minutes and e-mail the report every 30 minutes (default) to an administrator and project manager. The e-mail is sent using the local SMTP service which of course needs to be running.
.\Report-MoveRequestStatus -SMTPTo 'admin@contoso.com,projectmanager@contoso.com' -BatchName '201306*' -SaveReportEveryxMinutes 5
  • Generate a report for all mailbox move requests, save it to "C:\MoveReports\MailboxMoveReport.html" every 5 minutes (overwriting the previous one).
.\Report-MoveRequestStatus -MoveReportOutputFile "C:\MoveReports\MailboxMoveReport.html" -AppendTimeStampToReportFile:$false -SaveReportEveryxMinutes 5 -SendMailEveryxMinutes 0

Source code

<#
    .SYNOPSIS
	Report-MoveRequestStatus - Version 1.3, November 15th, 2013
	
	Maarten Piederiet - Microsoft Certified Solutions Master: Messaging & Microsoft Certified Master: Exchange 2010
	
	THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
    .DESCRIPTION
	This script checks Exchange 2010 move requests and creates an HTML report with useful statistics. The report can be saved to file and sent via e-mail to a given list of recipients.
    .EXAMPLE
	.\Report-MoveRequestStatus -SMTPTo 'admin@contoso.com,projectmanager@contoso.com' -BatchName '201306*' -SendMailEveryxMinutes 5
	
	Generates a report for all mailbox move requests in a batch which have a name starting with '201306' and e-mails the report every 5 minutes to an administrator and project manager.
    .EXAMPLE
	.\Report-MoveRequestStatus -SMTPTo 'admin@contoso.com,projectmanager@contoso.com' -BatchName '201306*' -SaveReportEveryxMinutes 5
	
	Generates a report for all mailbox move requests in a batch which have a name starting with '201306', saves it to a unique file with a timestamp every 5 minutes and e-mails the report every 30 minutes (default) to an administrator and project manager.	
    .EXAMPLE
	.\Report-MoveRequestStatus -MoveReportOutputFile "C:\MoveReports\MailboxMoveReport.html" -AppendTimeStampToReportFile:$false -SaveReportEveryxMinutes 5 -SendMailEveryxMinutes 0
	
	Generates a report for all mailbox move requests, saves it to a file every 5 minutes (overwriting the previous one).
    .INPUTS
	See Parameters
    .OUTPUTS
	(Configurable) HTML report saved to file and/or sent as e-mail to list of recipients
    .NOTES
	Script needs to run in Exchange Management Shell (EMS)
    .LINK

http://uccexperts.com

#>

param (
	# Name of the SMTP mail server. Defaults to 'localhost'.
	[parameter(Mandatory=$false)][string]$SMTPServer = "localhost",
	# The SMTP sender for the report. Defaults to 'MoveRequestReport@exchange.internal'
	[parameter(Mandatory=$false)][string]$SMTPFrom = "MoveRequestReport@exchange.internal",
	# (Comma-separated) list of recipients. Required.
	[parameter(Mandatory=$false)][string]$SMTPTo = "",
	# The messages subject for the e-mail report. " - BatchName: <batchname parameter>" will be added to the subject if populated. Default subject is 'Mailbox Move Report'
	[parameter(Mandatory=$false)][string]$MessageSubject = "Mailbox Move Report",
	# Move Request Batch name to base report upon. Leave empty to report on all Move Requests. Value may include wildcards. Default is empty.
	[parameter(Mandatory=$false)][string]$BatchName="",
	# Name of the report HTML file. Defaults to MailboxMoveReport.html in current directory.
	[parameter(Mandatory=$false)][string]$MoveReportOutputFile=".\MailboxMoveReport.html",
	# Append timestamp (in sortable format) to report file. If set to false, the report file will be overwritten each cycle. Enabled by default.
	[parameter(Mandatory=$false)][switch]$AppendTimeStampToReportFile=$true,
	# If changed to a value higher than 0, a HTML file report will be written on the specified interval. By default, the report will not be written to a HTML file.
	[parameter(Mandatory=$false)][int]$SaveReportEveryxMinutes=0,
	# If changed to a value higher than 0, a HTML report will be e-mailed on the specified interval. By default, the report will be e-mailed every 30 minutes.
	[parameter(Mandatory=$false)][int]$SendMailEveryxMinutes=30
)
 
#Requires -Version 2.0

$Global:iCounter=0
$Global:Abort=$false

# Helper function to convert multivalued properties to a separated string. Separator configurable
Function Prepare-CSVOutput {
	[cmdletbinding()]
	Param(
		[Parameter(Position=0,Mandatory=$True,HelpMessage="Please specify an object",
		ValueFromPipeline=$True)]
		[object]$InputObject,
		[string]$MVSeparator=";"
	)

	Process {
		$OutputObject=New-Object PSObject
		#get property names
		$names=$InputObject | get-member -MemberType properties | select-object -expandproperty name 
	 
		#go through the list of names and add each property and value to the output object
		$names | foreach-object {
		   Write-Debug "Prepare-CSVOutput: Adding property $_"
		   $OutputObject | Add-Member -type Noteproperty -Name $_ -Value ($InputObject.$_ -join $MVSeparator)
		}
		write-Output $OutputObject
	}
}

function Out-MoveRequestHTMLReport {
	[cmdletbinding()]
	Param(
		[Parameter(Position=0,Mandatory=$True,HelpMessage="Please specify an object",ValueFromPipeline=$True)]
		[object[]]$MoveRequestsToReport
	)
	
	# Stylesheet
	$Head = "<style>BODY{font-family: Calibri; font-size: 10pt;}"
	$Head = $Head + "TABLE{border: 1px solid black; border-collapse: collapse;}"
	$Head = $Head + "TH{border: 1px solid black; background: #dddddd; padding: 5px; }"
	$Head = $Head + "TD{border: 1px solid black; padding: 5px; }"
	$Head = $Head + "</style>"
	
	Write-Verbose "Grouping move requests"
	$MoveStatus="<B>$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Mailbox moves, grouped by status:</B><BR>"
	$MoveRequestByGroup=$MoveRequestsToReport | Group Status

	# Generate table for move requests grouped by status
	$GroupedRequestsHTML=($MoveRequestByGroup|prepare-csvoutput|select name,count,@{n="Group";e={if(($_.Group).Length -gt 400){"$(($_.Group).SubString(0,400))..."}else{$_.Group}}}|convertto-html -Fragment)
	# Add total row
	$GroupedRequestsHTML=($GroupedRequestsHTML|out-string).Replace("</table>","<tr><td><b>Total</b></td><td><b>$($MoveRequestsToReport.Count)</b></td><td></td></tr>`n</table>")
	# Insert graphic bar
	
	# Character to use for the bar
	[string]$g=[char]9608

	# Color table for move statuses:
	# Black: None
	# Purple: AutoSuspended
	# Purple: Suspended
	# Grey: Queued
	# Yellow: InProgress
	# GreenYellow: CompletionInProgress
	# DarkGreen: Completed
	# Orange: CompletedWithWarning
	# Red: Failed
	$ColorReplacements=@(@("None","Black","White"),@("AutoSuspended","Purple","White"),@("Suspended","Purple","White"),@("Queued","Gray","White"),@("InProgress","Yellow","Black"),@("CompletionInProgress","GreenYellow","Black"),@("Completed","DarkGreen","White"),@("CompletedWithWarning","Orange","Black"),@("Failed","Red","White"))

	# Initialize counters
	$StatusNone=0
	$StatusSuspended=0
	$StatusQueued=0
	$StatusInProgress=0
	$StatusCompletionInProgress=0
	$StatusCompleted=0
	$StatusCompletedWithWarnings=0
	$StatusFailed=0

	# Enumerate Mailbox Moves and populate counters
	[int]$TotalMoves=(($MoveRequestByGroup|Measure -sum Count).Sum)
	foreach ($MoveStatusGroup in $(@($MoveRequestByGroup))) {
		switch ($MoveStatusGroup.Name) {
			"None" {$StatusNone+=$MoveStatusGroup.Count}
			{$_ -like "*Suspended"} {$StatusSuspended+=$MoveStatusGroup.Count}
			"Queued" {$StatusQueued+=$MoveStatusGroup.Count}
			"InProgress" {$StatusInProgress+=$MoveStatusGroup.Count}
			"CompletionInProgress" {$StatusCompletionInProgress+=$MoveStatusGroup.Count}
			"Completed" {$StatusCompleted+=$MoveStatusGroup.Count}
			"CompletedWithWarnings" {$StatusCompletedWithWarnings+=$MoveStatusGroup.Count}
			"Failed" {$StatusFailed+=$MoveStatusGroup.Count}
		}
	}

	# Initialize the graphical bar
	$ProgressBar=('<Font Color=Black>{0}</Font><Font Color=Purple>{1}</font><Font Color=Gray>{2}</font><Font Color=Yellow>{3}</font><Font Color=GreenYellow>{4}</font><Font Color=DarkGreen>{5}</font><Font Color=Orange>{6}</font><Font Color=Red>{7}</font>' -f ($g*[math]::Round(($StatusNone/$TotalMoves)*100,0)),($g*[math]::Round(($StatusSuspended/$TotalMoves)*100,0)),($g*[math]::Round(($StatusQueued/$TotalMoves)*100,0)),($g*[math]::Round(($StatusInProgress/$TotalMoves)*100,0)),($g*[math]::Round(($StatusCompletionInProgress/$TotalMoves)*100,0)),($g*[math]::Round(($StatusCompleted/$TotalMoves)*100,0)),($g*[math]::Round(($StatusCompletedWithWarnings/$TotalMoves)*100,0)),($g*[math]::Round(($StatusFailed/$TotalMoves)*100,0)))
	
	# "inject" the graphical bar into the original table
	$GroupedRequestsHTML=($GroupedRequestsHTML|out-string).Replace("</th></tr>","</th></tr>`n<tr><td colspan=""3"" align=""left"">$ProgressBar</td></tr>")
	
	# Replace the font color for each item in the table as legenda
	foreach ($ColorReplacement in $ColorReplacements) {
		$GroupedRequestsHTML=$GroupedRequestsHTML.Replace("<td>$($ColorReplacement[0])</td>","<td><font style=""background-color: $($ColorReplacement[1])"" color=$($ColorReplacement[2])>$($ColorReplacement[0])</font></td>")
	}

	$MoveStatus+=$GroupedRequestsHTML
	
	Write-Verbose "Active move statistics"
	# Add active mailbox move statistics
	$MoveStatus+="<BR><BR><B>$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Active mailbox moves:</B><BR>"
	$ActiveMoves=$MoveRequestsToReport | ?{$_.Status -eq "Inprogress"}
	if ($ActiveMoves) {
		$MoveStatus+=$ActiveMoves | Get-MoveRequestStatistics | select displayname,status,StartTimeStamp,totalmailboxsize,BytesTransferred,ItemsTransferred,percentcomplete,BadItemsEncountered| ConvertTo-HTML -fragment
	} else {
		$MoveStatus+="<I>No active mailbox moves to report</I><BR>"
	}
	
	Write-Verbose "Completed move statistics"
	# Add completed mailbox move statistics
	$CompletedMoves=$MoveRequestsToReport | Where { $_.Status -eq "Completed"}
	if ($CompletedMoves) {
		$MoveStats=$CompletedMoves | Get-MoveRequestStatistics | select *,@{n="Speed (MB/min)"; e={ [int]($_.BytesTransferred.ToMB() / $_.TotalInProgressDuration.TotalMinutes) }}
	} else {
		$MoveStatus+="<I>No completed mailbox moves to report</I><BR>"
	}

	Write-Verbose "Completed move bad item and average speed statistics"
	$MoveStatus+="<BR><BR><B>$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Statistics for completed mailboxes:</B><BR>"
	$BadItemStats=$MoveStats|Measure -average -max -min -sum BadItemsEncountered
	$SpeedStats=$MoveStats|Measure -average -max -min "Speed (MB/min)"
	$BadItemStats=$BadItemStats|select @{n="Value";e={"Bad Items"}},Sum,Minimum,Maximum,@{N="Average";E={[math]::Round($_.Average,2)}}
	$SpeedStats=$SpeedStats|select @{n="Value";e={"Mailbox copy speed (MB/min)"}},Sum,Minimum,Maximum,@{N="Average";E={[math]::Round($_.Average,2)}}
	$MoveStatus+=@($BadItemStats,$SpeedStats)|ConvertTo-HTML -fragment

	Write-Verbose "Fastest 20 move statistics"
	# Add fastest 20
	$MoveStatus+="<BR><BR><B>$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Fastest 20 mailbox moves:</B><BR>"
	if ($CompletedMoves){
		$SortedStats=$MoveStats| Sort CompletionTimestamp | Select DisplayName,TotalMailboxSize,ItemsTransferred,CompletionTimeStamp,BytesTransferred,TotalInProgressDuration,"Speed (MB/min)"|sort "Speed (MB/min)" -desc
		$MoveStatus+=$SortedStats|select -first 20|ConvertTo-HTML -fragment
	} else {
		$MoveStatus+="<I>No completed mailbox moves to report</I><BR>"
	}	

	Write-Verbose "Slowest 20 move statistics"
	# Add slowest 20
	$MoveStatus+="<BR><BR><B>$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Slowest 20 mailbox moves:</B><BR>"
	if ($CompletedMoves){
		$MoveStatus+=$SortedStats|sort "Speed (MB/min)"|select -first 20|ConvertTo-HTML -fragment
	} else {
		$MoveStatus+="<I>No completed mailbox moves to report</I><BR>"
	}

	# Complete HTML generation
	$MoveStatus=ConvertTo-HTML -Head $Head -Body $MoveStatus

	Return $MoveStatus
}

##### Script body starts here #####

# Append Batch name to message subject if specified
if ($BatchName) {
	$MessageSubject+=" - BatchName: $BatchName"
}

Write-Host "$(Get-Date -f "[yyyyMMdd HH:mm:ss]") $([string]$MyInvocation.MyCommand.Path): Script started."
Write-Host "$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Press the 'q' key to stop generating reports." 

if ($SendMailEveryxMinutes -gt 0) {
	if (!$SMTPTo) { Throw "An e-mail report was requested but no recipients were specified. Please use the SMTPTo parameter to specify recipients." }
}

# Loop until 'q' key pressed
while (!($Global:Abort)) {
	$MoveRequestsToReport=$Null
	# Only gather MoveRequest details if we are going to write it to a file or send it. Skip otherwise.
	if ((($SaveReportEveryxMinutes -gt 0) -and (($Global:iCounter)%$SaveReportEveryxMinutes -eq 0)) -or (($SendMailEveryxMinutes -gt 0) -and (($Global:iCounter)%$SendMailEveryxMinutes -eq 0))) {
		Write-Host "$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Gathering move requests in batch..."
		try {
			if ($BatchName) {
				$MoveRequestsToReport=Get-MoveRequest -BatchName $BatchName
			} else {
				$MoveRequestsToReport=Get-MoveRequest
			}
		}
		catch [System.Management.Automation.CommandNotFoundException] {
			Throw "An error occured while trying to enumerate Mailbox Move Requests. This script depends on Exchange Management Shell. Make sure this script is loaded in EMS."
		}
		catch {
			Throw $Error[0].Exception
		}
		if ($MoveRequestsToReport) {
			Write-Host "$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Generating report..."
			$HTMLReport=Out-MoveRequestHTMLReport -MoveRequestsToReport ($MoveRequestsToReport)

			if (($SaveReportEveryxMinutes -gt 0) -and $MoveReportOutputFile -and (($Global:iCounter)%$SaveReportEveryxMinutes -eq 0)) {
				$OutputFile=$MoveReportOutputFile
				if ($AppendTimeStampToReportFile) {
					# Get plain filename
					$FileName=[System.IO.Path]::GetFileNameWithoutExtension($OutputFile)
					# Append date/timestamp to filename
					$FileName=([System.IO.Path]::GetFileName($outputFile)).Replace($FileName,"$($FileName)_$(Get-Date -f "yyyyMMdd_HHmmss")")
					# Recreate filename by joining original pathname and new filename
					$OutputFile=Join-Path (Split-Path $MoveReportOutputFile -parent) $FileName
				}
				Write-Verbose "Writing report file $OutputFileName..."
				try {
					$HTMLReport|out-file $OutputFile
					Write-Host "$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Report $OutputFile saved"
				}
				catch {
					Write-Host "An error occured while trying to save the HTML report to $OutputFileName."
					Write-Error $Error[0].Exception
				}
			}
			if (($SendMailEveryxMinutes -gt 0) -and (($Global:iCounter)%$SendMailEveryxMinutes -eq 0)) {
				Write-Verbose "Generating e-mail message to $SMTPTo"
				$Message = New-Object System.Net.Mail.MailMessage $SMTPFrom, $SMTPTo
				$Message.Subject = $MessageSubject
				$Message.IsBodyHTML = $true
				$Message.Body = $HTMLReport
				$SMTPclient = New-Object Net.Mail.SmtpClient($SMTPServer)
				# Use current account credentials to authenticate to SMTP server
				$SMTPclient.UseDefaultCredentials=$true
				try {
					$SMTPclient.Send($message)
					Write-Verbose "Message sent"
					Write-Host "$(Get-Date -f "[yyyyMMdd HH:mm:ss]") Message sent"
				}
				catch {
					Write-Host "An error occured while trying to send the e-mail report."
					Write-Error $Error[0].Exception
				}
			}
		} else {
			Write-Host "$(Get-Date -f "[yyyyMMdd HH:mm:ss]") No mailbox moves in batch '$BatchName'. No report generated."
		}
	}
	$Global:iCounter+=1
	# Sleep 60 seconds while checking for "q" keypress
	1..240|%{
		Start-Sleep -milliseconds 250;
		if ($Host.UI.RawUI.KeyAvailable) {
			switch ($Host.UI.RawUI.ReadKey("IncludeKeyUp,NoEcho").Character) {
				 "q" {
					Write-Host "'q' key press detected - aborting report script"
					$Global:Abort=$true
				}
			}
		}
		if ($Global:Abort) {Break}
	}
}

How it works

The script consists of 1 helper function, 1 function to create the HTML report and the main body. The helper function Prepare-CSVOutput concatenates multiple values into a comma-separated string. Out-MoveRequestHTMLReport takes an object (assuming it is a collection of MoveRequests) and generates a formatted HTML string which can be written to file or sent as e-mail message. The progress bar code was based on the inspiration given by this The Lonely Administrator’s blog post: http://jdhitsolutions.com/blog/2012/02/create-html-bar-charts-from-powershell/.

 

The main script runs in a continuous loop (until someone presses the ‘q’ key). The primary reason for running it in a loop instead a scheduled task was ease of configuration. Running it as a scheduled task would require configuration to be saved in config files and would need more “smart” routines to determine the status of the report.

 

Example output

A typical e-mail sent by the script looks like this (this report was generated in a production environment. Names have been blurred for obvious reasons):

image

Adding thumbnailphoto to FIM Galsync

Situation

I was working in an environment with multiple Exchange forests where Forefront Identity Manager was used to realize a common global address list (GAL). The customer wanted the users’ pictures from both forests to appear in the GAL in both Exchange forests.

Solution

The solution is to include the AD attribute “thumbnailphoto” in the FIM address list synchronization. I couldn’t find a step-by-step procedure, so I decided to write one myself.

Lab Setup

The overview below depicts my lab setup:

The lab is running Exchange 2010, FIM 2010 in a Windows 2008 R2 Active Directory.

Scope

The scope of this procedure is to add the picture of the mailbox in the local forest to the contact in the remote forest by using the built-in Galsync management agents of FIM 2010. This procedure does not cover the implementation of the Galsync itself.

Procedure

Step 1: Adding user picture to Active Directory

In the first place the picture of user corporate01 should be added to Active Directory. The picture is stored in the attribute “thumbnailphoto”. This is done by running the following Exchange cmdlet:

Import-RecipientDataProperty -Identity corporate01 -Picture -FileData ([Byte[]]$(Get-Content -Path “c:\foto\ab_corporate.jpg” -Encoding byte -ReadCount 0))

Pictures must be smaller than 10 kb. The recommended format is 96×96 pixels.

Step 2: Configure Management Agent of corporate.local

·         Start the FIM Synchronization Service Manager Console and select “Management Agents

·         Right click the Management Agent you want to modify and select Properties.

·         Go to the “Select Attributes”section

·         Check the Show All box and select the attribute “thumbnailphoto”, click OK

·         Return to the properties of the Management Agent and select the section “Configure Attribute Flow

·         Configure this section according to the following table:

Data source object type

user

Metaverse object type

person

Mapping Type

Direct

Flow Direction

Import

Data source attribute

thumbnailphoto

Metaverse attribute

photo

 

 
·         Click New

·         Verify this modification by collapsing the following header:
 

·         Check if the following rule is added:
 

Step 3: Import modification to the metaverse

·         Right click the management agent you just modified and select Properties

·         Select Run  and do a Full Import and Full Synchronization

Step 4: Verify attribute import

·         Start the FIM Synchronization Service Manager Console and select “Metaverse Search

·         Click “Add clause

·         Enter the following clause:
 

·         Click “Search

·         In the “Search Results” pane, right click the user with displayname corporate01 and select Properties

·         Confirm that the attribute “photo” contains a value
 

·         Click Close

Step 5: Configure Management Agent of company.local

·         Start the FIM Synchronization Service Manager Console and select “Management Agents

·         Right click the Management Agent you want to modify and select Properties.

·         Go to the “Select Attributes”section

·         Check the Show All box and select the attribute “thumbnailphoto”, click OK
 

·         Return to the properties of the Management Agent and select the section “Configure Attribute Flow

·         Configure this section according to the following table:

Data source object type

contact

Metaverse object type

person

Mapping Type

Direct

Flow Direction

Export (allow nulls)

Data source attribute

thumbnailphoto

Metaverse attribute

photo

·         Click New

·         Verify this modification by collapsing the following header:
 

·         Check if the following rule is added:
 

Step 6: Import modification to the remote forest

·         Right click the management agent you just modified and select Properties

·         Select Run  and do an Export

Step 7: Verify attributes in remote forest

·         Start Active Directory Users And Computers and enable the Advanced features

·         Go to the OU where the FIM Galsync creates the contacts

·         Double click the contact “corporate01” and go the the Attribute Editor
 

·         Confirm that the attribute “thumbnailphoto”contains a value.

 

What does it look like in Outlook ?

If I open the mailbox of user company01, we see the following results:

 

Disclaimer

This information is provided as-is.

 

 

 

 

 

 

 

Enabling AD mail contacts for Lync

Situation

While working in an environment with multiple Exchange 2010 forests where Forefront Identity Manager was used to realize a common global address list (GAL). Each forest also has its own Lync 2010 implementation without Enterprise voice.

By default the Lync address book is automatically populated with all objects that have one of the following attributes filled in:

  • msRTCSIP-PrimaryUserAddress
  • telephoneNumber
  • homePhone
  • mobile

By default the FIM Galsync synchronizes all those attributes, except the msRTCSIP-PrimaryUserAddress. This caused contacts in the remote forest to appear in the address book with a telephone icon:

This situation caused confusion for our users because they expect the Lync client to work for Instant Messaging with Lync users in the remote forests. When they try to start an IM session with a remote forest user Outlook starts and will created a new e-mail message.

Continue reading

Office 365: New version of DirSync released

Microsoft has released a new version of DirSync or Windows Azure Active Directory Sync Tool

Notable Changes:

DirSync can be installed on a Domain Controller

Documentation on how to deploy can be found here or a guide with additional pictures

Other changes fixed in this release:

Fix to address Sync Engine memory leak
Fix to address “staging-error” during full import from Azure Active Directory
Fix to handle Read-Only Domain Controllers in Password Sync

Version 6553.0002
Date Released 11/4/2013

The latest version can be downloaded here: Windows Azure Active Directory Sync tool – 64 bit

Long and awaited: Exchange 2013 Server Role Requirements Calculator

In the Exchange community, the Mailbox calculator is the tool to size your Exchange environment.
During the Exchange 2013 TAP (Technology Adoption Programm) a lot of communications has been going around about this tool.
At the RTM release of Exchange 2013 no calc tool yet. Not even during the release of CU1 for Exchange 2013
And now there it is: Ross Smith IV has released his “baby”
Version: 5.1
To read more about it:
To download it go to:

Review Kuando Busylight for Microsoft Lync

Everybody knows the following situations. Lync Enterprise Voice deployed. Fancy bluetooth headsets are handed out within the company and the complaining starts.
Collegues bother eachother during important phone calls. While concentrating finishing your important document, you are constantly disturbed by co-workers. Moods can be agitated and money can get lost during these situations.

I have made a review of Kuando Busylight for Microsoft Lync. There are more similar devices in the field, but this one was provided me for testing. This device can display your presence from Lync to your collegues, so they know how to deal with your situation. On the phone, finishing up that important quotation or having a meeting with customers.

In the package:

  • Kuando Busylight for Microsoft Lync
  • double sided M3 adhesive patch
  • a small manual

Installation:

As easy as it can be. Connect the Busylight to an empty USB slot. Download the software from http://www.busylight.com/support/lync. (Version 1.2.172) You need to register yourself before downloading the software.

Run the MSI to install the software. It a straight next-next-finish installation

Usage:

The Busylight cycles it’s colors as soon as it is connected. It works well together with Lync 2013. Your status is mimicked with the colors on the Busy light. (Available – green, Busy – red, Do Not Disturb – purple and Away – yellow). When in a call the Busylight flashes red to indicate your call). When you are called the Busylight flashes blue and has a ring-tone.
With the software you can choose from 8 different ring-tones. The volume can be set at 4 levels 25%-50%-75%-100% or mute). Also the colors can be changed for Busy in a call and Do Not Disturb.


With the M3 adhesive the Busylight can be stick-ed on your monitor or desktop. It’s angle can be adjusted so it can be sticked on a vertical surface.

Pro’s and Con’s:

Pro’s
  • Easy to install and use.
  • Long USB cable (±3 m. / 10 ft.) Even for Desktops tucked under bureau’s it is long enough to connect to.
  • Different sounds even when your PC is muted
Con’s
  • Difficult to stick it onto a laptop. A clip to attach it to your laptop screen would be more convenient
  • For laptops, the length of the USB cable is a bit of an overkill. A shorter cable (±0,5 m. / 2 ft)  with an extension could be an alternative.
  •  Bit pricy (€ 49,90 recommended retail price according  Kuando’s website)

Thanks:

I would like to thank ATIS Telecommunicatie B.V. for providing this test specimen.

TMG Console javascript error

While working with Sharepoint, TMG (Microsoft Forefront Thread Management Gateway) is a product that you probably will encounter at customers to publish SharePoint sites to the internet. If you are getting a error when opening TMG console that looks like this one:TMGConsoleJavascript.png

You can fix this bij editing the following file:
C:\Program Files\Microsoft Forefront Threat Management Gateway\UI_HTMLs\TabsHandler\TabsHandler.htc
Open it with notepad and search for the following: “paddingTop”
Add at the start of the sentense “//” to comment out the line.
You should find three of these lines, then save the file and start the TMG console again. Now is this problem gone.

Coexistence of Exchange 2013 and earlier versions of Exchange Server

Now Exchange 2013 CU1 has been released the Coexistence of Exchange 2013 and earlier versions of Exchange Server can be shown in a matrix, as shown in the following table.

Exchange version Exchange organization coexistence
Exchange Server 2003 and earlier versions Not supported
Exchange 2007 Supported with the following versions of Exchange:

  • Update Rollup 10 for Exchange 2007 Service Pack 3 (SP3) on all Exchange 2007 servers in the organization, including Edge Transport servers.
  • Exchange 2013 Cumulative Update 1 (CU1) on all Exchange 2013 servers in the organization.
Exchange 2010 Supported with the following versions of Exchange:

  • Exchange 2010 SP3 on all Exchange 2010 servers in the organization, including Edge Transport servers.
  • Exchange 2013 CU1 on all Exchange 2013 servers in the organization.
Mixed Exchange 2010 and Exchange 2007 organization Supported with the following versions of Exchange:

  • Update Rollup 10 for Exchange 2007 SP3 on all Exchange 2007 servers in the organization, including Edge Transport servers.
  • Exchange 2010 SP3 on all Exchange 2010 servers in the organization, including Edge Transport servers.
  • Exchange 2013 CU1 on all Exchange 2013 servers in the organization.