http://marcdekeyser.com

Script Browser & Script Analyzer

Good morning!

 

I randomly stumbled across these tools (Download link: https://www.microsoft.com/en-us/download/confirmation.aspx?id=42525). If you work with PowerShell scripting a lot these could be useful! The Script Browser allows you to browse the script repository from the PowerShell ISE directly, the Script Analyzer actually scans a script you write against recommended practices.

 

This is making my life a little bit easier, I hope it does the same for you!



O365 Selective licensing script

I finally digged-in and finished this script. It runs based on a CSV file allowing you to specify what user gets what modality enabled. I should probably start a git to manage my scripts better, but that'll have to wait until after my move...

Installing the Windows Azure Active Directory Synchronization Tool (a.k.a. DirSync)


IIS ARR: Installing the reverse proxy server

Step 1: Open Server manager

Step 2: Install the “web server (IIS)” component. Accept all defaults.

step 3: Download the web platform installer

Step 4: Run the WPi

Step 5: Search for the Application Request Routing component

Step 6: Click on “Add” next to Application Request Routing 3.0

clip_image002

Step 7: Click on “Install

clip_image004

Step 8: Accept the Prerequisites list

clip_image006

Step 9: Wait for the download and installation to finish

clip_image008

Step 10: Click “Finish

clip_image010

Step 11: Open IIS manager

clip_image002[5]

Step 12: Select your server in the Internet Information Services (IIS) Manager

clip_image004[5]

Step 13: Select “Server Certificates

clip_image006[5]

Step 14: Verify the correct certificate is listed

clip_image008[5]

If the certificate is not listed, use the “Import…” link in the action pane to import the correct certificate.

clip_image010[5]



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

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 10: Configuring Free/Busy

Note: In order for Free/Busy to work Outlook Anywhere needs to be enabled in both forest and auto discover needs to be functioning properly. Additionally, the external URLs for EWS need to be configured.

Note: A service account in each forest is required for authentication purposes. This account should not have a mailbox and the minimum amounts of rights possible. In the example configuration it has been configured as “\svc_fb”.

Step 1: Open the Exchange management shell

Note: Step 2 relates to the target forest. This is the forest you are pulling the information to.

Step 2: Run “Set-AvailabilityConfig -OrgWideAccount ‘\svc_fb’

image

Step 3: Run “$a = Get-Credential (Enter the credentials for organization-wide user the domain you want to get Free/Busy from)”
image

Step 4: Run “Add-AvailabilityAddressspace -Forestname Contoso.com -Accessmethod OrgWideFB -Credential:$a
image

 

ArrowGreen Coexistence between Exchange forests (without trusts…)  -- Part 9: Synchronization!
ArrowGreen Coexistence between Exchange forests (without trusts…)  -- Part 11: References

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