{"id":827,"date":"2010-06-03T12:03:32","date_gmt":"2010-06-03T12:03:32","guid":{"rendered":"https:\/\/www.wapshere.com\/missmiis\/?p=827"},"modified":"2010-12-15T15:12:06","modified_gmt":"2010-12-15T15:12:06","slug":"phase-one-joins-and-data-matching","status":"publish","type":"post","link":"https:\/\/www.wapshere.com\/missmiis\/phase-one-joins-and-data-matching","title":{"rendered":"Phase One Joins and Data Matching"},"content":{"rendered":"<p>I&#8217;ve just posted a <a href=\"http:\/\/social.technet.microsoft.com\/Forums\/en-US\/identitylifecyclemanager\/thread\/be607357-2e40-4161-af53-83f3f59c5a54\">new Greatest Hits article<\/a> on the ILM forum on the subject of how ILM\u00c2\u00a0(or the FIM Sync Service) can be used to clean up the mess of existing accounts, before you can actually get \u00c2\u00a0on to the more interesting tasks of provisioning and updating. With the way FIM codeless sync works, needing an existing attribute to match on, and only allowing simple matching rules, it will be more important than ever to start from a position of tidy directories with correctly identified existsing accounts.\u00c2\u00a0Here&#8217;s the article&#8230;<!--more-->\u00c2\u00a0<\/p>\n<h2>Phase One Joins and Data Matching<\/h2>\n<p>The tedious truth is that most IdM projects must begin with a phase of data matching and cleaning. Before you can start to automate management of identities, you need a predictable<br \/>\ndata set around which to base your rules. Many organizations today, whether due to changes in IT personnel, company mergers, variable naming conventions or lack of guidance on handling resignations, have existing user account bases that can only be described as a mess.<\/p>\n<p>This document covers some of the methods you can use with ILM to get through that first project phase. Unfortunately there are no magic bullets here \u00e2\u20ac\u201c eye-straining, brain-numbing trawls through long lists of unmatched accounts cannot usually be avoided. What you can do is try to extract quality data, and construct your lists as helpfully as possible, so they can be targeted at the people whose eyeballs and brains are most likely to give the best response.<\/p>\n<p>It must be added that this is a very big topic, and the methods you choose will depend on the data you are faced with, and the aims of your project.<\/p>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Joins<\/h2>\n<p>Really, it\u00e2\u20ac\u2122s all about joins.<\/p>\n<p>Once existing accounts are joined to their correct data source (such as matching an HR record to an AD account) you can begin to flow updates.<\/p>\n<p>Once you have a clear idea who does and does not have an account in a target directory, you can begin to make provisioning and deprovisioning decisions.<\/p>\n<div>But be careful \u00e2\u20ac\u201c your joins must be reliable! The last thing you want is to kill the credibility of your fledgling IdM project by updating someone\u00e2\u20ac\u2122s account with another person\u00e2\u20ac\u2122s details or, even worse, deleting their account because you thought they\u00e2\u20ac\u2122d left!\u00c2\u00a0<\/div>\n<div><strong>\u00c2\u00a0<\/strong><\/div>\n<p><strong>\u00c2\u00a0<\/p>\n<p><\/strong><\/p>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Your eventual aim is a simple Join Rule<\/h3>\n<p>When you first start data matching, you will use a lot of different join rules, and you will make joins manually and with CSV files (more on that below). But keep<br \/>\nthis in mind:\u00c2\u00a0<\/p>\n<p><strong>Any join made manually, or with extra effort, should be considered temporary.<\/strong>\u00c2\u00a0<\/p>\n<p>There are various situations in ILM which can only be reliably rectified by a clear-out and re-import of a connector space.<\/p>\n<p>Always plan for this.<\/p>\n<p>Ideally, when you re-import a connector space, you will have a single, direct join rule which effortlessly re-joins all your objects. And to achieve this we use&#8230;\u00c2\u00a0<\/p>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Breadcrumbing<\/h3>\n<p>Once the join is verified you should export a uniquely identifying attribute, such as an employee number, to the target directory.<\/p>\n<p>After that a simple \u00e2\u20ac\u0153<em>employeeID = employeeID<\/em>\u00e2\u20ac\u009d type join rule is all you will need.\u00c2\u00a0<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/public.bay.livefilestore.com\/y1puu1gB56bAwNV0j6l5x2arQF_QoG9rXeWeWhfCwNo7IE_bMzlEmKJickwi1p_DwfxfQ5tMoc61tlLdl-OQ5CZow\/CW01.png\" alt=\"\" width=\"602\" height=\"150\" \/>\u00c2\u00a0<\/p>\n<p>Sometimes you are faced with an import-only system. For either technical or political reasons you are not able to export the breadcrumb attribute.<\/p>\n<p>There are a couple of things you can do:\u00c2\u00a0<\/p>\n<ol>\n<li>\u00c2\u00a0Import the DN or other identifying attribute from the target directory into an attribute on the Metaverse object.<strong>But be warned<\/strong>, you could still lose these joins if you had to repopulate your Metaverse.<\/li>\n<li>\u00c2\u00a0To be on the safe side you should also \u00e2\u20ac\u0153save\u00e2\u20ac\u009d these joins by exporting an identifying pair somewhere else \u00e2\u20ac\u201c for example using a database or text file<br \/>\nMA, as pictured below.<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/public.bay.livefilestore.com\/y1pvIj0ZbLAvgRj_vd6hwUtb7dq1sv3Qd9Q0OraE_hLtCfEzrNaDwcbf7LaQIYkELBIocV0-A0ZP211HvM092E6WA\/CW02.png\" alt=\"\" width=\"602\" height=\"208\" \/>\u00c2\u00a0<\/p>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Understanding Join Rules<\/h3>\n<p>There are some key points to note about Join Rules:\u00c2\u00a0<\/p>\n<ol>\n<li>Join rules operate on the CS object. It takes one CS object and attempts to find a match among all Metaverse objects of the correct type, even if they are already joined.<\/li>\n<li>Components within one rule are AND-ed together \u00e2\u20ac\u201c they must all match.<\/li>\n<li>Multiple rules are evaluated top down, so put your strongest rules at the top.<\/li>\n<li>While you can offer variations on a CS objects attribute using an Advanced Join Rule, you have to find an exact match with an attribute already on a Metaverse<br \/>\nobject. There are no \u00e2\u20ac\u0153<em>StartsWith<\/em>\u00e2\u20ac\u009d or \u00e2\u20ac\u0153<em>Contains<\/em>\u00e2\u20ac\u009d comparisons.<\/li>\n<\/ol>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Resolve Join<\/h2>\n<p>At the bottom of the Join Rule configuration you will see a check box \u00e2\u20ac\u0153<em>Use rules extension to resolve<\/em>\u00e2\u20ac\u009d.<\/p>\n<p>Here you can link to code you write under the <em>ResolveJoinSearch<\/em> subroutine in the MA extension.<\/p>\n<p>The Resolve rule is used when ILM finds multiple possible matches in the Metaverve (including already-joined objects).<\/p>\n<p>All the possible matches into the collection rgmventry and your job, in the code, is to check through them, looking for the best possible match.<\/p>\n<p>If your code finds an ideal match you return the index number of the object in imventry, and set the value of ResolveJoinSearch to true.<\/p>\n<p>The following example only joins if a single, unjoined Metaverse object was found.\u00c2\u00a0<\/p>\n<div style=\"background-color: #e6e6e6; width: 620px; overflow: scroll;\">\n<pre style=\"background-color: #e6e6e6; font-family: 'Courier New'; font-size: 11px;\" lang=\"x-js\">Public Function ResolveJoinSearch(ByVal joinCriteriaName As String, ByVal csentry As CSEntry, ByVal rgmventry() As MVEntry, ByRef imventry As Integer, ByRef MVObjectType As String) As Boolean Implements IMASynchronization.ResolveJoinSearch\r\n<span lang=\"en-us\">   <\/span>Select Case joinCriteriaName\r\n<span lang=\"en-us\">      <\/span>Case \"Resolve_NotYetJoined\"\r\n<span lang=\"en-us\">         <\/span>If rgmventry.Length = 1 AndAlso _\r\n<span lang=\"en-us\">   <\/span> <span lang=\"en-us\">     <\/span> <span lang=\"en-us\">  <\/span>rgmventry(0).ConnectedMAs(\"AD\").Connectors.Count = 0  Then\r\n<span lang=\"en-us\">      <\/span> <span lang=\"en-us\">  <\/span> <span lang=\"en-us\">  <\/span>imventry = 0\r\n<span lang=\"en-us\">            <\/span>Return True\r\n<span lang=\"en-us\">         <\/span>Else\r\n<span lang=\"en-us\">            <\/span>Return False\r\n<span lang=\"en-us\">         <\/span>End If\r\n<span lang=\"en-us\">   <\/span>End Select\r\nEnd Function<\/pre>\n<\/div>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Advanced Join Rules<\/h3>\n<p>A simple join rule directly matches a connector space attribute to a Metaverse attribute:<\/p>\n<table style=\"border-collapse: collapse;\" border=\"0\">\n<tbody>\n<tr>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">Connector Space<\/th>\n<th style=\"text-align: center; background-color: #d4d0c8; width: 15px; font-size: 11px; font-weight: bold;\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">Metaverse<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">givenName = Kathryn<\/td>\n<td style=\"width: 15px;\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">FirstName = Kathryn<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">sn = Bigalow<\/td>\n<td style=\"width: 15px;\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">Lastname = Bigalow<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>With an Advanced Join Rule you construct a list of possible values with which to find an exact match in the Metaverse:<\/p>\n<table style=\"border-collapse: collapse;\" border=\"0\">\n<tbody>\n<tr>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">Connector Space<\/th>\n<th style=\"text-align: center; background-color: #d4d0c8; width: 15px; font-size: 11px; font-weight: bold;\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">Metaverse<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">givenName = KathryngivenName = KategivenName = Kathy<\/td>\n<td style=\"width: 15px;\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\" valign=\"top\">FirstName = Kate<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">sn = Bigalow<\/td>\n<td style=\"width: 15px;\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\" valign=\"top\">Lastname = Bigalow<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Sometimes you can use code rules to make your list of possible matches (eg., presenting a phone number in different formats); other times you have to use long<br \/>\nlook-up lists of possible variations (try genealogy websites for name-variation lists).<\/p>\n<p>The following example uses a lookup file of aliases, where each line has the possible variations on a name.<\/p>\n<p>If a match to the first name is found on the connector space object, the whole line is added to the possible values to search for in the Metaverse.\u00c2\u00a0<\/p>\n<p>Elizabeth,Liz,Beth,Betty<br \/>\nDavid,Dave,Davey<br \/>\nJerome,J\u00c3\u00a9r\u00c3\u00b4me<br \/>\n&#8230;\u00c2\u00a0<\/p>\n<div style=\"background-color: #e6e6e6; width: 620px; overflow: scroll;\">\n<pre style=\"background-color: #e6e6e6; font-family: 'Courier New'; font-size: 11px;\" lang=\"x-js\">Public Class MAExtensionObject\r\n  <span lang=\"en-us\"> <\/span>Implements IMASynchronization\r\n  <span lang=\"en-us\"> <\/span>Dim fileAliases As System.IO.StreamReader\r\n  <span lang=\"en-us\"> <\/span>Dim arrAliases As String()\r\n  <span lang=\"en-us\"> <\/span>Dim i As Integer\r\n\r\n  <span lang=\"en-us\"> <\/span>Public<span lang=\"en-us\"> <\/span>Sub Initialize() Implements IMASynchronization.Initialize\r\n    <span lang=\"en-us\">  <\/span>fileAliases = New System.IO.StreamReader(\"C:\\aliases.txt\",  System.Text.Encoding.Default)\r\n    <span lang=\"en-us\">  <\/span>i = 0\r\n    <span lang=\"en-us\">  <\/span>While Not fileAliases.EndOfStream\r\n      <span lang=\"en-us\">   <\/span>ReDim Preserve arrAliases(i)\r\n      <span lang=\"en-us\">   <\/span>arrAliases(i) = fileAliases.ReadLine\r\n      <span lang=\"en-us\">   <\/span>i = i + 1\r\n    <span lang=\"en-us\">  <\/span>End While\r\n\r\n    <span lang=\"en-us\"> <\/span>fileAliases.Close()\r\n  <span lang=\"en-us\"> <\/span>End Sub\r\n\r\n<span lang=\"en-us\">   <\/span>Public Sub MapAttributesForJoin(ByVal FlowRuleName As String, ByVal csentry As CSEntry, ByRef values As ValueCollection) Implements IMASynchronization.MapAttributesForJoin\r\n   <span lang=\"en-us\">   <\/span>Select FlowRuleName\r\n      <span lang=\"en-us\">   <\/span>Case \"Join_aliases\"\r\n         <span lang=\"en-us\">   <\/span>Dim aliasList As String\r\n         <span lang=\"en-us\">   <\/span>Dim value As String\r\n\r\n         <span lang=\"en-us\">   <\/span>For Each aliasList In arrAliases\r\n            <span lang=\"en-us\">   <\/span>If aliasList.Contains(csentry(\"givenName\").Value) Then\r\n               <span lang=\"en-us\">   <\/span>For Each value In aliasList.Split(\",\".ToCharArray)\r\n                 <span lang=\"en-us\"> <\/span> <span lang=\"en-us\">  <\/span>values.Add(value)\r\n               <span lang=\"en-us\">   <\/span>Next\r\n\t<span lang=\"en-us\">      <\/span>End If\r\n         <span lang=\"en-us\">   <\/span>Next\r\n    <span lang=\"en-us\">     <\/span>End Select\r\n  <span lang=\"en-us\">   <\/span>End Sub\r\nEnd Class<\/pre>\n<\/div>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Using CSV Files<\/h2>\n<p>A lot of the difficult account matching will probably be done outside ILM.<\/p>\n<p>It is therefore useful to be able to \u00e2\u20ac\u0153export\u00e2\u20ac\u009d lists of possible matches, and later, \u00e2\u20ac\u0153import\u00e2\u20ac\u009d the joins from a CSV file.<\/p>\n<p>Be careful when writing to files from extension code \u00e2\u20ac\u201c the DLL doesn\u00e2\u20ac\u2122t unload for five minutes after the MA run completes, which means you may have to wait for it<br \/>\nto finish writing to the file. If you\u00e2\u20ac\u2122re in a hurry, recompiling the code will force the DLL to finish writing to the file.\u00c2\u00a0<\/p>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Export Possible Matches to a CSV file<\/h3>\n<p>You can use the Resolve rule to export possible matches to a CSV file. The following example resolves the join rule \u00e2\u20ac\u0153sn | Direct | lastname\u00e2\u20ac\u009d. As a match on the last<br \/>\nname alone is too weak for an immediate join, we just write the possible matches to the text file.\u00c2\u00a0<\/p>\n<div style=\"background-color: #e6e6e6; width: 620px; overflow: scroll;\">\n<pre style=\"background-color: #e6e6e6; font-family: 'Courier New'; font-size: 11px;\" lang=\"x-js\">Public Class MAExtensionObject<span lang=\"en-us\"> <\/span>Implements IMASynchronization\r\n  <span lang=\"en-us\"> <\/span>Dim fileMatches As System.IO.StreamWriter\r\n\r\n<span lang=\"en-us\"> <span class=\"style1\">  <\/span><\/span>Public<span lang=\"en-us\"> <\/span>Sub Initialize() Implements IMASynchronization.Initialize\r\n    <span lang=\"en-us\">  <\/span>fileMatches = New System.IO.StreamWriter(\"C:\\possible matches.txt\", System.Text.Encoding.Default)\r\n  <span lang=\"en-us\"> <\/span>End Sub\r\n\r\n  <span lang=\"en-us\"> <\/span>Public Sub Terminate() Implements IMASynchronization.Terminate\r\n    <span lang=\"en-us\">  <\/span>fileMatches.Close()\r\n  <span lang=\"en-us\"> <\/span>End Sub\r\n\r\n<span lang=\"en-us\">   <\/span>Public Function ResolveJoinSearch(ByVal joinCriteriaName As String, ByVal csentry As CSEntry, ByVal rgmventry() As MVEntry, ByRef imventry As Integer, ByRef MVObjectType As String) As Boolean Implements IMASynchronization.ResolveJoinSearch\r\n  <span lang=\"en-us\">    <\/span>Select Case joinCriteriaName\r\n      <span lang=\"en-us\"> <span class=\"style1\">  <\/span><\/span>Case \"Resolve_Lastname\"\r\n         <span lang=\"en-us\">   <\/span>Dim MAName As String = csentry.MA.Name\r\n         <span lang=\"en-us\">   <\/span>Dim mvobject As MVEntry\r\n         <span lang=\"en-us\">   <\/span>Dim cFirstname, mFirstname As String\r\n\r\n         <span lang=\"en-us\">   <\/span>If csentry(\"givenName\").IsPresent Then\r\n     <span lang=\"en-us\">          <\/span>cFirstname = csentry(\"givenName\").StringValue\r\n   <span lang=\"en-us\">         <\/span>Else\r\n     <span lang=\"en-us\">       <\/span> <span lang=\"en-us\">  <\/span>cFirstname = \"UNKNOWN\"\r\n         <span lang=\"en-us\">   <\/span>End If\r\n\r\n         <span lang=\"en-us\">   <\/span>If mvobject(\"firstname\").IsPresent Then\r\n     <span lang=\"en-us\">       <\/span> <span lang=\"en-us\">  <\/span>mFirstname = mvobject(\"firstname\").StringValue\r\n   <span lang=\"en-us\">         <\/span>Else\r\n     <span lang=\"en-us\">       <\/span> <span lang=\"en-us\">  <\/span>mFirstname = \"UNKNOWN\"\r\n         <span lang=\"en-us\">   <\/span>End If\r\n\r\n         <span lang=\"en-us\">   <\/span>For Each mvobject In rgmventry\r\n           <span lang=\"en-us\">    <\/span>If mvobject.ConnectedMAs(MAName).Connectors.Count = 0  Then\r\n            <span lang=\"en-us\"> <span class=\"style1\">  <\/span><\/span> <span lang=\"en-us\">  <\/span>fileMatches.WriteLine(csentry(\"sn\").StringValue <span lang=\"en-us\">&amp;<\/span> \";\" _\r\n     \t\t<span lang=\"en-us\">   &amp;<\/span> cFirstname <span lang=\"en-us\">&amp;<\/span> \";\" _\r\n     \t\t<span lang=\"en-us\">   &amp;<\/span> mvobject(\"lastname\").StringValue <span lang=\"en-us\">&amp;<\/span> \";\" _\r\n     \t\t<span lang=\"en-us\">   &amp;<\/span> mFirstname)\r\n           <span lang=\"en-us\">    <\/span>End If\r\n         <span lang=\"en-us\">   <\/span>Next\r\n         <span lang=\"en-us\">   <\/span>Return False\r\n<span lang=\"en-us\">         <\/span>End Select\r\n<span lang=\"en-us\"> <span class=\"style1\">  <\/span><\/span>End Function\r\n<span lang=\"en-us\">End Class<\/span><\/pre>\n<\/div>\n<p>You will need to adapt this code of course. Firstly, you probably want to export a lot more identifying information in your CSV file \u00e2\u20ac\u201c department, email address,<br \/>\ndn &#8230; whatever helps. Next, it can really help to supplement your possible matches with a probability score. This is where you do a series of tests and add points-<br \/>\nthe more points, the higher the chance of the match. For example:\u00c2\u00a0<\/p>\n<ul>\n<li>Names similar*\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 +1<\/li>\n<li>Department the same +1<\/li>\n<li>City the same\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0<br \/>\n+1<\/li>\n<\/ul>\n<p>*Some tips for testing if names are similar:\u00c2\u00a0<\/p>\n<ul>\n<li>Strip out all spaces, dashes and hyphens then compare;<\/li>\n<li>Check if one string is contained in the other (so that \u00e2\u20ac\u0153Sally-Anne\u00e2\u20ac\u009d gets<br \/>\na point for \u00e2\u20ac\u0153Sally\u00e2\u20ac\u009d);<\/li>\n<li>Use a function which compares string similarity (search \u00e2\u20ac\u0153Soundex\u00e2\u20ac\u009d and \u00e2\u20ac\u0153Levenshtein<br \/>\ndistance\u00e2\u20ac\u009d).<\/li>\n<\/ul>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Join from CSV<\/h2>\n<p>You can use an Advanced Join Rule to \u00e2\u20ac\u0153<em>import<\/em>\u00e2\u20ac\u009d joins from a CSV file.<\/p>\n<p>Firstly, our CSV file must be constructed like this:\u00c2\u00a0<\/p>\n<p>CS_identifier;MV_identifier\u00c2\u00a0<\/p>\n<p>For example, if we are trying to match an AD account against Metaverse objects imported from the HR system, we populate the CSV with the AD DN and the employeeID:\u00c2\u00a0<\/p>\n<p>CN=Fred Bloggs,OU=User,OU=MyOrg,DC=mydomain,DC=com;0012988\u00c2\u00a0<\/p>\n<p>Next we create the Advanced Join Rule which will look up the csobject\u00e2\u20ac\u2122s DN in the text file, but use the employeeID to search the Metaverse.\u00c2\u00a0<\/p>\n<p>Note that you can\u00e2\u20ac\u2122t actually use the DN in the join rule \u00e2\u20ac\u201c but that\u00e2\u20ac\u2122s ok, just use any attribute that definitely exists. Eg.,\u00c2\u00a0<\/p>\n<p>sAMAccountName | Rules extension \u00e2\u20ac\u201c Join_CSV | employeeID\u00c2\u00a0<\/p>\n<p>And now for the code :\u00c2\u00a0<\/p>\n<div style=\"background-color: #e6e6e6; width: 620px; overflow: scroll;\">\n<pre style=\"background-color: #e6e6e6; font-family: 'Courier New'; font-size: 11px;\" lang=\"x-js\">Imports Microsoft.MetadirectoryServices\r\n\r\nPublic Class MAExtensionObject<span lang=\"en-us\"> <\/span>Implements IMASynchronization\r\n  <span lang=\"en-us\"> <\/span>Dim joins As String()\r\n  <span lang=\"en-us\"> <\/span>Dim i As Integer\r\n\r\n<span lang=\"en-us\">   <\/span>Public<span lang=\"en-us\"> <\/span>Sub Initialize() Implements IMASynchronization.Initialize\r\n   <span lang=\"en-us\">   <\/span>Dim fileJoins As System.IO.StreamReader\r\n   <span lang=\"en-us\">   <\/span>Dim strLine As String\r\n   <span lang=\"en-us\">  <\/span>'Open the csv file and read into an array\r\n   <span lang=\"en-us\">   <\/span>fileJoins = New System.IO.StreamReader(\"C:\\joins.csv\", System.Text.Encoding.Default)\r\n   <span lang=\"en-us\">   <\/span>i = 0\r\n   <span lang=\"en-us\">   <\/span>While Not fileJoins.EndOfStream\r\n     <span lang=\"en-us\"> <\/span> <span lang=\"en-us\">  <\/span>ReDim Preserve joins(i)\r\n     <span lang=\"en-us\">    <\/span>joins(i) = fileJoins.ReadLine\r\n      <span lang=\"en-us\">   <\/span>i = i + 1\r\n   <span lang=\"en-us\">   <\/span>End While\r\n   <span lang=\"en-us\">   <\/span>fileJoins.Close()\r\n<span lang=\"en-us\">   <\/span>End Sub\r\n\r\n<span lang=\"en-us\">   <\/span>Public Sub MapAttributesForJoin(ByVal FlowRuleName As String, ByVal csentry As CSEntry, ByRef values As ValueCollection) Implements IMASynchronization.MapAttributesForJoin\r\n   <span lang=\"en-us\">   <\/span>Select FlowRuleName\r\n      <span lang=\"en-us\">   <\/span>Case \"Join_CSV\"\r\n  <span lang=\"en-us\">          <\/span>'If the csentry DN is found in the joins array, then\r\n  <span lang=\"en-us\">          <\/span>'use the paired employeeID to search the Metaverse.\r\n        <span lang=\"en-us\">    <\/span>For i = 0 To joins.Length - 1\r\n          <span lang=\"en-us\">     <\/span>If joins(i).Contains(csentry.DN.ToString) Then\r\n            <span lang=\"en-us\">   <\/span> <span lang=\"en-us\">  <\/span>values.Add(joins(i).Split(\";\")(1))\r\n          <span lang=\"en-us\">     <\/span>End If\r\n        <span lang=\"en-us\"> <\/span>Next\r\n\r\n   End Select\r\nEnd Sub<\/pre>\n<\/div>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Reporting<\/h2>\n<p>People will ask you questions like \u00e2\u20ac\u0153How many people have you joined in system X but not in Y?\u00e2\u20ac\u009d, \u00e2\u20ac\u0153How sure are you that the joins are correct?\u00e2\u20ac\u009d, \u00e2\u20ac\u0153Which department<br \/>\nhas the most unidentified accounts?\u00e2\u20ac\u009d It\u00e2\u20ac\u2122s best to be prepared for these sorts of questions.\u00c2\u00a0<\/p>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Querying the Metaverse<\/h3>\n<p>Once data is in the Metaverse it is a simple matter to access it for reporting, either by exporting it into a reporting table, or by directly querying the underlying<br \/>\ntables (as long as you\u00e2\u20ac\u2122re careful to do it when ILM is idle, or else use NOLOCK).\u00c2\u00a0<\/p>\n<p>So consider this: <strong>During the data cleaning phase, import all identifying attributes into the Metaverse from all sources<\/strong>.\u00c2\u00a0<\/p>\n<p>For example: You\u00e2\u20ac\u2122ve made a join between a user in AD and an HR record.<\/p>\n<p>Under normal operations you would consider HR as the master source for the name, and you would only flow it from there.<\/p>\n<p>You wouldn\u00e2\u20ac\u2122t bother importing the name attributes from AD \u00e2\u20ac\u201c in fact you\u00e2\u20ac\u2122re more likely to be overwriting them with export flow rules.<\/p>\n<table style=\"border-collapse: collapse;\" border=\"0\">\n<tbody>\n<tr>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">HR<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">Metaverse<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">AD<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">Lastname = Powells-Brown<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">-&gt;<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">lastname = Powells-Brown<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">-&gt;<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">sn = Powells-Brown<\/td>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">Firstname = Joanna<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">-&gt;<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">firstname = Joanna<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">-&gt;<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">givenName = Joanna<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>However, during the data matching phase, you\u00e2\u20ac\u2122re probably not ready to start overwriting attributes, and the information about current values can be very important in your<br \/>\nverification and reporting.<\/p>\n<table border=\"0\">\n<tbody>\n<tr>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">HR<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">Metaverse<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\">AD<\/th>\n<th style=\"background-color: #d4d0c8; text-align: center; font-weight: bold; font-size: 11px;\" width=\"5\">\u00c2\u00a0<\/th>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">Lastname = Powells-Brown<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">-&gt;<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">HR_lastname = Powells-Brown<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">-&gt;<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">sn = Powells-Brown<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<\/tr>\n<tr>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">Firstname = Joanna<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">-&gt;<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">HR_firstname = Joanna<br \/>\nAD_lastname = Powells<br \/>\nAD_firstname = Jo<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td style=\"text-align: center; font-size: 11px;\">&lt;-<br \/>\n&lt;-<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<td class=\"style1\" style=\"font-size: 11px;\">givenName = Joanna<br \/>\ngivenName = Jo<\/td>\n<td width=\"5\">\u00c2\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now, if you have a look at the <em>mms_metaverse<\/em> table in the ILM database, you will see how simple it is to query the progress of your joins, and also to judge<br \/>\non what criteria the joins were made.<\/p>\n<p>Some example queries\u00e2\u20ac\u00a6\u00c2\u00a0<\/p>\n<p>\/* HR person with no join to AD *\/<\/p>\n<p>select HR_lastname, HR_firstname, HR_employeeid from mms_metaverse<br \/>\nwhere AD_dn is null\u00c2\u00a0<\/p>\n<p>\u00c2\u00a0\/* HR person with join to AD *\/<\/p>\n<p>select HR_lastname, HR_firstname, HR_employeeid, AD_lastname,AD_firstname,AD_dn from mms_metaverse<br \/>\nwhere AD_dn is not null\u00c2\u00a0<\/p>\n<table style=\"width: 100%;\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<th style=\"text-align: left; background-color: #c0c0c0;\"><img decoding=\"async\" src=\"http:\/\/apfhrw.bay.livefilestore.com\/y1psOzY0M-yAjhcC7950DyjOEOMTO7g7UXPJsJ3MsIEAGPWZ7cLWtso19wWC_aIFviX49wqL5BR_hx5guCJhgwNCAqxNFi-2oyW\/Caution.gif\" alt=\"Caution\" \/><span class=\"style2\"><strong> Caution<\/strong><\/span><\/th>\n<\/tr>\n<tr>\n<td style=\"background-color: #f0f0f0;\">As mentioned above you need to be<br \/>\ncareful when directly querying the Metaverse tables.If your system is already in production, and you happen to be adding in<br \/>\na new data source, then you may be better off employing a SQL MA to export<br \/>\nthe data you\u00e2\u20ac\u2122re interested in to another table, where you can query it as<br \/>\nmuch as you like without risk of locking errors.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u00c2\u00a0\u00c2\u00a0<\/p>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Querying the Connector Space<\/h2>\n<p>Unfortunately it is not so simple to query the connector space to, for example, report on that state of your disconnected objects.<\/p>\n<p>It\u00e2\u20ac\u2122s a great pity that you can\u00e2\u20ac\u2122t just save results from the Joins page in the Identity Manager GUI, so your options are:\u00c2\u00a0<\/p>\n<p><strong>SQL query<\/strong> &#8211; The CS table holds data differently to the Metaverse tables.<\/p>\n<p>It is possible to query for disconnectors in this way, however you will only be able to retrieve the CN of objects \u00e2\u20ac\u201c which may not be sufficient to identify them.\u00c2\u00a0<\/p>\n<div style=\"background-color: #e6e6e6; width: 620px; overflow: scroll;\">\n<pre style=\"background-color: #e6e6e6; font-family: 'Courier New'; font-size: 11px;\" lang=\"x-js\">select cs.rdn from dbo.mms_connectorspace cs\r\njoin dbo.mms_management_agent ma\r\non cs.ma_id = ma.ma_id\r\nleft outer join dbo.mms_csmv_link mv\r\non mv.cs_object_id = cs.object_id\r\nwhere ma.ma_name = 'My MA'\r\nand mv.mv_object_id is null\r\nand cs.connector_state = 0<\/pre>\n<\/div>\n<p><strong>CSExport<\/strong> &#8211; The command-line utility csexport.exe, found in the &lt;ILM program&gt;\\bin folder, will allow you to dump connector space objects to an XML file.\u00c2\u00a0<\/p>\n<p><strong>Report directly from the data source<\/strong> &#8211; Once an object in the data source has been correctly identified you will ideally export a unique attribute out to it. It may then be possible to identify the non-joined objects as those which don\u00e2\u20ac\u2122t possess this attribute.\u00c2\u00a0<\/p>\n<p><strong>Project to a different object type<\/strong> &#8211; ILM processes joins before projections, so it is fairly simple to project all non-joined objects to a different Metaverse object type \u00e2\u20ac\u201c for example, one called \u00e2\u20ac\u0153disconnectors\u00e2\u20ac\u009d. This may help in reporting on an overall status direct from the Metaverse tables.<\/p>\n<p>Note however that to make these objects once again available for joins they will have to be disconnected from the MVExtension provisioning code.\u00c2\u00a0<\/p>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Advanced Tips and Tricks<\/h2>\n<p style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Join to Multi-value Attribute<\/p>\n<p>Sometimes you may need to join to a value in a multi-value attribute. An example is searching through all the <em>proxyAddresses<\/em> for a match against<br \/>\na single email address.\u00c2\u00a0<\/p>\n<p>When the multi-valued attribute exists in the connector space, and the single valued attribute is in the Metaverse, this is very easily accomplished. Just use an Advanced Join Rule to break the multi-valued attribute down into the values list used by the join rule.\u00c2\u00a0<\/p>\n<div style=\"background-color: #e6e6e6; width: 620px; overflow: scroll;\">\n<pre style=\"background-color: #e6e6e6; font-family: 'Courier New'; font-size: 11px;\" lang=\"x-js\"> Public Sub MapAttributesForJoin(ByVal FlowRuleName As String, ByVal csentry As CSEntry, ByRef values As ValueCollection) Implements IMASynchronization.MapAttributesForJoin\r\n<span lang=\"en-us\">   <\/span>Select FlowRuleName\r\n<span lang=\"en-us\">      <\/span>Case \"Join_proxyAddresses\"\r\n<span lang=\"en-us\">         <\/span>Dim alias As String\r\n<span lang=\"en-us\">         <\/span>For Each alias In csentry(\"givenName\").Values\r\n<span lang=\"en-us\">   <\/span> <span lang=\"en-us\">  <\/span> <span lang=\"en-us\">  <\/span> <span lang=\"en-us\">  <\/span>values.Add(alias)\r\n<span lang=\"en-us\">         <\/span>Next\r\n<span lang=\"en-us\">   <\/span>End Select\r\nEnd Sub<\/pre>\n<\/div>\n<p>However, when the multi-valued attribute has already been imported into the Metaverse you will have a problem. While you can join on a multi-valued attribute, you have to join on the whole thing. There is no way that you can match one value out of a multi-valued Metaverse attribute against a single-valued connector space attribute.\u00c2\u00a0<\/p>\n<p>Some possible options:\u00c2\u00a0<\/p>\n<ul>\n<li>Import the multi-valued attribute into a series of single-valued attributes in the Metaverse, eg., proxy1, proxy2, proxy3 \u00e2\u20ac\u00a6<\/li>\n<li>Use a different Metaverse object type to do the project and join the other way around.<\/li>\n<\/ul>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Multiple Possibilities in the Connector Space<\/h3>\n<p>All the joining techniques so far have been based around a single connector space object, with one or more possible matches in the Metaverse. But what do you do if you want to work the other way around, where there are multiple possible matches in the connector space for a single Metaverse object, and you want to pick the best one? Unfortunately this is not straight-forward. There is no way to offer a selection of connector space objects in a join rule, as joins always work on a single connector space object at a time. You can judge the merits of the current CS object, but you can\u00e2\u20ac\u2122t tell if there\u00e2\u20ac\u2122s a better one coming up. One way around this is to do everything with CSV files.\u00c2\u00a0<\/p>\n<ol>\n<li>Use the ResolveJoinSearch to write possible joins to a CSV, but don\u00e2\u20ac\u2122t actually join anything,<\/li>\n<li>Do your matching outside ILM, then<\/li>\n<li>Use a CSV with an Advanced Join Rule to make the joins.<\/li>\n<\/ol>\n<h3 style=\"color: #365f91; font-size: 13px; font-weight: bold;\">Too much data<\/h3>\n<p>If you have an enormous number of accounts, and different data sources to trawl through, you may be best off doing your data matching outside of ILM, and then just using the CSV join rule above to make the joins.\u00c2\u00a0 Check out the Fuzzy Lookup Transformation from the Enterprise version of SQL SSIS for help here.\u00c2\u00a0<\/p>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Take-Home Thoughts<\/h2>\n<ul>\n<li>Complex join rules are a means to an end \u00e2\u20ac\u201c not the end itself.<\/li>\n<li>Breadcrumbing is essential for automation.<\/li>\n<li>When matching on weak rules (eg., surname only) then verify the match another way.<\/li>\n<li>You can\u00e2\u20ac\u2122t do it all in ILM. CSV, Excel and fuzzy lookup algorithms will also help, but an element of by-hand matching is inevitable.<\/li>\n<li>Get the matching and breadcrumbing sorted out before you start flowing and provisioning.This will make for a happier project, stake holders, users and YOU!<\/li>\n<\/ul>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">Extra Reading<\/h2>\n<ul>\n<li><a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/cc720664(WS.10).aspx\">Design Concepts for Correlating Digital Identities<\/a><\/li>\n<li><a href=\"http:\/\/download.microsoft.com\/download\/1\/3\/7\/137d2f75-f95c-4aea-b553-a311203058cc\/CorrectingJoins.doc\">Correcting incorrect joins<\/a><\/li>\n<\/ul>\n<h2 style=\"color: #365f91; font-size: 14px; font-weight: bold;\">About the Author<\/h2>\n<p>Carol Wapshere is an ILM MVP and contributor of a few of these documents now. She has always believed that putting the work in up-front will save you lots of headaches in the long run. It\u00e2\u20ac\u2122s a self-defense strategy really \u00e2\u20ac\u201c there\u00e2\u20ac\u2122s nothing worse than having to go over and over (and over) the same ground again and again, especially when you\u00e2\u20ac\u2122d already moved on to something new and far more interesting. Keep it neat, and keep it simple, and things should work just fine.\u00c2\u00a0<\/p>\n<p>Thanks to Markus Vilcinskas and Paul Loonen for their help with this document.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve just posted a new Greatest Hits article on the ILM forum on the subject of how ILM\u00c2\u00a0(or the FIM Sync Service) can be used to clean up the mess of existing accounts, before you can actually get \u00c2\u00a0on to the more interesting tasks of provisioning and updating. With the way FIM codeless sync works,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[42,34,28],"tags":[],"class_list":["post-827","post","type-post","status-publish","format-standard","hentry","category-fim-2010","category-ilm2007","category-miis2003"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pkp1o-dl","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/827","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/comments?post=827"}],"version-history":[{"count":9,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/827\/revisions"}],"predecessor-version":[{"id":866,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/827\/revisions\/866"}],"wp:attachment":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/media?parent=827"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/categories?post=827"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/tags?post=827"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}