Updated Bio

October 23, 2013 Leave a comment

I finally got around to updating my Bio page for anyone interested.

Categories: Personal Tags:

System Center 2012 R2 Evaluation Virtual Machines

October 22, 2013 1 comment

Dirsync: Determine if Password Sync is Enabled

October 22, 2013 1 comment

For those not interested in the complete DirSync Report I published last week, now you can run just the Password Hash Sync portion, in a script I published here: Dirsync: Determine if Password Sync is Enabled.

For deployments with remote SQL installations: As with the previous report, note that we make use of the SQL PowerShell Module, which must be present on the computer.

Sample Output(s)

DirSync “Busted Users” Report

October 17, 2013 4 comments

If you administer DirSync for your organization, you likely have seen emails like this, indicating some of your users didn’t sync.

DirSync Error Email

It can be a frustrating email, since the “error description” is for some reason blank and the “On-premises object ID” column is not something that’s easy to correlate to a user account within your Active Directory. There are also application event log entries (FIMSynchronizationService #6111 and Directory Synchronization #0), but again these aren’t exactly rich with detail.

Many of you know that DirSync is actually a customized installation FIM 2010 R2′s Synchronization Service. Within the miisclient.exe console, you can look at your most recent “Export” job and examine the errors one at a time.

Miisclient.exe Console


(By the way, this is actually the place to go if you wanted to configure filtering for directory synchronization.)

Using this console certainly works, but it’s not an efficient way to resolve errors. Microsoft seems to acknowledge this, but falls short of a fix with that email, in my opinion. Instead of wearing out your mouse, I propose you use the PowerShell script I have written below. Within, I leverage the free FimSyncPowerShellModule which you’ll need to download and copy to:

…\System32\WindowsPowerShell\v1.0\Modules\FimSyncPowerShellModule\FimSyncPowerShellModule.psm1

Once you’ve copied the module, you’re ready to run the report, which can be downloaded here.

Here is a sample output, followed by the code itself.

Sample Output

<#
Description:
This script generates a list of users who are failing to export to Azure AD.

This script makes use of the FimSyncPowerShellModule

https://fimpowershellmodule.codeplex.com/

(Download and copy to C:\Windows\System32\WindowsPowerShell\v1.0\Modules\FimSyncPowerShellModule\FimSyncPowerShellModule.psm1)

October 18 2013
Mike Crowley

http://mikecrowley.us

#>

#Import the FimSyncPowerShellModule Module
ipmo FimSyncPowerShellModule

#Get the last export run
$LastExportRun = (Get-MIIS_RunHistory -MaName 'Windows Azure Active Directory Connector' -RunProfile 'Export')[0]

#Get error objects from last export run (user errors only)
$UserErrorObjects = $LastExportRun | Get-RunHistoryDetailErrors | ? {$_.dn -ne $null}

$ErrorFile = @()

#Build the custom Output Object
$UserErrorObjects | % {
 $TmpCSObject = Get-MIIS_CSObject -ManagementAgent 'Windows Azure Active Directory Connector' -DN $_.DN
 [xml]$UserXML = $TmpCSObject.UnappliedExportHologram
 $MyObject = New-Object PSObject -Property @{
 EmailAddress = (Select-Xml -Xml $UserXML -XPath "/entry/attr" | select -expand node | ? {$_.name -eq 'mail'}).value
 UPN = (Select-Xml -Xml $UserXML -XPath "/entry/attr" | select -expand node | ? {$_.name -eq 'userPrincipalName'}).value
 ErrorType = $_.ErrorType
 DN = $_.DN
 }
 $ErrorFile += $MyObject
 }

$FileName = "$env:TMP\ErrorList-{0:yyyyMMdd-HHmm}" -f (Get-Date) + ".CSV"
$ErrorFile | select UPN, EmailAddress, ErrorType, DN | epcsv $FileName -NoType

#Output to the screen
$ErrorFile | select UPN, EmailAddress, ErrorType, DN

Write-Host
Write-Host $ErrorFile.count "users with errors. See here for a list:" -F Yellow
Write-Host $FileName -F Yellow
Write-Host

DirSync Report

October 16, 2013 6 comments

Azure Active Directory Sync (DirSync) seems so simple on the surface doesn’t it?  “Next, Next, Finish”, right?  Ha!  If you’ve ever had to revisit your DirSync server to troubleshoot or make a configuration change, you know there can be more than meets the eye.  A lot of useful information happens to be scattered across various registry keys, SQL tables and XML files.  If you’re not familiar with the FIM Management Console, and these other locations it might be hard to see what’s going on.

Here’s a free script that aims to help by creating a dashboard highlighting useful DirSync configurations.  See the image below for a sample output.  Before you run it you should be aware of the limitations listed in the “known issues” area of the script.

DirSync Report


You can Review the script below or download it and try it for yourself!

<#
Description:
This script gathers DirSync information from various locations and reports to the screen.

November 5 2013
Mike Crowley

http://mikecrowley.us

Known Issues:
1) All commands, including SQL queries run as the local user.  This may cause issues on locked-down SQL deployments.
2) For remote SQL installations, the SQL PowerShell module must be installed on the dirsync server.
    (http://technet.microsoft.com/en-us/library/hh231683.aspx)
3) The Azure Service account field is actually just the last account to use the Sign In Assistant.
    There are multiple entries at that registry location.  We're just taking the last one.
4) Assumes Dirsync version 6385.0012 or later.

#>

#Console Prep
cls
Write-Host "Please wait..." -F Yellow
ipmo SQLps

#Check for SQL Module
if ((gmo sqlps) -eq $null) {
    write-host "The SQL PowerShell Module Is Not loaded.  Please install and try again" -F Red
    write-host "http://technet.microsoft.com/en-us/library/hh231683.aspx" -F Red
    Write-Host "Quitting..." -F Red; sleep 5; Break
    }

#Get Dirsync Registry Info
$DirsyncVersion = (gp 'hklm:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Online Directory Sync').DisplayVersion
$DirsyncPath = (gp 'hklm:SOFTWARE\Microsoft\MSOLCoExistence').InstallPath
$FullSyncNeededBit = (gp 'hklm:SOFTWARE\Microsoft\MSOLCoExistence').FullSyncNeeded
$FullSyncNeeded = "No"
If ((gp 'hklm:SOFTWARE\Microsoft\MSOLCoExistence').FullSyncNeeded -eq '1') {$FullSyncNeeded = "Yes"}

#Get SQL Info
$SQLServer = (gp 'HKLM:SYSTEM\CurrentControlSet\services\FIMSynchronizationService\Parameters').Server
if ($SQLServer.Length -eq '0') {$SQLServer = $env:computername}
$SQLInstance = (gp 'HKLM:SYSTEM\CurrentControlSet\services\FIMSynchronizationService\Parameters').SQLInstance
$MSOLInstance = ($SQLServer + "\" + $SQLInstance)
$SQLVersion = Invoke-Sqlcmd -ServerInstance $MSOLInstance -Query "SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition')"

#Get Password Sync Status
[xml]$ADMAxml = Invoke-Sqlcmd -ServerInstance $MSOLInstance -Query "SELECT [ma_id] ,[ma_name] ,[private_configuration_xml] FROM [FIMSynchronizationService].[dbo].[mms_management_agent]" | ? {$_.ma_name -eq 'Active Directory Connector'} | select -Expand private_configuration_xml
$PasswordSyncBit = (Select-Xml -XML $ADMAxml -XPath "/adma-configuration/password-hash-sync-config/enabled" | select -expand node).'#text'
$PasswordSyncStatus = "Disabled"
If ($PasswordSyncBit -eq '1') {$PasswordSyncStatus = "Enabled"}

#Get Account Info
$ServiceAccountGuess = (((gci 'hkcu:Software\Microsoft\MSOIdentityCRL\UserExtendedProperties' | select PSChildName)[-1]).PSChildName -split ':')[-1]
$ADServiceAccountUser = $ADMAxml.'adma-configuration'.'forest-login-user'
$ADServiceAccountDomain = $ADMAxml.'adma-configuration'.'forest-login-domain'
$ADServiceAccount = $ADServiceAccountDomain + "\" + $ADServiceAccountUser

#Get DirSync Database Info
$SQLDirSyncInfo = Invoke-Sqlcmd -ServerInstance $MSOLInstance -Query "SELECT DB_NAME(database_id) AS DatabaseName, Name AS Logical_Name, Physical_Name, (size*8)/1024 SizeMB FROM sys.master_files WHERE DB_NAME(database_id) = 'FIMSynchronizationService'"
$DirSyncDB = $SQLDirSyncInfo | ? {$_.Logical_Name -eq 'FIMSynchronizationService'}
$DirSyncLog = $SQLDirSyncInfo | ? {$_.Logical_Name -eq 'FIMSynchronizationService_log'}

#Get connector space info (optional)
$ADMA = Invoke-Sqlcmd -ServerInstance $MSOLInstance -Query "SELECT [ma_id] ,[ma_name] FROM [FIMSynchronizationService].[dbo].[mms_management_agent] WHERE ma_name = 'Active Directory Connector'"
$AzureMA = Invoke-Sqlcmd -ServerInstance $MSOLInstance -Query "SELECT [ma_id] ,[ma_name] FROM [FIMSynchronizationService].[dbo].[mms_management_agent] WHERE ma_name = 'Windows Azure Active Directory Connector'"
$UsersFromBothMAs = Invoke-Sqlcmd -ServerInstance $MSOLInstance -Query "SELECT [ma_id] ,[rdn] FROM [FIMSynchronizationService].[dbo].[mms_connectorspace] WHERE object_type = 'user'"
$AzureUsers = $UsersFromBothMAs | ? {$_.ma_id -eq $AzureMA.ma_id}
$ADUsers = $UsersFromBothMAs | ? {$_.ma_id -eq $ADMA.ma_id}

#Get DirSync Run History
$SyncHistory = Invoke-Sqlcmd -ServerInstance $MSOLInstance -Query "SELECT [step_result] ,[end_date] ,[stage_no_change] ,[stage_add] ,[stage_update] ,[stage_rename] ,[stage_delete] ,[stage_deleteadd] ,[stage_failure] FROM [FIMSynchronizationService].[dbo].[mms_step_history]" | sort end_date -Descending

#GetDirSync interval (3 hours is default)
$SyncTimeInterval = (Select-Xml -Path ($DirsyncPath + "Microsoft.Online.DirSync.Scheduler.exe.config") -XPath "configuration/appSettings/add" | select -expand Node).value

#Generate Output
cls

Write-Host "Report Info" -F DarkGray
Write-Host "Date: " -F Cyan -NoNewline ; Write-Host (Get-Date) -F DarkCyan
Write-Host "Server: " -F Cyan -NoNewline ; Write-Host  $env:computername -F DarkCyan
Write-Host

Write-Host "Account Info" -F DarkGray
Write-Host "Active Directory Service Account: " -F Cyan -NoNewline ; Write-Host $ADServiceAccount -F DarkCyan
Write-Host "Azure Service Account Guess: " -F Cyan -NoNewline ; Write-Host $ServiceAccountGuess -F DarkCyan
Write-Host

Write-Host "DirSync Info" -F DarkGray
Write-Host "Version: " -F Cyan -NoNewline ; Write-Host $DirsyncVersion -F DarkCyan
Write-Host "Path: " -F Cyan -NoNewline ; Write-Host $DirsyncPath -F DarkCyan
Write-Host "Password Sync Status: " -F Cyan -NoNewline ; Write-Host $PasswordSyncStatus -F DarkCyan
Write-Host "Sync Interval (H:M:S): " -F Cyan -NoNewline ; Write-Host $SyncTimeInterval -F DarkCyan
Write-Host "Full Sync Needed? " -F Cyan -NoNewline ; Write-Host $FullSyncNeeded -F DarkCyan
Write-Host

Write-Host "User Info" -F DarkGray
Write-Host "Users in AD connector space: " -F Cyan -NoNewline ; Write-Host $ADUsers.count -F DarkCyan
Write-Host "Users in Azure connector space: " -F Cyan -NoNewline ; Write-Host $AzureUsers.count -F DarkCyan
Write-Host "Total Users: " -F Cyan -NoNewline ; Write-Host $UsersFromBothMAs.count -F DarkCyan
Write-Host

Write-Host "SQL Info " -F DarkGray
Write-Host "Version: " -F Cyan -NoNewline ; Write-host $SQLVersion.Column1 $SQLVersion.Column2 $SQLVersion.Column3 -F DarkCyan
Write-Host "Instance: " -F Cyan -NoNewline ; Write-Host  $MSOLInstance -F DarkCyan
Write-Host "Database Location: " -F Cyan -NoNewline ; Write-Host $DirSyncDB.Physical_Name -F DarkCyan
Write-Host "Database Size: " -F Cyan -NoNewline ; Write-Host $DirSyncDB.SizeMB "MB" -F DarkCyan
Write-Host "Database Log Size: " -F Cyan -NoNewline ; Write-Host $DirSyncLog.SizeMB "MB" -F DarkCyan
Write-Host

Write-Host "Most Recent Sync Activity" -F DarkGray
Write-Host "(For more detail, launch:" $DirsyncPath`SYNCBUS\Synchronization Service\UIShell\miisclient.exe")" -F DarkGray
Write-Host "  " ($SyncHistory[0].end_date).ToLocalTime() -F DarkCyan -NoNewline ; Write-Host " --" $SyncHistory[0].step_result -F Gray
Write-Host "  " ($SyncHistory[1].end_date).ToLocalTime() -F DarkCyan -NoNewline ; Write-Host " --" $SyncHistory[1].step_result -F Gray
Write-Host "  " ($SyncHistory[2].end_date).ToLocalTime() -F DarkCyan -NoNewline ; Write-Host " --" $SyncHistory[2].step_result -F Gray
Write-Host

Converting SMTP Proxy Addresses to Lowercase

May 14, 2012 4 comments

Update: Be aware, this script has not been tested with SIP, X400 or other address types. I am working on an update to validate these scenarios, but in the meantime, proceed at your own risk with these address types.

I recently encountered a question in an online forum where someone asked for a script to convert all of their user’s email addresses to lower case values.  While this doesn’t affect the message delivery, it can have an impact on aesthetics when the address is displayed in an external recipient’s email client.  An Exchange Email Address Policy can do this to some degree, but I wanted to see how it could be done with PowerShell.

The challenge with a script like this is twofold:

  1. Email addresses (proxy addresses) are a multi-valued attribute, which can be tricky to work with.
  2. PowerShell is generally not case-sensitive, and therefore when we try to rename Mr. Gallalee’s email address in the screenshot below, we can see that it does not work:

WARNING: The command completed successfully but no settings of 'demolab.local/Users/Rob Gallalee' have been modified.

After a little bit of inspiration from a script written by Michael B Smith, I came up with the below:


$MailboxList = Get-Mailbox  -ResultSize unlimited

$MailboxList | % {

$LoweredList = @()
$RenamedList = @()

foreach ($Address in $_.EmailAddresses){
if ($Address.prefixstring -eq "SMTP"){
$RenamedList += $Address.smtpaddress + "TempRename"
$LoweredList += $Address.smtpaddress.ToLower()
}
}
Set-mailbox $_ -emailaddresses $RenamedList -EmailAddressPolicyEnabled $false
Set-mailbox $_ -emailaddresses $LoweredList

#Without this line the "Reply To" Address could be lost on recipients with more than one proxy address:
Set-mailbox $_ -PrimarySmtpAddress $_.PrimarySmtpAddress
}

This script works as follows:

  1. Puts all mailboxes into the $MailboxList variable.  If you don’t want all mailboxes,  edit the Get-Mailbox cmdlet as you see fit.
  2. Filters out X400 and other non-SMTP addresses.
  3. Creates an array called $RenamedList which stores each proxy address with “TempRename” appended to it (e.g. Rgallalee@demolab.localTempRename).
  4. Creates another array ($LoweredList) and use the “ToLower” method on each proxy address.
  5. Sets the proxy address for the user to the value of $RenamedList and then to $LoweredList.
    1. This is how we get around the case case insensitivity – name it to something else and then name it back.
  6. Step 4 and 5 don’t preserve the “Primary” / “Reply-To” address, so we set it back manually with the last line.

Note: This script turns off the email address policy for each user.

As always, feedback is welcome.

100,000 and Counting…

May 1, 2012 2 comments

I am proud to report that today this blog has reached 100,000 views! Maintaining this site has been very rewarding for me, and I’m happy to have been able to contribute to the technical community, which has served me very well since I entered the infotech industry ten years ago.

WordPress.com provides statistics on how every blog is used.  Here are some facts about mine:

  • My post on setting logon wallpaper for Windows 7 has been my most popular
  • Google’s search results have sent most of my readers (by far). TechNet forums comes in 2nd
  • Google has sent people to this site at a rate 34x to that of Bing
  • Only 9 viewers have used ask.com to find me (GASP!)
  • The anti-spam feature for comments is very good, though the comment spam I receive has been surprisingly complementary; I’m half way inclined to let it through. Winking smile

Thanks to all my visitors.  Please share my site with a friend, visit the sites that pay my bills (Mike’s Links – top right), and if you have any topic suggestions or feedback in general, please contact me!

Categories: Personal Tags:
Follow

Get every new post delivered to your Inbox.

Join 51 other followers