Exchange
Everyone Exchange!
Exchange Hybrid deployment scenarios

 

Hybrids: The final frontier. These are the voyages of the starship Enterprise.
Whether Exchange or Lync, hybrids are the wave of the future, something everyone will have to start dealing with at one point in their career.

With Exchange Hybrids in particular, we enable coexistence between our on-prem exchange environment and Office 365’s Exchange Online, enabling features such as:

  • Secure mail routing
  • Unified GAL
  • Free/Busy sharing
  • Centralized SMTP control & mail box management
  • A single Outlook Web App URL
  • Moving mailbox back and forth
  • Message tracking, mail box search and mail tips across both environments
  • The possibility to use message archiving in the cloud for on-prem mailboxes (Which could result in a huge cost savings!)

For those who have been in the exchange field a while, these all start to look fairly familiar with cross forest exchange deployments, don’t they? OK, not all these features are available in the same form if we do this between different on-prem forests, but Microsoft had to take its inspiration from somewhere now don’t they!

In essence there are a few deployment options we can consider for our Exchange Hybrid, but just to clarify, I’m talking about a full rich coexistence hybrid here, not some cutover migration plan.

Scenario 1: Direct connection in to the on-prem environment

 

clip_image002

 

The simplest of scenarios in that sense that we have a direct connection in to the on-prem environment. No hassle with reverse proxies and edge servers. Just open the ports, configure the hybrid and you’re ready to rock!

Scenario 2: Direct connection in to the on prem with mail routing through O365

A variation on scenario 1 would be to have your MX traffic going through the Exchange Online environment, which would have it protected by EOP.

 

clip_image004

 

Scenario 3: Direct connection in to the on-prem environment with edge server

 

clip_image006

 

In this scenario we still have a direct connection in to our exchange environment but are utilizing the edge server to have mail traffic filtered before coming in to the on-prem environment. A direct smtp connector still exists between Exchange Online and the Exchange on-prem environment.

Scenario 4: Direct connection in to the on-prem environment with mail routing through the edge server.

A variation on scenario 3 would be to let the edge server handle the mail routing to Exchange online.

clip_image008

 

Scenario 5: Direct connection in to the on-prem environment with mail arriving in O365 and routing down to the on-prem through the edge server

Or, we could have all the mail traffic come in to Exchange Online, using the awesome powers of EOP (Exchange Online Protection in case you have been wondering) and route the on-prem mail in through the edge server(s).

 

clip_image010

 

Scenario 6: No direct connection

And this is where things start to get slightly complex. There are a lot of companies out there that do not like opening up their internal network to O365 directly (I’m looking at you, security teams!). Whilst I can relate to some of the concerns that are raised with that setup, let’s not forget that Exchange is secure by design, and, if you let external users access you exchange servers directly, you should not have any concerns about O365 doing the same!

But how do we solve this conundrum? After all, in order to have rich coexistence Exchange Online will need to be able to access the on-prem EWS services for things like for free/busy lookups…

Enter the reverse proxy (Microsoft Application Request routing)!

 

clip_image012

 

Or, the mail routing alternative:

clip_image014

 

The attentive reader will have noticed two things. One, I didn’t put EWS traffic arrows on the last two images (we will cover that configuration and flow in a future article). And two, there is no directory sync in the diagrams. Nor are any ADFS servers displayed. I’ll cover all that and more in future articles!

 

Ports, IPs and URLs

One important thing to keep in mind is the ports, urls and ip adresses Microsoft uses for traffic. Since these get updated regularly I’ll refer you to the following article: https://support.office.com/en-us/article/Office-365-URLs-and-IP-address-ranges-8548a211-3fe7-47cb-abb1-355ea5aa88a2?ui=en-US&rs=en-US&ad=US

Coexistence between Exchange forests (without trusts…) -- Part 1: Conceptual
Imagine a scenario where you acquire a company in a different country and they don’t want to be absorb in your IT environment (because they don’t like it, have regulatory requirements that can’t be met or are just trying to be difficult) but you do need some fashion of coexistence between the two Exchange organizations… After all, you’re part of the same company now and people should be able to find them in the GAL and do free/busy look ups!
Coexistence between Exchange forests (without trusts…) -- Part 11: References & Sync Script

· How to license FIM 2010 & MIM 2016
http://social.technet.microsoft.com/wiki/contents/articles/2487.how-to-license-fim-2010-and-mim-2016.aspx

· Global address list synchronization walkthrough:
https://technet.microsoft.com/en-us/library/cc708642(v=ws.10).aspx

 

Scripts

I did not write these scripts myself, found them only on https://social.technet.microsoft.com/Forums/en-US/c7e204be-05b4-40e1-bf95-e0191a76ece3/using-powershell-to-start-run-profiles?forum=ilm2. Going through them, they get the job done and are synchronizing the directories in my lab happily Smile.

Start-sync.ps1

#@author: Lance Hext

#@mail: lhext@h-cp.us

#############################################################################################

# PARAMETERS #

#############################################################################################

# #

# $debug #

# debug on = $true; #

# Debug off = $false; #

# #

#############################################################################################

#

$debug = $false;

#

#############################################################################################

# #

# $SequenceInfo #

# Place some meaningful information for display at the #

# beginning of the squ3nce #

# #

#############################################################################################

#

$SequenceInfo = "Hourly Synchronization Sequence"

#

#############################################################################################

# #

# $params_ComputerName #

# The name of the server running FIM #

# Use "." if running on the PowerShell script on the FIM server #

# #

#############################################################################################

#

$params_ComputerName = "."

#

#############################################################################################

# #

# $params_delayBetweenExecs #

# delay between each execution, in seconds #

# 1 Minute = 60 #

# 10 Minutes = 600 #

# 15 Minutes = 900 #

# 30 Minutes = 1800 #

# 1 Hour = 3600 #

# #

#############################################################################################

#

$params_delayBetweenExecs = 0

#

#############################################################################################

# #

# $params_numOfExecs #

# The number of executions the script will run before terminating #

# 0 for infinite #

# #

#############################################################################################

#

$params_numOfExecs = 1

#

#############################################################################################

# RUN Profile Sequence Section #

# #

# The array below contains the MA run sequence order #

# #

# Format #

# @{ #

# name=""; #

# profilesToRun=@(""); #

# }; #

# #

#############################################################################################

#

## Addition Info :: Author: Ethan Turk

## Change Info 1: Added to set thresholds for maximum number of changes

## which are allowed in a single run step. You can set individual limits

## for add, updates or deletes.

##

## Ex: Add 'addthreshold=100;', 'delthreshold=100;' or 'updthreshold=1000;' to the

## cycle step below.

## See the 'AD MA' Export step below.

##

## Change Info 2: Added ability to set a mandatory sleep for individual

## sync cycle steps instead of a sleep for the whole set of steps.

##

## Ex: Add 'sleep=120;' to the cycle step below.

## See the 'FIM MA' Delta Import step below.

? Note: Adapt the below names to match the names of the Synchronization agents created in MIM. The names must match exactly!

$params_runProfilesOrder =

@(

@{

name="UK GALMA - EX2007";

profilesToRun=@("Delta Synchronization");

};

@{

name="US GALMA - Exchange 2010";

profilesToRun=@("Delta Synchronization");

};

@{

name="UK GALMA - EX2007";

profilesToRun=@("Export");

};

@{

name="Us GALMA - Exchange 2010";

profilesToRun=@("Export");

};

@{

name="UK GALMA - EX2007";

profilesToRun=@("Delta Import");

};

@{

name="US GALMA - Exchange 2010";

profilesToRun=@("Delta Import");

};

);

#

#############################################################################################

# EMAIL PARAMETERS #

#############################################################################################

# #

# $UseEmailCreds #

# Directs the PowerShell script to send Using Credentials #

# $true; = Send email with Creds #

# $false; = Send email without Creds #

# #

#############################################################################################

#

$UseEmailCreds = $false;

#

#############################################################################################

# #

# $emailFrom #

# A Well formatted email address for the FIM Server #

# Example "FIMSequencer@xyz.com" #

# #

#############################################################################################

#

$emailFrom = "SentFrom@yourdomain.com"

#

#############################################################################################

# #

# $emailTo #

# A Well formatted email address for Recipient of the email #

# Example "someone@xyz.com" #

# #

#############################################################################################

#

$emailTo = "AdminEmail@yourdomain.com"

#

#############################################################################################

# #

# $emailCC #

# A Well formatted email address for send a Copy of the email to #

# Example "carboncopy@xyz.com" #

# #

#############################################################################################

#

$emailCC = ""

#

#############################################################################################

# #

# $emailCC1 #

# A Well formatted email address for send a Copy of the email to #

# Example "carboncopy@xyz.com" #

# #

#############################################################################################

#

$emailCC1 = ""

#

#############################################################################################

# #

# $smtpServer #

# A FQDN of a mail server #

# Example "mail.xyz.com" #

# #

#############################################################################################

#

$smtpServer = "your SMTP Server"

#

#############################################################################################

# #

# $EmailperMA #

# Directs the PowerShell script to either send an email at the completion #

# of each MA OR generate One email at the end of the sequence run #

# #

# $true; = Send an email for each MA #

# $false; = Send 1 (one) email at the completion on the Sequence #

# #

#############################################################################################

#

$EmailperMA = $false;

#

#############################################################################################

# #

# $EmailOnErrorOnly #

# Directs the PowerShell script to send and email on failues only #

# $true; = Send email only on an MA Sync Failure ie MA.Result <> "success" #

# $false; = Send email when MA finishes #

# #

#############################################################################################

#

$EmailOnErrorOnly = $false;

#

#############################################################################################

# #

# INCLUDE THE PROGRAM #

# #

# Location of the Actual Program. This can be a relative or physical path #

# #

# NOTE the 1st period is required #

# Examples #

# Physical Path #

# .C:\powershell\RunSequencerProg.ps1 #

# Relative Path #

# ..\RunSequencerProg.ps1 #

# #

#############################################################################################

#

.\RunSequencerProg.ps1

 

RunSequencerProg.ps1

#################################################################################################################################

# FIM 2010 Management Agent Runner #

# Author : Lance Hext #

# Mail : lhext@h-cp.us #

# Version : 1.2.3 #

# Information : This PowerShell script executes an array of managements agents supplied form the calling PowerShell #

# script. This PowerShell script utilizes the FIM wmi interface for the execution and statistics #

# gathering. #

# For each Management executed either a single email is generated per management agent #

# or a combined email is generated which will contain all the statistic from the run. #

# The email contains the following information as part on the email body #

# 1: The Management agent name #

# 2: The Management Agent completion status #

# 3: All Connector space statistics #

# 4: All Inbound Metaverse statistics #

# 5: All Export data counts #

# The email contains the following attachments #

# 1: The complete run history xml #

# 2: All inbound synchronization errors #

# 3: All Exported synchronization errors #

# #

# #

# #

#################################################################################################################################

# #

# START OF POWERSHELL SCRIPT #

# #

#################################################################################################################################

# #

# * * * * * * * * * * * D O N O T M O D I F Y B E L O W * * * * * * * * * * * * * #

# __________________________________ #

# #

#################################################################################################################################

#################################################################################################################################

# START OF FUNCTIONS #

#################################################################################################################################

$line = "-----------------------------"

function Write-Output-Banner([string]$msg)

{

Write-Output $line,("- "+$msg),$line

}

#################################################################################################################################

# END OF FUNCTIONS #

#################################################################################################################################

# START OF DATA #

#################################################################################################################################

$MAs = @(get-wmiobject -class "MIIS_ManagementAgent" -namespace "root\MicrosoftIdentityIntegrationServer" -computername $params_ComputerName)

$numOfExecDone = 0

$SendEmails = $true;

#################################################################################################################################

# END OF DATA #

#################################################################################################################################

# START OF PROGRAM #

#################################################################################################################################

Write-Output-Banner($SequenceInfo)

do

{

Write-Output-Banner("Execution #:"+(++$numOfExecDone))

if (!$EmailperMA)

{

$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$smtpmsg = new-object Net.Mail.MailMessage

$smtpmsg.From = $emailFrom

$smtpmsg.To.Add($emailTo)

if ($emailCC)

{

$smtpmsg.Cc.Add($emailCC)

}

if ($emailCC1)

{

$smtpmsg.Cc.Add($emailCC1)

}

$RunStart = Get-Date

$AllRunTimeStart = Get-Date -uformat "%Y%m%d%I%M%S"

$smtpmsg.Subject = "FIM Run Sequence Results Started : " +$RunStart

$smtpbody = "

$smtpbody += "

"

$smtpbody += "

FIM Processing Results"

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email Create Mail Object"

Write-Output ($dbugmsg)

}

$AllRuns = "AllRuns-" + $AllRunTimeStart + ".xml"

$AllImportErrorRuns = "AllRuns-" + $AllRunTimeStart + "-ImportErrors.csv"

$AllExportErrorRuns = "AllRuns-" + $AllRunTimeStart + "-ExportErrors.csv"

}

foreach($MATypeNRun in $params_runProfilesOrder)

{

$found = $false;

$SendEmails = $true;

foreach($MA in $MAS)

{

if(!$found)

{

if($MA.Name.Equals($MATypeNRun.name))

{

$abortnow = $false

$found=$true;

$maname = $MA.Name

Write-Output-Banner("MA Name: "+$MA.Name,"`n- Type: "+$MA.Type)

foreach($profileName in $MATypeNRun.profilesToRun)

{

## Added 01/23/2012 :: Author: Ethan Turk

## Change Info: Added to allow sleeping for individual Sync Cycle

## Steps instead of a sleep which must run for every step.

if($MATypeNRun.sleep -gt 0) {

"Found Sleep. Sleeping for " + $MATypeNRun.sleep + " seconds" | Out-Host

Start-Sleep -Seconds $MATypeNRun.sleep

}

## End Addition

## Added 03/05/2012 :: Author: Ethan Turk

## Change Info: Added to set thresholds for maximum number of changes

## which are allowed in a single run step. You can set individual limits

## for add, updates and deletes.

$abortthreshold = "Threshold Reached on $maname $profileName. Cancelling run cycle..."

if($MATypeNRun.addthreshold) {

$ttype = "Add"

$addstatus = .\FIM_ActionThresholds.ps1 $MA.Name $ttype $MATypeNRun.addthreshold

if($addstatus) {

$abortnow = "$ttype $abortthreshold"

break

}

}

if($MATypeNRun.updthreshold) {

$ttype = "Update"

$updstatus = .\FIM_ActionThresholds.ps1 $MA.Name $ttype $MATypeNRun.updthreshold

if($updstatus) {

$abortnow = "$ttype $abortthreshold"

break

}

}

if($MATypeNRun.delthreshold) {

$ttype = "Delete"

$delstatus = .\FIM_ActionThresholds.ps1 $MA.Name $ttype $MATypeNRun.delthreshold

if($delstatus) {

$abortnow = "$ttype $abortthreshold"

break

}

}

## End Addition

Write-Output ("Run Profile "+$profileName,"`n -> starting")

$RunTimeStart = Get-Date -uformat "%Y%m%d%I%M%S"

$datetimeBefore = Get-Date;

$result = $MA.Execute($profileName);

$datetimeAfter = Get-Date;

$duration = $datetimeAfter - $datetimeBefore;

# Setup SMTP Message

if($EmailperMA)

{

if ($debug)

{

$dbugmsg = "--> DB ---- In Multiple Email Create new Mail Object"

Write-Output ($dbugmsg)

}

$smtp = new-object Net.Mail.SmtpClient($smtpServer)

if ($UseEmailCreds)

{

$smtp.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials

}

$smtpmsg = new-object Net.Mail.MailMessage

$smtpmsg.From = $emailFrom

$smtpmsg.To.Add($emailTo)

if ($emailCC)

{

$smtpmsg.Cc.Add($emailCC)

}

$smtpbody = "

$smtpbody += "

"

$smtpbody += "

FIM Processing Results"

#$smtpbody += "

"

$smtpbody += "

Forefront Identity Manager Management Agent Run Results for: " + $MA.Name +"
"

$smtpbody += "

"

$smtpbody += "

"

$smtpmsg.Subject = "FIM Management Agent - " + $MA.Name

if("success".Equals($result.ReturnValue))

{

$msg = "Done. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds

$smtpmsg.Subject += " Processing Success"

$smtpbody += "

"

if ($EmailOnErrorOnly)

{

$SendEmails = $false;

}

}

else

{

$msg = "Done with Error. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds +" - Error: "+$result

$smtpmsg.Subject += " Processing Error"

$smtpbody +="

"

$smtpbody +="

"

}

}

else

{

$smtpbody += "

Forefront Identity Manager Management Agent Run Results
 
 
" + $MA.Name +" Completed Successfully
" + $MA.Name +": Completed.
MA Synchronization Status: " + $result.ReturnValue + "
"

$smtpbody += "

"

$smtpbody += "

"

if("success".Equals($result.ReturnValue))

{

$msg = "Done. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds

$smtpbody += "

"

$smtpbody += "

"

if ($EmailOnErrorOnly)

{

$SendEmails = $false;

}

}

else

{

$msg = "Done with Error. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds +" - Error: "+$result

$smtpbody += "

"

$smtpbody += "

"

}

$SendEmails = $true

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email : New Table "

Write-Output ($dbugmsg)

}

}

# Problems with RunHistory WMI not working with MaGuid or MaName, so I used RunDetails from the MA.

# $RunHistory = get-wmiobject -class "MIIS_RunHistory" -namespace "root\MicrosoftIdentityIntegrationServer" -filter("MaGuid='" + $MA.guid + "'")

# # Write the RunHistory file XML out to a file to then attach to the e-mail, and also set it to a XML attribute.

# $RunHistory[1].RunDetails().ReturnValue | Out-File RunHistory.xml

# Grab the first run-history, which always is the latest result.

# [xml]$RunHistoryXML = $RunHistory[1].RunDetails().ReturnValue

#Take the MA RunDetails RunHistory XML and write to file.

$RunHistFileName = $MA.Name + "-" + $RunTimeStart +"-RunHistory.xml"

$ImportErrorUserFileName = $MA.Name + "-" + $RunTimeStart + "-ImportErrors.csv"

$ExportErrorUserFileName = $MA.Name + "-" + $RunTimeStart + "-ExportErrors.csv"

$MA.RunDetails().ReturnValue | Out-File $RunHistFileName

# Grab the MA run-history and put it into a XML var.

[xml]$RunHistoryXML = $MA.RunDetails().ReturnValue

[xml]$RunHistory1XML = $MA.RunDetails().ReturnValue

# Build User Errors for Exports

$RunHistoryXML."run-history"."run-details"."step-details"."synchronization-errors"."import-error" | select dn,"error-type" | export-csv $ImportErrorUserFileName

$RunHistoryXML."run-history"."run-details"."step-details"."synchronization-errors"."export-error" | select dn,"error-type" | export-csv $ExportErrorUserFileName

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# Discovery Counters #

#################################################################################################################################

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/ma-discovery-counters/filtered-deletions")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/ma-discovery-counters/filtered-objects")

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# Staging Counters #

#################################################################################################################################

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-no-change")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-update")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-delete")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-delete-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-failure")

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# MV Inbound Counters #

#################################################################################################################################

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-remains")

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-projected-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-projected-no-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-joined-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-joined-no-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-flow-remove-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-delete-leave-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/flow-failure")

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-no-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-filtered-leave-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-filtered-remove-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-delete-add-processed")

$smtpbody += "

"

# $mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-projected-remove-mv")

# $smtpbody += "

"

# $mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-joined-remove-mv")

# $smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-update")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-rename")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-delete")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-delete-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-failure")

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# Outbound Counters #

#################################################################################################################################

$outboundCounters=$RunHistory1XML.SelectNodes("run-history/run-details/step-details/outbound-flow-counters")

foreach($outboundCounter in $outboundCounters)

{

$mvValue = $outboundCounter.ma

if ($debug)

{

Write-Output ($outboundCounter)

Write-Output ($mvValue)

}

$smtpbody += "

"

$mvValue = $outboundCounter.SelectSingleNode("provisioned-add-flow")

$smtpbody += "

"

$mvValue = $outboundCounter.SelectSingleNode("provisioned-disconnect")

$smtpbody += "

"

$mvValue = $outboundCounter.SelectSingleNode("connector-flow")

$smtpbody += "

"

$smtpbody += "

"

}

$smtpbody += "

"

$smtpbody += "

Forefront Identity Manager Management Agent Run Results
 
 
" + $MA.Name +" Completed Successfully
MA Synchronization Status: " + $result.ReturnValue + "
" + $MA.Name +": Completed.
MA Synchronization Status: " + $result.ReturnValue + "
 
 
" + $MA.Name + " Run Statistics
 
MA Run Profile:" + $profileName+"
Start Date & Time:" + $MA.RunStartTime().ReturnValue + "
End Date & Time:" + $MA.RunEndTime().ReturnValue + "
Total Run Time:"+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds + "
 
Connector Space Statistics
Total Connector Space Objects:" + $MA.NumCSObjects().ReturnValue +"
Total Connectors:" + $MA.NumTotalConnectors().ReturnValue +"
Connectors:" + $MA.NumConnectors().ReturnValue +"
Explicit Connectors:" + $MA.NumExplicitConnectors().ReturnValue +"
Total Disconnectors:" + $MA.NumTotalDisconnectors().ReturnValue +"
Disconnectors:" + $MA.NumDisconnectors().ReturnValue +"
Explicit Disconnectors:" + $MA.NumExplicitDisconnectors().ReturnValue +"
Total Placeholders:" + $MA.NumPlaceholders().ReturnValue +"
Total Placeholders:" + $MA.NumPlaceholders().ReturnValue +"
 
Discovoery Statistics
Filtered Deletions" + $mvValue.InnerText +"
Filtered Objects:" + $mvValue.InnerText +"
 
Stage Import Statistics
Import Stage Unchanged:" + $mvValue.InnerText +"
Import Stage Add:" + $mvValue.InnerText +"
Import Stage Updates:" + $mvValue.InnerText +"
Import Stage Deletes:" + $mvValue.InnerText +"
Import Stage Delete-Adds:" + $mvValue.InnerText +"
Import Stage Failures:" + $mvValue.InnerText +"
 
Inbound Statistics
Disconnectors
Filtered Disconnectors:" + $MA.NumFilteredDisconnectors().ReturnValue +"
Disconnectors" + $mvValue.InnerText +"
 
Metaverse
Projections - With Flow Updates:" + $mvValue.InnerText +"
Projections - Without Flow Updates:" + $mvValue.InnerText +"
Joins - With Flow Updates:" + $mvValue.InnerText +"
Joins - Without Flow Updates:" + $mvValue.InnerText +"
Deleted Metaverse Objects" + $mvValue.InnerText +"
Disconnected Metaverse Objects" + $mvValue.InnerText +"
Metaverse Flow Failures" + $mvValue.InnerText +"
Connector Objects
Connector With Flow Updates:" + $mvValue.InnerText +"
Connector Without Flow Updates:" + $mvValue.InnerText +"
Connector Filtered" + $mvValue.InnerText +"
Connector Filtered Deleted" + $mvValue.InnerText +"
Connector Delete-Add" + $mvValue.InnerText +"
Metaverse Disconnectors Projected" + $mvValue.InnerText +"
MV Deleted - Joined Disconnectors" + $mvValue.InnerText +"
 
Export Statistics
Export Adds" + $mvValue.InnerText +"
Export Updates" + $mvValue.InnerText +"
Export Renames" + $mvValue.InnerText +"
Export Deletes" + $mvValue.InnerText +"
Export Delete-Adds" + $mvValue.InnerText +"
Export Failures" + $mvValue.InnerText +"
 
OutBound Statistics for: " + $mvValue + "
Provisioning Adds:" + $mvValue.InnerText +"
Provisioning Disconnect:" + $mvValue.InnerText +"
Export Attribute Flow:" + $mvValue.InnerText +"
 
 
"

# Write-Output ( $smtpbody )

if($EmailperMA)

{

if ($debug)

{

$dbugmsg = "--> DB ---- In Multiple Email Send"

Write-Output ($dbugmsg)

}

$RHFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $RunHistFileName)

$smtpmsg.Attachments.Add($RHFile)

$IUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $ImportErrorUserFileName)

$smtpmsg.Attachments.Add($IUFile)

$EUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $ExportErrorUserFileName)

$smtpmsg.Attachments.Add($EUFile)

$smtpmsg.Body = $smtpbody

$smtpmsg.IsBodyHTML = $true

if ($SendEmails)

{

$smtp.Send($smtpmsg)

}

$RHFile.Dispose()

$IUFile.Dispose()

$EUFile.Dispose()

remove-item $RunHistFileName

remove-item $ImportErrorUserFileName

remove-item $ExportErrorUserFileName

}

else

{

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email Conat files"

Write-Output ($dbugmsg)

}

cat $RunHistFileName > $AllRuns

cat $ImportErrorUserFileName > $AllImportErrorRuns

cat $ExportErrorUserFileName > $AllExportErrorRuns

remove-item $RunHistFileName

remove-item $ImportErrorUserFileName

remove-item $ExportErrorUserFileName

}

Write-Output (" -> "+$msg)

Write-Output (" -> "+$result.ReturnValue)

}

}

}

if($abortnow) { break }

}

if(!$found -and !$abortnow)

{

Write-Output ("Not found MA Name:"+$MATypeNRun.name);

}

}

if($abortnow) { $abortnow | Out-Host }

$continue = ($params_numOfExecs -EQ 0) -OR ($numOfExecDone -lt $params_numOfExecs)

if($continue)

{

Write-Output-Banner("Sleeping "+$params_delayBetweenExecs+" seconds")

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email SLEEP Send"

Write-Output ($dbugmsg)

}

if(!$EmailperMA)

{

$smtpbody = "" + $abortnow +

"

" + $smtpbody

$RHFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllRuns)

$smtpmsg.Attachments.Add($RHFile)

$IUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllImportErrorRuns )

$smtpmsg.Attachments.Add($IUFile)

$EUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllExportErrorRuns )

$smtpmsg.Attachments.Add($EUFile)

$smtpmsg.Body = $smtpbody

$smtpmsg.IsBodyHTML = $true

if ($SendEmails -and $abortnow)

{

$smtp.Send($smtpmsg)

}

$RHFile.Dispose()

$IUFile.Dispose()

$EUFile.Dispose()

remove-item $AllRuns

remove-item $AllImportErrorRuns

remove-item $AllExportErrorRuns

}

Start-Sleep -s $params_delayBetweenExecs

}

else

{

Write-Output ("******************************")

}

}

while($continue)

if(!$EmailperMA)

{

$smtpbody = "" +

$abortnow + "

" + $smtpbody

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email Send"

Write-Output ($dbugmsg)

}

$RHFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllRuns)

$smtpmsg.Attachments.Add($RHFile)

$IUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllImportErrorRuns )

$smtpmsg.Attachments.Add($IUFile)

$EUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllExportErrorRuns )

$smtpmsg.Attachments.Add($EUFile)

$smtpmsg.Body = $smtpbody

$smtpmsg.IsBodyHTML = $true

if ($SendEmails -and $abortnow)

{

$smtp.Send($smtpmsg)

}

$RHFile.Dispose()

$IUFile.Dispose()

$EUFile.Dispose()

remove-item $AllRuns

remove-item $AllImportErrorRuns

remove-item $AllExportErrorRuns

}

 

FIM_ActionThresholds.ps1

#################################################################################################################################

# FIM 2010 Management Agent Runner #

# Author : Lance Hext #

# Mail : lhext@h-cp.us #

# Version : 1.2.3 #

# Information : This Powershell script execcutes an array of managements agents supplied form the calling Powershell #

# script. This powershell script utilizes the FIM wmi interface for the execution and statistics #

# gathering. #

# For each Management executed either a single email is generted per management agent #

# or a combined email is generated which will contain all the statistic from the run. #

# The email containes the following information as part on the email body #

# 1: The Managemenmt agent name #

# 2: The Mnagement Agent complition status #

# 3: All Connector space statistics #

# 4: All Inbound Metaverse statistices #

# 5: All Export data counts #

# The email contains the following attachments #

# 1: The complete run history xml #

# 2: All inbound syncronization errors #

# 3: All Exported syncronization errors #

# #

# #

# #

#################################################################################################################################

# #

# START OF POWERSHELL SCRIPT #

# #

#################################################################################################################################

# #

# * * * * * * * * * * * D O N O T M O D I F Y B E L O W * * * * * * * * * * * * * #

# __________________________________ #

# #

#################################################################################################################################

#################################################################################################################################

# START OF FUNCTIONS #

#################################################################################################################################

$line = "-----------------------------"

function Write-Output-Banner([string]$msg)

{

Write-Output $line,("- "+$msg),$line

}

#################################################################################################################################

# END OF FUNCTIONS #

#################################################################################################################################

# START OF DATA #

#################################################################################################################################

$MAs = @(get-wmiobject -class "MIIS_ManagementAgent" -namespace "root\MicrosoftIdentityIntegrationServer" -computername $params_ComputerName)

$numOfExecDone = 0

$SendEmails = $true;

#################################################################################################################################

# END OF DATA #

#################################################################################################################################

# START OF PROGRAM #

#################################################################################################################################

Write-Output-Banner($SequenceInfo)

do

{

Write-Output-Banner("Execution #:"+(++$numOfExecDone))

if (!$EmailperMA)

{

$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$smtpmsg = new-object Net.Mail.MailMessage

$smtpmsg.From = $emailFrom

$smtpmsg.To.Add($emailTo)

if ($emailCC)

{

$smtpmsg.Cc.Add($emailCC)

}

if ($emailCC1)

{

$smtpmsg.Cc.Add($emailCC1)

}

$RunStart = Get-Date

$AllRunTimeStart = Get-Date -uformat "%Y%m%d%I%M%S"

$smtpmsg.Subject = "FIM Run Sequence Results Started : " +$RunStart

$smtpbody = "

$smtpbody += "

"

$smtpbody += "

FIM Processing Results"

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email Create Mail Object"

Write-Output ($dbugmsg)

}

$AllRuns = "AllRuns-" + $AllRunTimeStart + ".xml"

$AllImportErrorRuns = "AllRuns-" + $AllRunTimeStart + "-ImportErrors.csv"

$AllExportErrorRuns = "AllRuns-" + $AllRunTimeStart + "-ExportErrors.csv"

}

foreach($MATypeNRun in $params_runProfilesOrder)

{

$found = $false;

$SendEmails = $true;

foreach($MA in $MAS)

{

if(!$found)

{

if($MA.Name.Equals($MATypeNRun.name))

{

$abortnow = $false

$found=$true;

$maname = $MA.Name

Write-Output-Banner("MA Name: "+$MA.Name,"`n- Type: "+$MA.Type)

foreach($profileName in $MATypeNRun.profilesToRun)

{

## Added 01/23/2012 :: Author: Ethan Turk

## Change Info: Added to allow sleeping for individual Sync Cycle

## Steps instead of a sleep which must run for every step.

if($MATypeNRun.sleep -gt 0) {

"Found Sleep. Sleeping for " + $MATypeNRun.sleep + " seconds" | Out-Host

Start-Sleep -Seconds $MATypeNRun.sleep

}

## End Addition

## Added 03/05/2012 :: Author: Ethan Turk

## Change Info: Added to set thresholds for maximum number of changes

## which are allowed in a single run step. You can set individual limits

## for add, updates and deletes.

$abortthreshold = "Threshold Reached on $maname $profileName. Cancelling run cycle..."

if($MATypeNRun.addthreshold) {

$ttype = "Add"

$addstatus = .\FIM_ActionThresholds.ps1 $MA.Name $ttype $MATypeNRun.addthreshold

if($addstatus) {

$abortnow = "$ttype $abortthreshold"

break

}

}

if($MATypeNRun.updthreshold) {

$ttype = "Update"

$updstatus = .\FIM_ActionThresholds.ps1 $MA.Name $ttype $MATypeNRun.updthreshold

if($updstatus) {

$abortnow = "$ttype $abortthreshold"

break

}

}

if($MATypeNRun.delthreshold) {

$ttype = "Delete"

$delstatus = .\FIM_ActionThresholds.ps1 $MA.Name $ttype $MATypeNRun.delthreshold

if($delstatus) {

$abortnow = "$ttype $abortthreshold"

break

}

}

## End Addition

Write-Output ("Run Profile "+$profileName,"`n -> starting")

$RunTimeStart = Get-Date -uformat "%Y%m%d%I%M%S"

$datetimeBefore = Get-Date;

$result = $MA.Execute($profileName);

$datetimeAfter = Get-Date;

$duration = $datetimeAfter - $datetimeBefore;

# Setup SMTP Message

if($EmailperMA)

{

if ($debug)

{

$dbugmsg = "--> DB ---- In Multiple Email Create new Mail Object"

Write-Output ($dbugmsg)

}

$smtp = new-object Net.Mail.SmtpClient($smtpServer)

if ($UseEmailCreds)

{

$smtp.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials

}

$smtpmsg = new-object Net.Mail.MailMessage

$smtpmsg.From = $emailFrom

$smtpmsg.To.Add($emailTo)

if ($emailCC)

{

$smtpmsg.Cc.Add($emailCC)

}

$smtpbody = "

$smtpbody += "

"

$smtpbody += "

FIM Processing Results"

#$smtpbody += "

"

$smtpbody += "

Forefront Identity Manager Management Agent Run Results for: " + $MA.Name +"
"

$smtpbody += "

"

$smtpbody += "

"

$smtpmsg.Subject = "FIM Management Agent - " + $MA.Name

if("success".Equals($result.ReturnValue))

{

$msg = "Done. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds

$smtpmsg.Subject += " Processing Success"

$smtpbody += "

"

if ($EmailOnErrorOnly)

{

$SendEmails = $false;

}

}

else

{

$msg = "Done with Error. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds +" - Error: "+$result

$smtpmsg.Subject += " Processing Error"

$smtpbody +="

"

$smtpbody +="

"

}

}

else

{

$smtpbody += "

Forefront Identity Manager Management Agent Run Results
 
 
" + $MA.Name +" Completed Successfully
" + $MA.Name +": Completed.
MA Synchronization Status: " + $result.ReturnValue + "
"

$smtpbody += "

"

$smtpbody += "

"

if("success".Equals($result.ReturnValue))

{

$msg = "Done. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds

$smtpbody += "

"

$smtpbody += "

"

if ($EmailOnErrorOnly)

{

$SendEmails = $false;

}

}

else

{

$msg = "Done with Error. Duration: "+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds +" - Error: "+$result

$smtpbody += "

"

$smtpbody += "

"

}

$SendEmails = $true

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email : New Table "

Write-Output ($dbugmsg)

}

}

# Problems with RunHistory WMI not working with MaGuid or MaName, so I used RunDetails from the MA.

# $RunHistory = get-wmiobject -class "MIIS_RunHistory" -namespace "root\MicrosoftIdentityIntegrationServer" -filter("MaGuid='" + $MA.guid + "'")

# # Write the RunHistory file XML out to a file to then attach to the e-mail, and also set it to a XML attribute.

# $RunHistory[1].RunDetails().ReturnValue | Out-File RunHistory.xml

# Grab the first run-history, which always is the latest result.

# [xml]$RunHistoryXML = $RunHistory[1].RunDetails().ReturnValue

#Take the MA RunDetails RunHistory XML and write to file.

$RunHistFileName = $MA.Name + "-" + $RunTimeStart +"-RunHistory.xml"

$ImportErrorUserFileName = $MA.Name + "-" + $RunTimeStart + "-ImportErrors.csv"

$ExportErrorUserFileName = $MA.Name + "-" + $RunTimeStart + "-ExportErrors.csv"

$MA.RunDetails().ReturnValue | Out-File $RunHistFileName

# Grab the MA run-history and put it into a XML var.

[xml]$RunHistoryXML = $MA.RunDetails().ReturnValue

[xml]$RunHistory1XML = $MA.RunDetails().ReturnValue

# Build User Errors for Exports

$RunHistoryXML."run-history"."run-details"."step-details"."synchronization-errors"."import-error" | select dn,"error-type" | export-csv $ImportErrorUserFileName

$RunHistoryXML."run-history"."run-details"."step-details"."synchronization-errors"."export-error" | select dn,"error-type" | export-csv $ExportErrorUserFileName

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# Discovery Counters #

#################################################################################################################################

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/ma-discovery-counters/filtered-deletions")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/ma-discovery-counters/filtered-objects")

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# Staging Counters #

#################################################################################################################################

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-no-change")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-update")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-delete")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-delete-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/staging-counters/stage-failure")

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# MV Inbound Counters #

#################################################################################################################################

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-remains")

$smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-projected-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-projected-no-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-joined-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-joined-no-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-flow-remove-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-delete-leave-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/flow-failure")

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-no-flow")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-filtered-leave-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-filtered-remove-mv")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/connector-delete-add-processed")

$smtpbody += "

"

# $mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-projected-remove-mv")

# $smtpbody += "

"

# $mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/inbound-flow-counters/disconnector-joined-remove-mv")

# $smtpbody += "

"

$smtpbody += "

"

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-update")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-rename")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-delete")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-delete-add")

$smtpbody += "

"

$mvValue = $RunHistory1XML.SelectSingleNode("run-history/run-details/step-details/export-counters/export-failure")

$smtpbody += "

"

$smtpbody += "

"

#################################################################################################################################

# Outbound Counters #

#################################################################################################################################

$outboundCounters=$RunHistory1XML.SelectNodes("run-history/run-details/step-details/outbound-flow-counters")

foreach($outboundCounter in $outboundCounters)

{

$mvValue = $outboundCounter.ma

if ($debug)

{

Write-Output ($outboundCounter)

Write-Output ($mvValue)

}

$smtpbody += "

"

$mvValue = $outboundCounter.SelectSingleNode("provisioned-add-flow")

$smtpbody += "

"

$mvValue = $outboundCounter.SelectSingleNode("provisioned-disconnect")

$smtpbody += "

"

$mvValue = $outboundCounter.SelectSingleNode("connector-flow")

$smtpbody += "

"

$smtpbody += "

"

}

$smtpbody += "

"

$smtpbody += "

Forefront Identity Manager Management Agent Run Results
 
 
" + $MA.Name +" Completed Successfully
MA Synchronization Status: " + $result.ReturnValue + "
" + $MA.Name +": Completed.
MA Synchronization Status: " + $result.ReturnValue + "
 
 
" + $MA.Name + " Run Statistics
 
MA Run Profile:" + $profileName+"
Start Date & Time:" + $MA.RunStartTime().ReturnValue + "
End Date & Time:" + $MA.RunEndTime().ReturnValue + "
Total Run Time:"+$duration.Hours+":"+$duration.Minutes+":"+$duration.Seconds + "
 
Connector Space Statistics
Total Connector Space Objects:" + $MA.NumCSObjects().ReturnValue +"
Total Connectors:" + $MA.NumTotalConnectors().ReturnValue +"
Connectors:" + $MA.NumConnectors().ReturnValue +"
Explicit Connectors:" + $MA.NumExplicitConnectors().ReturnValue +"
Total Disconnectors:" + $MA.NumTotalDisconnectors().ReturnValue +"
Disconnectors:" + $MA.NumDisconnectors().ReturnValue +"
Explicit Disconnectors:" + $MA.NumExplicitDisconnectors().ReturnValue +"
Total Placeholders:" + $MA.NumPlaceholders().ReturnValue +"
Total Placeholders:" + $MA.NumPlaceholders().ReturnValue +"
 
Discovoery Statistics
Filtered Deletions" + $mvValue.InnerText +"
Filtered Objects:" + $mvValue.InnerText +"
 
Stage Import Statistics
Import Stage Unchanged:" + $mvValue.InnerText +"
Import Stage Add:" + $mvValue.InnerText +"
Import Stage Updates:" + $mvValue.InnerText +"
Import Stage Deletes:" + $mvValue.InnerText +"
Import Stage Delete-Adds:" + $mvValue.InnerText +"
Import Stage Failures:" + $mvValue.InnerText +"
 
Inbound Statistics
Disconnectors
Filtered Disconnectors:" + $MA.NumFilteredDisconnectors().ReturnValue +"
Disconnectors" + $mvValue.InnerText +"
 
Metaverse
Projections - With Flow Updates:" + $mvValue.InnerText +"
Projections - Without Flow Updates:" + $mvValue.InnerText +"
Joins - With Flow Updates:" + $mvValue.InnerText +"
Joins - Without Flow Updates:" + $mvValue.InnerText +"
Deleted Metaverse Objects" + $mvValue.InnerText +"
Disconnected Metaverse Objects" + $mvValue.InnerText +"
Metaverse Flow Failures" + $mvValue.InnerText +"
Connector Objects
Connector With Flow Updates:" + $mvValue.InnerText +"
Connector Without Flow Updates:" + $mvValue.InnerText +"
Connector Filtered" + $mvValue.InnerText +"
Connector Filtered Deleted" + $mvValue.InnerText +"
Connector Delete-Add" + $mvValue.InnerText +"
Metaverse Disconnectors Projected" + $mvValue.InnerText +"
MV Deleted - Joined Disconnectors" + $mvValue.InnerText +"
 
Export Statistics
Export Adds" + $mvValue.InnerText +"
Export Updates" + $mvValue.InnerText +"
Export Renames" + $mvValue.InnerText +"
Export Deletes" + $mvValue.InnerText +"
Export Delete-Adds" + $mvValue.InnerText +"
Export Failures" + $mvValue.InnerText +"
 
OutBound Statistics for: " + $mvValue + "
Provisioning Adds:" + $mvValue.InnerText +"
Provisioning Disconnect:" + $mvValue.InnerText +"
Export Attribute Flow:" + $mvValue.InnerText +"
 
 
"

# Write-Output ( $smtpbody )

if($EmailperMA)

{

if ($debug)

{

$dbugmsg = "--> DB ---- In Multiple Email Send"

Write-Output ($dbugmsg)

}

$RHFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $RunHistFileName)

$smtpmsg.Attachments.Add($RHFile)

$IUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $ImportErrorUserFileName)

$smtpmsg.Attachments.Add($IUFile)

$EUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $ExportErrorUserFileName)

$smtpmsg.Attachments.Add($EUFile)

$smtpmsg.Body = $smtpbody

$smtpmsg.IsBodyHTML = $true

if ($SendEmails)

{

$smtp.Send($smtpmsg)

}

$RHFile.Dispose()

$IUFile.Dispose()

$EUFile.Dispose()

remove-item $RunHistFileName

remove-item $ImportErrorUserFileName

remove-item $ExportErrorUserFileName

}

else

{

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email Conat files"

Write-Output ($dbugmsg)

}

cat $RunHistFileName > $AllRuns

cat $ImportErrorUserFileName > $AllImportErrorRuns

cat $ExportErrorUserFileName > $AllExportErrorRuns

remove-item $RunHistFileName

remove-item $ImportErrorUserFileName

remove-item $ExportErrorUserFileName

}

Write-Output (" -> "+$msg)

Write-Output (" -> "+$result.ReturnValue)

}

}

}

if($abortnow) { break }

}

if(!$found -and !$abortnow)

{

Write-Output ("Not found MA Name:"+$MATypeNRun.name);

}

}

if($abortnow) { $abortnow | Out-Host }

$continue = ($params_numOfExecs -EQ 0) -OR ($numOfExecDone -lt $params_numOfExecs)

if($continue)

{

Write-Output-Banner("Sleeping "+$params_delayBetweenExecs+" seconds")

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email SLEEP Send"

Write-Output ($dbugmsg)

}

if(!$EmailperMA)

{

$smtpbody = "" + $abortnow +

"

" + $smtpbody

$RHFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllRuns)

$smtpmsg.Attachments.Add($RHFile)

$IUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllImportErrorRuns )

$smtpmsg.Attachments.Add($IUFile)

$EUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllExportErrorRuns )

$smtpmsg.Attachments.Add($EUFile)

$smtpmsg.Body = $smtpbody

$smtpmsg.IsBodyHTML = $true

if ($SendEmails -and $abortnow)

{

$smtp.Send($smtpmsg)

}

$RHFile.Dispose()

$IUFile.Dispose()

$EUFile.Dispose()

remove-item $AllRuns

remove-item $AllImportErrorRuns

remove-item $AllExportErrorRuns

}

Start-Sleep -s $params_delayBetweenExecs

}

else

{

Write-Output ("******************************")

}

}

while($continue)

if(!$EmailperMA)

{

$smtpbody = "" +

$abortnow + "

" + $smtpbody

if ($debug)

{

$dbugmsg = "--> DB ---- In Single Email Send"

Write-Output ($dbugmsg)

}

$RHFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllRuns)

$smtpmsg.Attachments.Add($RHFile)

$IUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllImportErrorRuns )

$smtpmsg.Attachments.Add($IUFile)

$EUFile = new-object Net.Mail.Attachment([string](get-location)+'\'+ $AllExportErrorRuns )

$smtpmsg.Attachments.Add($EUFile)

$smtpmsg.Body = $smtpbody

$smtpmsg.IsBodyHTML = $true

if ($SendEmails -and $abortnow)

{

$smtp.Send($smtpmsg)

}

$RHFile.Dispose()

$IUFile.Dispose()

$EUFile.Dispose()

remove-item $AllRuns

remove-item $AllImportErrorRuns

remove-item $AllExportErrorRuns

}

ArrowGreen Coexistence between Exchange forests (without trusts…)  -- Part 10: Configuring Free/Busy
   
Coexistence between Exchange forests (without trusts…) -- Part 9: Synchronization!

? Note: The entire list must be run through in order and progression to the next step cannot be done until the current step has completed for all Management agents!

Step 1: Right click the management agent, select “Run” and “Full Import (Staging Only)”.

Step 2: Right click the management agent, select “Run” and “Full Synchronization”.

Step 3: Right click the management agent, select “Run” and “Export”.

Step 4: Right click the management agent, select “Run” and “Delta Import”.

? Note: Step 4 (Delta Import) is to confirm the export was successful.

? Note: It is recommended to verify the contact objects were created in all Active Directory forests at this point.

 

Create a scheduled task to run hourly synchronization

? Note: This is required to keep both GALs up to date. If this hourly (or regular) synchronizations are not performed, eventually, the GALs will no longer represent accurate information.

? Note: The code for the synchronization scripts is located in the references chapter. No warranty is given on this code by the author of this document or Microsoft and it should be reviewed, as well as tested, before being placed in production environments.

Step 1: Open task scheduler (administrative tools > Task Scheduler).

Step 2: In the Actions Pane, click on Create Basic Task….

Step 3: Enter a Name and Description.

Step 4: As Trigger, use Daily.

Step 5: Have the task recur every day and select a start time.
image

Step 6: Use Start a program as action.
image

Step 7: Enter powershell in the Program/script field and –command .\start-sync.ps1 in the add arguments field. In the start in field, enter the directory where the script is located.
image

Step 8: In the Finish pane, tick the box next to Open the Properties dialog for this task when I click finish and click Finish.
image

Step 9: In the properties of the newly created task, click on the Triggers tab.

Step 10: In the Triggers tab, click the edit button to adapt the schedule of the task.
image

Step 11: In the Edit Trigger window, tick the box next to Repeat task every: and select 1 hour from the drop down box. Click on the drop down box next to for a duration of and select Indefinitely. Click OK.
image

Step 12: Click on Task Scheduler Library, select your task, right click it and select Run to test the execution of the task. A PowerShell window will open and show the progression through each synchronization step.
image

 

ArrowGreen

Coexistence between Exchange forests (without trusts…)  -- Part 8: Enabling Provisioning

ArrowGreen Coexistence between Exchange forests (without trusts…)  -- Part 10: Configuring Free/Busy
Coexistence between Exchange forests (without trusts…) -- Part 4: Preparing the US Exchange 2010 environment

Create receive connector

Step 1: Open the exchange management console

Step 2: Click on Hub Transport under Server Configuration

Step 3: In the Actions pane click on New Receive Connector

Step 4: In the Introduction page, under Name, enter a name for the receive connector. From the drop down box under Select the intended use for this receive connector, select Internal. Click next.
image

Step 5: On the Remote Network Settings page, remove the default remote IP addresses and enter the IP address of the server(s) in the remote domain. Click Next.
image

Step 6: On the New Connector page, click New.

Step 7: On the Completion page, click Finish.

Step 8: Right click the newly created connector and select properties.

Step 9: Click the Permission Groups tab.
image

Step 10: Tick the box next to Anonymous Users on the permission groups tab and click Apply and OK.
image

? Note: Repeat this step for every server who will be receiving SMTP traffic from the other organization.

Create a send connector

Step 1: Open the exchange management console

Step 2: Click on Hub Transport under Organization Configuration

Step 3: Click on the Send Connectors Tab

Step 4: In the Actions pane click on New Send Connector

Step 5: Enter a name for the send connector. From the drop down menu under Select the intended use for this Send connector select Internal.

Step 6: In the address space pane, click on Add.

Step 7: In the SMTP Address Space window, under Address, enter the domain name for the organization mail will be routed to. Enter a cost for the send connector if applicable.

Step 8: In the network setting page, select Route mail through the following smart hosts and click Add

Step 9: In the Add smart host window, enter the IP address of the HUB transport server in the organization you will be routing the mail to and click ok.

? Note: It is recommended to add multiple HUB servers in this field (if available) for redundancy.

Step 10: Click Next.

Step 11: Leave the authentication settings on their defaults. Click Next.

Step 12: In the Source server page, verify the defaults and click Next.

? Note: It is recommended to add multiple source servers here (if available) for redundancy.

Step 13: In the New Connector window click New.

Step 14: In the Completion window review the results and click Finish.

 

Change accepted domain to internal relay

Step 1: Open the exchange management console.

Step 2: Click on Hub Transport under Organization Configuration.

Step 3: Click on the Accepted Domains tab.

Step 4: Right click the domain that needs to be changed and select properties.
image

Step 5: On the Properties tab, select the radio button for Internal Relay Domain.
image

Step 6: On the Properties tab, click OK.

 

Add internal relay domain for uk.sutherland.com

Step 1: Open the exchange management console

Step 2: Click on Accepted Domains under Organization Configuration

Step 3: In the Actions pane click on New Accepted Domain

Step 4: On the New accepted domain page, enter a name and the accepted domain (for example: contoso.com). Tick the radio button for Internal Relay.
image

Step 5: Click New.

 

External domain preparations

1.1.2.1 Create MX record for uk.toasterlabs.com

Not documented

 

Create external DNS records for uk.toasterlabs.com (autodiscover & mail)

Not documented

 

ArrowGreen

Coexistence between Exchange forests (without trusts…)  -- Part 3: Preparing the UK Exchange 2007 environment

ArrowGreen Coexistence between Exchange forests (without trusts…)  -- Part 5: Preparing the GALSync Server
Office 365: Authentication

When we’re talking authentication the first thing that pops up in our minds is Active Directory. For years, active directory has been the staple identity provider for most companies and the foundational building block upon which most applications were built.

With Azure and O365, we need to think about the different authentication methods that could be. Are we going to an “all in the cloud” model? Federated identities? Hybrid active directory? Or maybe something else completely?

All in the Microsoft cloud

With the first possible solution we’re looking at an identity solution where nothing exists on premise. The most unlikely solution for the larger businesses, but if you’re a small or new business, this might be an option. You’re still in charge and you can still do, pretty much, the same things when it comes to authentication, but you’re hosting all your user information in the cloud with no servers on-prem.

clip_image002

1 - From the identity and authentication in office 2013 and O365 document

clip_image004

clip_image006

2 - From the Identity and authentication in office 2013 and O365 document

clip_image008

Living in a hybrid world

Most of the world does not live in an all Microsoft cloud only world. Companies have existing infrastructure, applications that need time to be ported and so much more items that restrict them from doing away with existing on-prem servers (and thank god for that or we would be out of a job!) that living in a hybrid world is necessary. But what does that look like?

Same-sign on

Behind door number one is the same-sign on option. Minimal on-prem infrastructure (a dirsync server is required) allows companies to leverage the “same sign-on” method. Historically the DirSync server would not sync end users password but we have that possibility now. OK, before anyone freaks out, let me clarify: Password Sync does not synchronize the password. It has no way of doing that. What it actually does is take the hash of the users password that exists in active directory, hashes that again, and syncs it up to Azure Active Directory. That way, Users can be authenticated using the same password they have in their on-prem active directory. But they will have to authenticate again. It is not single sign-on!

And yes, that’s a hash of a password hash. Hashception… (I really had to do that!)

Single sign-on

Single sign-on is what Microsoft preaches, but does require some extra infrastructure. For a start, you will need at least one ADFS server in order to authenticate against. Ideally you would have multiple so there is redundancy and you don’t lose your authentication infrastructure due to patching.

Authenticating against an ADFS server also means that users don’t need to re-enter their credentials when they are already logged in. Hence the name “Single Sign-On”.

Authentication flow with ADFS for an intranet user

clip_image010

1. An intranet user tries to access an application on Office 365, but hasn’t been authenticated before

2. The application redirects the user to Azure AD for authentication.

3. The user enters the username for the application and, because Azure AD knows ADFS has been set up, redirects that user to the ADFS server for authentication.

4. Since this is a Single sign-on scenario, and the user is working on a desktop which is domain-joined, ADFS issues a user token.

5. User token gets sent to the Intranet User.

6. The User token gets sent to Azure Active Directory, is validated and generates a new token for the application the user is trying to access.

7. The user can happily use the application.

Authentication flow with ADFS for an extranet user

clip_image012

1. Our extranet user tries to access the application for the very first time.

2. The application issues a redirect to Azure AD.

3. Once the redirect is processed, the user enters his or her credentials in the webpage. Azure AD, knowing the organization has ADFS enabled, issues a redirect to the ADFS server.

4. As the user is using the internet (Extranet) to access the ADFS server they hit the ADFS Proxy server which proxies all traffic to the ADFS server living in the internal network. Just like with the Intranet user the ADFS server issues a token.

5. The token gets sent to the user.

6. The token is passed on to Azure Active Directory, validated, a new token is created and passed down to the user.

7. The token is then used to authenticate against the application and the user can start working with vigor!

References:

  • Identity and authentication in Office 2013 and O365
    http://www.microsoft.com/en-us/download/details.aspx?id=38193
Add Comment Filed Under [ Exchange Office 365 ]
Coexistence between Exchange forests (without trusts…) -- Part 2: DNS Forwarders

Creating conditional forwarders

Step 1: Open DNS manager
image

Step 2: Select Conditional forwarders
image

Step 3: Right click “Conditional Forwards" and select “New Conditional Forwarder…”
image

Step 4: Enter the dns name the forwarder is being created for
image

 

Step 5: Enter the IP address(es) of the DNS server(s) authorative for the domain.
image

Step 6: Select Store this conditional forwarder in Active Directory, and replicate it as follows:
image

image

 

Step 7: Click OK
image

Step 8: Repeat in each forest you want to replicate to/from

ArrowGreen

Coexistence between Exchange forests (without trusts…)  -- Part 1: Conceptual

ArrowGreen Coexistence between Exchange forests (without trusts…)  -- Part 3: Preparing the UK Exchange 2007 environment
Recovery storage groups

As you might, or not, be aware off there is no item level based recovery in Exchange that is natively supported by Microsoft. So to perform recoveries we need the recovery storage group (captain obvious). The purpose of this is to mount the database you want to recover from on the Exchange server and use exmerge to merge the data you are recovering into the active database. An RSG will not be accessible to users and you can only create one at a time.

Exchange 2003:

1. Open Exchange system manager
2. Drill down to the server you want to recover to
3. Right click the server and select New > Recovery storage group
4. Specify the drive you have restored the database to. If possible use the same drive as the active database as this will improve performance quite a bit.
5. Click ok J
6. Right click the RSG and select “Add database to recover”
7. Select the right database you are going to recover.
8. Make sure that “This database can be overwritten by a restore” is checked (by default is should be)
9. Mount the database
10. Select the database in the recovery storage group, open the mailboxes container .
11. Select and right click the user(s) you want to restore.
12. Run Exchange task from the menu
13. Select the “recover mailbox data” task
14. Depending on your situation select either the Merge data or Copy data context
15. Schedule if necessary
16. Watch as your data is getting recovered.

Once this process is complete all data will be where it is supposed to be once more!

Exchange 2007:

1. Open up the Exchange management console
2. Go to the tools subset and open the Database Recovery Management tool
3. Select the storage group you want to link to the RSG
4. Double check the information presented in the next screen and click the Create the recovery storage group.
5. By default the RSG will be linked to the following folder: C:\program files\microsoft\exchange server\mailbox\<storage group>\RSGxxxxxxxxxx
6. Recover files to the RSG folder
7. Once recovered g o to the recovery storage group management tool and slect to mount the database.
8. Once mounted, go back to the task center and select Merge or copy mailbox contents
9. Make sure the proper database is selected and click on the gather merge information.
10. If you are dealing with a dial tone database click the “Swap database configurations” checkmark. If not, just click next
11. Click the Perform Pre-merge task
12. Select the mailboxes you wish to recover and let ExTRA do its thing.
13. Once the process is completed use ExTRA to dismount and remove the recovery storage group. Once that has been done you manually can remove the files in the RSGxxxxxxxx folder.

And that is it!

Exchange 2010:

1.       Open the exchange management shell

2.       Type in the following:

a.       New-mailboxdatabase “name” –server “servername” –recovery:$true –edbfilepath “path to restored edb file”

b.      Mount-database “name of DB you just created”

c.       Get-mailboxstatistics –database “name of restored database”

d.      New-mailboxrestorerequest –sourcedatabase “name of recovery database” –sourcestoremailbox “Username” –targetmailbox “emailaddress of target mailbox”

That will create a new recovery request. You can view the state of the request by using:

Get- MailboxRestoreRequest

Once completed, remove the request with

Get- MailboxRestoreRequest –status completed | remove-MailboxRestoreRequest

And that is it!

EDIT: Added the 2010 section :)

Technorati Tags: ,,,,,,,,,,,,,
,,,,,,,,,,,,,,
,,,,,,,,,,,,,,
,,,,,,

3 Comments Filed Under [ Exchange ]
Large mailboxes & managing multiple mailboxes…. Ohhhh My!
In this case the customer ran in to some serious client IOPS issues as the combined size of all the mailboxes bloated the OST file (the local outlook cache!) somewhere between 30GB and 50GB. Considering the limit for OST files in outlook 2010 and outlook 2013 is actually 50GB, as well as that outlook OST access is random, that causes some heavy disk usage! Something the client computers with their 7200rpm spindles just couldn’t handle… So out comes the magic stick and we solve the problem. B
Exchange 2013: Public Folder Migrations
In this article we take a look at Modern Public folders and outline how to migration the legacy version to the Modern (exchange 2013) version.
5 Comments Filed Under [ Exchange ]