Simple #MIM2016 Reporting in PowerShell

As a veteran MIM implementer now, I can’t help being constantly impressed with how much Ryan Newington‘s work on his Lithnet Github site has changed the landscape for those of us continually faced with the task of extracting data from the various product databases.  The landing page doesn’t show all of the repositories either …

I simply want to give those of you who may not have used these yourself a simple example of how to do something you’ve always done in the past another way – e.g. CTRL+C from the Sync console’s Metaverse search results, or (heaven forbid) a query on one of the SQL databases.

Oh – and if you’re still on FIM2010 R1, then here’s another great reason to upgrade to MIM 2016!

Sample MV query to CSV

In the following example which uses the Lithnet PowerShell Module for FIM/MIM Synchronization Service, I am trying to mimic 2 variants on a Metaverse search in PowerShell for exceptions in across all active staff identities – in my case where the desired UPN suffix (Exchange multi mail/upn suffix scenario) did not match that present in AD, either because

  • there was no authoritative value at all (excluding a set of known exceptions), or
  • there was a value but it was incorrect.

The report is designed to be run BEFORE turning on a persistent EAF on UPN to allow MIM to master this property (up until then it was initial flow only).

The key observation to make should be how closely the script mirrors the process of setting up a corresponding MV query in the console.  I am using 2 separate reports only because this is exactly the way it has to be done in the console – obviously you could combine these if you wanted.

Import-Module LithnetMiisAutomation

$filePath = "D:\Logs\MismatchingUpnSuffixes.csv"
$missingOnly = $false

$mvQueries = @(
 New-MVQuery -Attribute PersonType -Operator Equals -Value "Staff"
 New-MVQuery -Attribute employeeStatus -Operator Equals -Value "active"
)
if ($missingOnly) {
 $filePath = "D:\Logs\MissingUpnSuffixes.csv"
 $mvQueries += New-MVQuery -Attribute UpnSuffix -Operator IsNotPresent
 $mvQueries += New-MVQuery -Attribute LegacyEmployeeID -Operator NotContains "B"
} else {
 $mvQueries += New-MVQuery -Attribute UpnSuffix -Operator IsPresent
}

$mvQueryResult = Get-MVObject -ObjectType Person -Queries $mvQueries
if ($missingOnly) {
 $exceptions = $mvQueryResult
} else {
 $exceptions = $mvQueryResult | Where-Object {$_.Attributes.uid.Values.ValueString -notlike "*$($_.Attributes.csoUpnSuffix.Values.ValueString)"}
}
$users = @{}
foreach ($exception in $exceptions) {
 $obj = [PSCustomObject]@{
 accountName = $exception.Attributes.accountName.Values.ValueString
 displayName = $exception.Attributes.displayName.Values.ValueString
 uid = $exception.Attributes.uid.Values.ValueString
 UpnSuffix = $exception.Attributes.UpnSuffix.Values.ValueString
 employeeID = $exception.Attributes.employeeID.Values.ValueString
 LegacyEmployeeID = $exception.Attributes.LegacyEmployeeID.Values.ValueString
 }
 $users.Add($exception.ID,$obj)
}

if (Test-Path -path $filePath) 
{ 
 Remove-Item -Force $filePath | Out-Null
}

foreach ($userKey in $users.Keys) {
 $users.$userKey | Select-Object * | Export-Csv $filePath –NoTypeInformation -Append
}

Sample MIM Service query to update a static set

The second query is against the MIM Service using the Lithnet FIM/MIM Service PowerShell Module, but instead of outputting a CSV, I am updating the membership of a static set to trigger an MPR to reprocess some entitlements (custom MIM resource linking a person to a role for a date range).  This was to allow me to fix some data retrospectively after having changed the policy – in this case updating the entitlement description with the new display name of a user after a name change.

A key observation here is that only the attributes I need are being returned from tens of thousands of query results – optimising all sorts of things including memory consumption and processing time (doing the same thing using the traditional “add-pssnapin FIMAutomation” approach had to be canned after taking many hours to almost exhaust system resources).

# Import the module
cls
Import-Module LithnetRMA;
Get-Date

# Connect to the FIM service instance
Set-ResourceManagementClient -BaseAddress http://localhost:5725;

# Retrieve the set we need to update
$set = Search-Resources -XPath "/Set[DisplayName = 'All entitlements to fix']" -ExpectedObjectType Set #-AttributesToGet @("ComputedMember", "ExplicitMember")

# Initialise our set Explicit Membership (without committing it)
$set.ExplicitMember.Clear()

# Get current entitlements (only DisplayName, Description and UserID)
[string]$from_date = get-date -Format "yyyy-MM-ddTHH:mm:ss"
$entitlements = Search-Resources -XPath "/Entitlement[(UserID=/Person)]" -AttributesToGet @("DisplayName","Description","UserID")
Write-Host "Total matched entitlements: [$($entitlements.Count)]"

# Initialise our hashtable of Users to be queried
$users = @{}
foreach($entitlement in $entitlements) {
 # Add user to users hashtable if we don't already have it
 $key = $entitlement.UserID.Value
 if (!$users.ContainsKey($key)) {
 $user = Get-Resource -ObjectType Person -AttributeName "ObjectID" -AttributeValue $key -AttributesToGet @("DisplayName","AccountName")
 if ($user.DisplayName -and $user.AccountName) {
 $users.Add($key,@{})
 $users."$key".Add("DisplayName",$user.DisplayName)
 $users."$key".Add("AccountName",$user.AccountName)
 } else {
 Write-Host "Error: User cannot be determined for entitlement [$($entitlement.DisplayName)] description [$($entitlement.Description)]!"
 }
 }
 $thisUser = $users."$key"
 if ($entitlement.Description -notlike "*$($thisUser.DisplayName)*") {
 Write-Host "Entitlement [$($entitlement.DisplayName)] description [$($entitlement.Description)] does not contain user displayname [$($thisUser.DisplayName)]"
 $set.ExplicitMember.Add($entitlement.ObjectID) | Out-Null
 }
}
Write-Host "Total user entitlements to fix: [$($set.ExplicitMember.Count)]"

# Update the set
Save-Resource $set
Get-Date

So kudos, Ryan!  Just 2 examples of things that can either be done better, quicker, and actually work where they couldn’t easily before (not without going to the SQL layer as a last resort)!

Advertisement

About bobbradley1967

Microsoft Identity and Access Professional with 2 decades of successful IAM implementations in APAC, specialising in MIM and its predecessors (FIM/ILM/MIIS) and now with SoftwareIDM. A Microsoft IAM MVP prior to that with a background in MS.Net applications development/SI. Now with a particular interest how Identity and HyperSync Panel provide the Identity and Access orchestration presently missing in the Azure Entra Suite to effectively enforce Zero Trust on the M365 platform.
This entry was posted in FIM (ForeFront Identity Manager) 2010, MIM (Microsoft Identity Manager) 2016, Uncategorized and tagged , , . Bookmark the permalink.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.