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)!