-
I’ve been meaning to post some of Richard’s excellent desktop wallpapers for Forefront Identity Manager 2010.
Direct Link: http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=imexsamples&DownloadId=6543
-
I am catching up on my blog roll, and I would like to shout out to a relatively new blog in the IDA space: The IDA Guys.
+1 on "Never say it will only take Five Minutes" -- the only task that takes 5 minutes is the one you don't do ...
-
Leading announcement: I appreciate the feedback I’ve gotten from some of you about the API of the new client. One piece of key feedback I want very much to incorporate is how we can unify accessing single-valued and multi-valued attributes. Please send any additional comments my way.
For this week’s post we see the benefits of promoted properties and transactions. The full source code is available on MSDN code gallery.
Example 1: Create a Named Group
In this example we create a new object and pass it to the client to be created on the server.
public Guid CreateGroup()
{
RmGroup newGroup = new RmGroup();
newGroup.DisplayName = "My New Group";
// Demonstrate accessing a non-promoted attribute
newGroup[RmGroup.AttributeNames.MembershipAddWorkflow].Value = "OwnerApproval";
// Any user object's ObjectId
newGroup.Owner = this.OwnerObjectId;
return this.client.Create(newGroup);
}
Now let’s add some users to the group.
Example 2: Add members to Group
In this example we demonstrate using a transaction to track our changes to the group object. To add a member to a group simply add another reference to the ExplicitMember attribute. The public client exposes the ExplicitMember as List<Guid>, and in this example we add another Guid. Once all of the changes are complete, we accept the changes and send the transaction to the client. The client transforms the changes into da:ModifyRequest/da:Change elements and sends them over the wire.
public void AddUsers(Guid groupId)
{
RmGroup group = this.client.Get(groupId) as RmGroup;
RmResourceTransaction transaction = new RmResourceTransaction(group);
transaction.BeginChanges();
int numberAdded = 0;
foreach(RmResource resource in this.client.Enumerate("/Person[Department='Sales']"))
{
RmPerson person = resource as RmPerson;
if(person != null)
{
numberAdded++;
group.ExplicitMember.Add(person.ObjectID);
}
}
Assert.IsTrue(numberAdded >0);
transaction.AcceptChanges();
Assert.IsTrue(this.client.Put(transaction));
}
We successfully created a group and added users to it programmatically.
-
Preparing for TechEd has eaten up cycles to proof today’s post. I should have the cycles on the plane this weekend.
-
I got a very good question today from a TAP customer last week which I hope is generally applicable. The customer asked “What can I do with the web service interface?” Fantastic question! With the web service interface, you can….
- Do everything the portal does. (The portal uses the web service interface for all requests.)
- Create and delete groups.
- Add and remove members from groups.
- Create and delete users.
- Configure synchronization rules to provision users and groups to connected systems like AD.
- Configure management agents and the metaverse using ma-data and mv-data objects.
- Create calculated groups and read their resulting calculated membership.
- Create workflow definitions and configure policy to fire these workflows upon changes in the system.
- Search for objects with complex queries.
- Save searches for objects by using Sets and configure the system to take action when values in the set change.
If you are considering batch creation of groups or objects or are looking for a good integration story with another product, I invite you to consider using the web service interface. For more information, I recommend reading my previous post on this topic.
-
Members from the product team will attend the TechEd 2009 conference next week in Los Angeles. Fred, Alym, Joel, and myself will attend the conference to speak about FIM and staff the booth. I invite you to attend our sessions:
- Alym and Joel will present using FIM to reduce the cost of the help desk for MSIT (SIA-307).
- Fred and I will speak about using FIM to reduce the cost of provisioning and credential management (SIA-308).
- Brian Komar will talk about integrating FIM workflows with certificate management (SIA-310).
If you are in the area, please stop by our booth on the expo floor or check out one of our sessions.
-
In this week’s post we look at the three most common queries to search for existing groups: getting all groups, “my groups”, and groups that match a particular filter. While these examples show off the current prototype client, the filters and principles may be used on any client. The test case source code may be downloaded from MSDN code gallery.
Example 1: All Groups
To search for all groups, we pass a simple filter “/group” to the Enumerate method. The Enumerate method returns a .NET enumerator, which C# can use in its foreach construct. Note that the enumerator returns the parent class RmResource and that we down-cast the returned object to an RmGroup. Some queries might return a mixed bag of object types so it is necessary to check the status of the cast in production.
Behind the scenes the enumerator is making the enumerate and pull requests. This iterator does not expose the count of the enumeration since we would prefer to move away from clients relying upon it. Therefore, if you only want a fixed number of objects, either include a break in the foreach or make the pull request with MaxElements set to the value you desire.
[TestMethod]
public void Example1AllGroups()
{
// In this example we enumerate all groups in the system
// and show how to get some common attributes
foreach (RmResource resource in client.Enumerate("/Group"))
{
RmGroup group = resource as RmGroup;
Assert.IsNotNull(group);
Console.WriteLine(String.Format(consoleFormat,
group.DisplayName,
group.ObjectID,
group.DisplayedOwner,
group.ExplicitMember.Count,
(group.Type == RmGroupType.Distribution)
)
);
}
}
Example 2: My Groups
To search for “my groups”, we search for groups whose owner are equal to the user’s ObjectID. In this example we already know the user’s ObjectID (perhaps from a previous query for all users whose ObjectSid equals some value). We also limit results to distribution lists by including the group type. Note that this client contains an enum for group type for easy checks and searches.
[TestMethod()]
public void Example2GetMyDls()
{
// In this example we enumerate all of "my" dls
String filter = String.Format("/Group[{0}={1} and {2}='{3}']",
RmGroup.AttributeNames.Owner.Name,
"6FB8413E-49DA-48d9-A204-FFB8F0EEE133",
RmGroup.AttributeNames.Type.Name,
RmGroupType.Distribution
);
foreach (RmResource resource in client.Enumerate(filter))
{
RmGroup group = resource as RmGroup;
Assert.IsNotNull(group);
Assert.IsTrue(group.Type == RmGroupType.Distribution);
Console.WriteLine(String.Format(consoleFormat,
group.DisplayName,
group.ObjectID,
group.DisplayedOwner,
group.ExplicitMember.Count,
(group.Type == RmGroupType.Distribution)
)
);
}
}
Example 3: Match DisplayName
To search for a group which has a DisplayName or MailNickname like a predicate, we need to use the contains and starts-with functions in FIM’s XPath dialect. Nima posted great examples on his blog with a more complete description of FIM’s dialect features. Last, note the static structures we’re using to refer to attribute names. The static definitions of attributes specific to groups, like Owner and MailNickname, are stored in the RmGroup class. The static definitions of attributes common to all objects types like DisplayName are stored in the RmResource class. The values of these attributes at runtime, however, are present on the RmGroup object.
[TestMethod()]
public void Example3SearchGroups()
{
// In this example we search for a group with displayname or alias that contain the search
String filter = String.Format("/Group[contains({0}, '{1}') or starts-with({2}, '{1}')]",
RmResource.AttributeNames.DisplayName.Name,
"Sales",
RmGroup.AttributeNames.MailNickname.Name
);
foreach (RmResource resource in client.Enumerate(filter))
{
RmGroup group = resource as RmGroup;
Assert.IsNotNull(group);
Console.WriteLine(String.Format(consoleFormat,
group.DisplayName,
group.ObjectID,
group.DisplayedOwner,
group.ExplicitMember.Count,
(group.Type == RmGroupType.Distribution)
)
);
}
}
-
In this post we’ll discuss the new Prototype Public Resource Management client. I uploaded the MSDN-style documentation to MSDN Code Gallery. The purpose of this post is to walk you through the direction I’m thinking about for a supported API. These ideas are a snapshot of my thinking today and will change in the future. I welcome feedback about both the high-level approach and specific granular details. There may be scenarios or features overlooked which should be incorporated into an eventual supported client.
Changes from previous prototype
Changes since the last version of the public prototype:
- This client is called “rmclient” to clearly differentiate it from the previous one.
- The source code is not yet available.
- Since the previous source code was meant only as documentation and never would become production code, there were few restrictions on publishing it.
- Elements of this prototype may become production code. As a result, we are not yet sure what pieces we may release at this time.
- We expect to have either a compiled binary, source code, or supported alternative ready for the RC1 release.
- The client and object model are decoupled.
- The client is layered on top of WCF.
- The client uses MEX for metadata.
- The original client was never intended to be copied into complex environments. As a result, many of you discovered the silent requirement to have the xsd file present for the previous public client to work.
- We have a factory method that instantiates strongly-typed objects. It is now possible to use casting to check whether an object is a Person or Group.
- There is a “DefaultClient” which provides basic functionality that I’ve seen in POCs. This DefaultClient isn’t the only client, however, unlike the previous prototype.
- There is a pattern of “Promoted Properties” which enable developers to use Intellisense for discovering FIM object schema.
Architecture
The basic architecture of the client has 3 layers: Default Client, WS-* clients, and WCF ClientBase. All three layers are publically accessible with the intention that your code can call the layer which is most appropriate. For most customers, the DefaultClient will be sufficient since it enables basic CRUD and Enumeration operations at a high level. The diagram below summarizes these three layers, with DefaultClient depending on the WS-* clients underneath it, and those clients depending on WCF ClientBase.

All of these classes are in the Microsoft.ResourceManagement.Client namespace. The DefaultClient is expected to be the client you use for 80% of scenarios. We intend it to be a low barrier to entry and sufficiently abstracted from the actual SOAP messages. Customers who need to modify the specific SOAP messages will need to use one of the WS-* clients. Customers who need to modify the WCF channel will have to use their own WCF ClientBase. Those three options should encompass 99% of all scenarios our customers want:
- DefaultClient for easy, straight-forward use
- WS-* clients for power users needing advanced control
- WCF ClientBase for extreme special cases
ObjectModel
The object model is completely decoupled from the client into the Microsoft.ResourceManagement.ObjectModel namespace. There are two key points I would like to make.
First, there is a base class called RmResource which is a Dictionary. You can always use a key to index any attribute out of RmResource using the Dictionary interface. RmResource also exposes public properties for some well-known attributes like ObjectId and ObjectType so that developers can use Intellisense and pass those properties as arguments to methods.
We created derived classes RmPerson and RmGroup. These classes get the common public properties like ObjectId from the parent class, but they also expose additional attributes specific for their types. For example, RmPerson exposes FirstName and EmployeeId. RmGroup exposes ComputedMember and ExplicitMember. We intend to have a class for each object type in FIM and that each object type has all of the necessary promoted properties.
Since all of these objects are public, customers can create their own derived classes for custom object types in FIM. For example, Contoso may create a Contact object with an Email address. Contoso simply needs to create a new class that derives from RmResource and create a public property for Email. The class diagram below summarizes the relationships and intended inheritance:

Second, RmResource just stores data. To commit changes back to the web service, we’re musing with the idea of requiring transactions. The reason is our web service is designed to accept “differences” to objects rather than entire representations. We are considering calculating these differences with a transaction object rather than inside the RmResource object itself. At first glance it feels more clean to separate data and operations about the data. We’ll show how these transactions work in the upcoming examples, and we hope that you agree they are a natural way to program against the FIM web service.
I hope this was a useful introduction for our direction of the new prototype. Please contact me with feedback about this direction.
-
As promised, this week I introduce the new prototype of the Public Resource Management Client and the remaining schedule. The theme of this quarter is “Yet Another FIM Portal”. The FIM Portal is a great experience for both Administrators and Information Workers, but I often hear how customers want pieces of FIM functionality integrated into their existing portals. We will explore how to accomplish some key scenarios using this new client in the context of creating a simple portal. The intention is that when RC1 is released there will be sufficient examples to demonstrate meaningful POCs to customers.
- April 23: Prototype V2: Overview of the new API
- April 30: Search for Existing Groups
- May 7: Create a Named Group and add Person objects as members
- May 14: Batch create 100 Person objects and then batch create 1 set per unique department
- May 21: Retrieve pending approvals and approve or reject them
- May 28: Sync config: Fundamentals of ma-data and mv-data
- June 4: Sync config: Updating the SQL MA
- June 11: Wrapping rmclient with SQL Reporting Services?
Note that custom workflows are omitted from this agenda because the content is very well covered by Joe Zamora’s blog CShark. Also note that June 11 will be the last blog post of the quarter.
-
I recently was writing help documentation for PowerShell cmdlets and came across a very helpful blog post. In the future I hope the PowerShell team comes out with a more rich editing experience, but editing XML wasn’t that bad.
-
Ahmad pointed me to Peter Geelen’s comprehensive list of resources for ILM “2” RC0. It’s worth bookmarking. I suspect Peter will keep this list up to date as we proceed into FIM RC1:
http://identityunderground.spaces.live.com/blog/cns!69F957860EA2CE40!846.entry?sa=631120844
-
As you may have seen in other announcements or in various unintentional hints the product previously known as Identity Lifecycle Manager (ILM) “2” is now Forefront Identity Manager (FIM) 2010. The re-branding comes as part of an overarching strategy which is better explained in the official website.
What does this mean? For one, I now can share screenshots of the product publically. Exhibit A:
Second, we can proceed with our discussions about extensibility based on current builds. While these current builds will not be publically available until Release Candidate 1 (RC1), I hope to provide sufficient content so that you can try out extensibility on day one of the public release. There is nothing worse than getting a fantastic product which you don’t know how to use!
Next week we will introduce the new prototype client API and present the full agenda for the remainder of this quarter. If you are internal to Microsoft, you may take a peek today at codebox (look for the rmclient). The intention of getting this next prototype out the door is to gain wisdom and feedback about what a good API looks like for FIM. Long-term we should* build a supported client. No one in the product team argues the “if” question -- for us it’s more a matter of “when.” It is is my hope that that we can use the prototype to deliver a supported client sooner than later.
As an aside, you may see references to FIM 2009. There is no shipping version of FIM 2009. Back in December when we planned to ship this fiscal year, we re-branded the product to FIM 2009. With the slip into the next fiscal year we now will ship as FIM 2010. We have not begun transitioning to FIM 2010, so many recent screenshots and splash screens most likely will show 2009. The re-branding last December was the key reason we could not show publically recent builds or screenshots since January.
*4/23/2009: Slight correction -- I softened the language form will to should since the supported client is not part of the official plan of record.
-
Members from the product team will attend the RSA Conference 2009 next week in San Francisco. Mark Wahl, one of our team’s architects, will present the session “Building Authorization Into The Enterprise Identity Metasystem” (ESS-108). Alex, Nima, and Jeff will attend.
If you are in the area, please stop by our booth on the expo floor. We will have a demo of a build from two weeks ago. This will be the first public viewing of the product since the anticipated marketing announcement tomorrow.
-
I apologize for not publishing the Q2 agenda last week. I am blocked on an upcoming marketing announcement, which is scheduled for some time next week. The estimate I have is April 16th. When the announcement goes out, we will have an agenda.
The Identity Management Team is working hard on one priority:
- Release Candidate 1
RC1 will differ from RC0 by having improved performance and wisdom from a successful MSIT deployment. Part of the improved performance is coming from database schema changes and part from minor web service protocol changes. When the dust settles, we will have the full list of web service changes here. It should be small things like making count in Enumeration optional or returning localized schema from the Metadata Exchange endpoint.
-
Mark Wahl passed on to me a reflection from a TEC 2009 attendee:
http://eternallyoptimistic.com/2009/03/26/tec-and-the-targeted-conference-value-proposition/