Thursday, August 21, 2014

How to: Create anchor for delta import record in ECMA 2.0 GetImportEntries

#region FAIL http://social.technet.microsoft.com/Forums/en-US/a2bc4a62-a987-444a-9382-7a37ba49fcf8/getting-missingchangetype-error-during-delta-import-on-callbased-ecma2?forum=ilm2
//csentry.DN = row["dn"].ToString().Trim();
#endregion
/// Lesson learned: DO NOT name one of the columns 'DN'.
/// http://c--shark.blogspot.com/2014/08/lesson-learned-ecma2-do-not-name-one-of.html
csentry.AnchorAttributes.Add(
    AnchorAttribute.Create("SQL_DN", row["dn"].ToString().Trim()));

Lesson learned: ECMA2: Do NOT name one of the schema columns 'DN'

Here are my notes on troubleshooting errors with an ECMA 2.0. Turns out, the errors were due to the fact that one of the columns was named 'DN'. As you can imagine, troubleshooting this was maddening...
·        ECMA2 connector to SQL Server
·        One of the column names was 'DN'
·        In GetSchema:
if (string.Equals(key, "dn"StringComparison.CurrentCultureIgnoreCase))
{
    personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute(key, AttributeType.String));
}
·        Upon full import (see event log below):
o   missing-anchor-component
o   invalid-attribute-value
o   staging-error
·        GetImportEntries taken from MSFT example:
public GetImportEntriesResults GetImportEntries(GetImportEntriesRunStep importRunStep)
{
    //
    // In our sample below, we create objects using a fixed format.
    //
    GetImportEntriesResults importReturnInfo;
    List<CSEntryChange> csentries = new List<CSEntryChange>();
    // Full Import.
    if (OperationType.Full == m_ImportType)
    {
        // Object 1
        CSEntryChange csentry1 = CSEntryChange.Create();
        csentry1.ObjectModificationType = ObjectModificationType.Add;
        csentry1.ObjectType = "person";
        //csentry1.DN = "CN=Object1,CN=Users,DC=contoso,DC=com";
        csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("DN""1"));
        //csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("USERNAME", "Object1"));
        //csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("email", "Object1@contoso.com"));
        //csentry1.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("multiAttr", new List<object>() { "value1", "value1", "value3" }));
        csentries.Add(csentry1);
        // Object 2
        CSEntryChange csentry2 = CSEntryChange.Create();
        csentry2.ObjectModificationType = ObjectModificationType.Add;
        csentry2.ObjectType = "person";
        //csentry2.DN = "CN=Object2,CN=Users,DC=contoso,DC=com";
        csentry2.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("DN""2"));
        //csentry2.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("USERNAME", "Object2"));
        //csentry2.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("email", "Object2@contoso.com"));
        //csentry2.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("multiAttr", new List<object>() { "value4", "value5", "value6" }));
        csentries.Add(csentry2);
    }
    // Create the result
    importReturnInfo = new GetImportEntriesResults();
    importReturnInfo.MoreToImport = false;
    importReturnInfo.CSEntries = csentries;
    // Also, GetImportEntriesResults.CustomData can be used for retaining necessary data
    // for performing a subsequent import.
    // A typical case of this might be the watermark used
    // for delta import.
    //
    return importReturnInfo;
}
·        Event log:
FIMSynchronizationService
The server encountered an unexpected error while performing an operation for a management agent.
 "Microsoft.MetadirectoryServices.Impl.InternalError: 0x80230301
   at Microsoft.MetadirectoryServices.Impl.ScriptHost.ThrowExceptionFromHRESULT(Int32 hr)
   at Microsoft.MetadirectoryServices.Impl.Ecma2ConversionServices.AddAttributeToDImage(CDImage* pdimage, String attributeName, AttributeModificationType attributeModificationType, IList`1 attributeValueChanges, Int32 escapeReferenceDNValues)
   at Microsoft.MetadirectoryServices.Impl.Ecma2ConversionServices.ConvertToDImage(CSEntryChange csEntryChange, CDImage** ppDImage, Int32 escapeReferenceDNValues)
   at Microsoft.MetadirectoryServices.Impl.ScriptHost.InvokeExtMA_ImportEntry(UInt32 cBatchSize, UInt16* pcszCustomData, UInt32 cFullObject, _OCTET* rgoctFullObject, UInt32* rgomodt, UInt32* pcpcszChangedAttributes, UInt16*** prgpcszChangedAttributes, Int32 fIsDNStyleNone, UInt16** ppszUpdatedCustomData, _OCTET* rgoctCSImage, Int32* rgextec, UInt16** rgpszErrorName, UInt16** rgpszErrorDetail, Int32* pfMoreToImport)"
FIMSynchronizationService
The server encountered an unexpected error in the synchronization engine:
 "BAIL: MMS(8916): d:\bt\32669\private\source\miis\server\sync\syncstage.cpp(595): 0x80230302 (The image or delta doesn't have an anchor.)
Forefront Identity Manager 4.1.3559.0"
FIMSynchronizationService
The server encountered an unexpected error in the synchronization engine:
 "BAIL: MMS(6992): d:\bt\32669\private\source\miis\ma\xml\ma.cpp(5012): 0x80230005 (Management agent encountered an error while processing an object.)
BAIL: MMS(6992): d:\bt\32669\private\source\miis\server\sync\syncstage.cpp(595): 0x80230302 (The image or delta doesn't have an anchor.)
Forefront Identity Manager 4.1.3559.0"
FIMSynchronizationService
The server encountered an unexpected error in the synchronization engine:
 "BAIL: MMS(13268): d:\bt\32669\private\source\miis\ma\xml\ma.cpp(5078): 0x80230005 (Management agent encountered an error while processing an object.)
BAIL: MMS(13268): d:\bt\32669\private\source\miis\shared\ldaputils\dnutils.cpp(580): 0x80070057 (The parameter is incorrect.): Error parsing DN 99999: DN terminates in attribute name
BAIL: MMS(13268): d:\bt\32669\private\source\miis\ma\extensible\extensible.cpp(2057): 0x80070057 (The parameter is incorrect.)
BAIL: MMS(13268): d:\bt\32669\private\source\miis\server\sync\syncstage.cpp(595): 0x80230302 (The image or delta doesn't have an anchor.)
Forefront Identity Manager 4.1.3559.0"
FIMSynchronizationService
The server encountered an unexpected error in the synchronization engine:
 "BAIL: MMS(8916): d:\bt\32669\private\source\miis\ma\xml\ma.h(734): 0x80230005 (Management agent encountered an error while processing an object.)
BAIL: MMS(8916): d:\bt\32669\private\source\miis\ma\xml\ma.cpp(4358): 0x80230005 (Management agent encountered an error while processing an object.)
BAIL: MMS(8916): d:\bt\32669\private\source\miis\ma\xml\ma.cpp(5240): 0x80230005 (Management agent encountered an error while processing an object.)
BAIL: MMS(8916): d:\bt\32669\private\source\miis\server\sync\syncstage.cpp(595): 0x80230302 (The image or delta doesn't have an anchor.)
Forefront Identity Manager 4.1.3559.0"

Monday, July 22, 2013

FIM metaverse SQL query: expected rules list

 

set transaction isolation level read uncommitted

 

select

     mvpers.displayName as [User Display Name]

     ,mvpers.accountName as [User Account Name]

    ,mvere.displayName as [ERE Display Name]

    ,mvere.object_type

    ,link.*

from dbo.mms_mv_link link

join dbo.mms_metaverse mvpers

on link.object_id = mvpers.object_id

full outer join dbo.mms_metaverse mvere

on reference_id = mvere.object_id

where attribute_name = 'expectedRulesList'

 

Sunday, July 14, 2013

Outbound System Scoping Filter: "Contains" doesn't work on multi-valued attributes (and why no "Is Present"?)

I was excited to finally have a scenario in which I could use the new Outbound System Scoping Filter, but sadly its limitations put the kibosh on that idea.  I was surprised to discover (empirically, of course) that you cannot use a "contains" operator on a multi-valued attribute.  What's more, there is no "is present" operator at all.  Anyone else frustrated by FIM's lack of consistency? </rant>

Anyway, here are the tests that I ran, in case you're interested:


Test
Scoping filter
Result
Single-valued attribute equals
employeeID EQUAL 100000
OK
Multi-valued attribute contains string
Roles CONTAINS MyRoleName
FAIL
(Not Applied)
Attempt at multi-valued attribute contains string
Roles STARTSWITH MyRoleName
FAIL
(Not Applied)
Another attempt at multi-valued attribute contains string
Roles EQUAL MyRoleName
FAIL
(Not Applied)
Desperate attempt at multi-valued attribute contains string
Roles STARTSWITH %MyRoleName%
FAIL
(Not Applied)
Attempt at "Is Present"
employeeID NOTEQUAL NO SUCH EMPID
FAIL
(Applied when employeeID is not present)
Another attempt at "Is Present"
employeeID STARTSWITH %
FAIL
(Not Applied)
Desperate attempt at "Is Present"
employeeID GREATERTHAN 0
SUCCESS
100000: Applied
A100000: Applied
<empty>: Not Applied

And here's the explanation from Andreas on the forum:

"That is not supposed to work. The scoping filter is only intended for string values. It isn't documented because I didn't think of documenting it."

Tuesday, July 9, 2013

FIM metaverse SQL query - employeeID contributing MA


set transaction isolation level read uncommitted

SELECT TOP 1000
    --lg.[object_id]
    mv.object_type
      --,lg.[displayName]
      --,lg.[employeeID]
      --,lg.[HR_USERID]
      --,lg.[HR_Joined]

    ,mv.employeeID as [employeeID]
    ,ma_empid.ma_name as [employeeID MA]

    ,mv.displayName as displayName
    ,ma_disp.ma_name as [displayName MA]

    ,mv.HR_USERID as HR_USERID
    ,ma_uid.ma_name as [HR_USERID MA]

    ,mv.HR_Joined as HR_Joined
    ,ma_hrj.ma_name as [HR_Joined MA]

  FROM [FIMSynchronizationService].[dbo].[mms_metaverse_lineageguid] lg
  join [FIMSynchronizationService].dbo.mms_metaverse mv
  on lg.object_id = mv.object_id

  left join [FIMSynchronizationService].[dbo].[mms_lineage_cross_reference] cr_empid
  on cr_empid.lineage_id = lg.employeeID
  left join [FIMSynchronizationService].[dbo].[mms_management_agent] ma_empid
  on ma_empid.ma_id = cr_empid.ma_id

  left join [FIMSynchronizationService].[dbo].[mms_lineage_cross_reference] cr_disp
  on cr_disp.lineage_id = lg.displayName
  left join [FIMSynchronizationService].[dbo].[mms_management_agent] ma_disp
  on ma_disp.ma_id = cr_disp.ma_id

  left join [FIMSynchronizationService].[dbo].[mms_lineage_cross_reference] cr_uid
  on cr_uid.lineage_id = lg.HR_USERID
  left join [FIMSynchronizationService].[dbo].[mms_management_agent] ma_uid
  on ma_uid.ma_id = cr_uid.ma_id

  left join [FIMSynchronizationService].[dbo].[mms_lineage_cross_reference] cr_hrj
  on cr_hrj.lineage_id = lg.HR_Joined
  left join [FIMSynchronizationService].[dbo].[mms_management_agent] ma_hrj
  on ma_hrj.ma_id = cr_hrj.ma_id

  where object_type = 'person'
  and (
        ma_empid.ma_name = 'FIM MA'
        or mv.employeeID is null
        --or mv.HR_Joined is null
    )


Wednesday, June 26, 2013

Dude, where's my flow?

Got burned by FIM's wacky function behavior again, so I thought I'd actually create some documentation on it.

I set up a pretty common flow: Name = First Middle Last

However, when the middle name is empty, I don't want two blanks between first and last name.  So, how's this for a custom expression?  (Note that I'm assuming that first and last name are always present.)

Trim(givenName+" "+HR_MIDDLE_NAME)+" "+sn

Submit, import, sync.  Now I run a full sync from my target MA.  No errors.  We're good, right?  Not so fast; let's check the attribute flow.  No flow into Name.

Okay, so let's run a full sync preview on one of the connectors.  The status of the flow is Error.  (Good luck finding an error message.)
  • Full sync preview
    • Connector updates
      • Export attribute flow
        • Outbound sync rules
Status
MV attribute
Mapping type
Data source attribute
Initial value
Final value
Error

sync-rule-mapping - expression
NAME

(Unchanged)

So what happened?  Evidently, FIM doesn't like it when you concatenate an attribute that isn't present on the metaverse object.  (Has a funny way of showing it though, doesn't it?)

For my reference and yours, here are the results of other experiments that I ran on this flow.
  • HR_MIDDLE_NAME is not present in MV
  • (Assume givenName and sn are always present)

Description
Function
Flow definition
Result/status
MV attribute not present, embedded expr in Trim
CustomExpression
Trim(givenName+" "+HR_MIDDLE_NAME)+" "+sn
Error
Same expression but using concat feature in portal interface
Concat
Trim(CustomExpression(givenName+" "+HR_MIDDLE_NAME))+" "+sn
Error
Added IsPresent check and removed Trim
CustomExpression
givenName+IIF(IsPresent(HR_MIDDLE_NAME)," "+HR_MIDDLE_NAME,"")+" "+sn
Success
Trim function with no assumptions
CustomExpression
Trim(
""+
IIF(IsPresent(givenName),givenName,"")+
" "+
IIF(IsPresent(HR_MIDDLE_NAME),HR_MIDDLE_NAME,"")+
" "+
IIF(IsPresent(sn),sn,"")
)
Parse failure:
Parameter of function Trim does not match
(Test embedded expr in Trim)
CustomExpression
Trim(givenName+" "+sn)
(Success)
(Test MV attribute not present in Trim)
CustomExpression
givenName+" "+HR_MIDDLE_NAME+" "+sn
(Error)

Friday, June 14, 2013

sync-rule-flow-provisioning-failed Microsoft.MetadirectoryServices.ProvisioningBySyncRuleException: attribute is read-only

Ran into this problem the other day, and I was a bit confused.

sync-rule-flow-provisioning-failed
Microsoft.MetadirectoryServices.ProvisioningBySyncRuleException: attribute is read-only

I Googled the error message, but I didn't get any direct hits.  Turns out, I'd forgotten to check the Initial Flow Only box on the anchor attribute in the outbound sync rule (OSR).

The following links helped find the answer.
http://social.technet.microsoft.com/Forums/en-US/ilm2/thread/1aa13147-e16c-4e99-a7da-76e3c9e8c10d/
http://social.technet.microsoft.com/Forums/en-US/ilm2/thread/6ac2bed1-9704-4fcc-94d2-3be73c5a7f47

I guess this is one of those subtle features that I'd taken for granted.  Essentially, this means that there's no built-in "anchor rename" feature on the SQL MAs (this is an Oracle MA, BTW).

Here's another subtle feature that's closely related to this one.  As Markus mentions in his article, "In case of a SQL management agent, the anchor attribute is also the DN."  However, this doesn't mean that the CS object DN is exported to the anchor column.  In fact, you can arbitrarily initialize the CS object DN with the csObjectID (as Markus mentions); and assuming your SQL anchor is generated automatically upon export, you can join on the anchor (like userid) during inbound sync, and (if I recall correctly) the CS object DN will be set equal to the anchor.