Quick Tip: Get local time for next AzureAD Sync

When you run the command

You get an output similar to

AllowedSyncCycleInterval : 00:30:00
CurrentlyEffectiveSyncCycleInterval : 01:00:00
CustomizedSyncCycleInterval : 01:00:00
NextSyncCyclePolicyType : Delta
NextSyncCycleStartTimeInUTC : 18/05/2017 10:55:42 PM
PurgeRunHistoryInterval : 7.00:00:00
SyncCycleEnabled : True
MaintenanceEnabled : True
StagingModeEnabled : False
SchedulerSuspended : False
SyncCycleInProgress : False

You will notice that the time is in UTC..

Quick one-liner can show you local time

Output is more sane

Friday, 19 May 2017 8:55:42 AM

FIM/MIM Powershell: XPath Demystified

I have been promising to get this post out there.. So here it is..

If you make extensive use of Lithnet ResourceManagement Powershell for MIM/FIM (You should if you don’t) you will probably be using the cmdlets “Search-Resources” or “Search-ResourcesPaged” which require an “-XPATH” input.

Now my xpath is real bad in for a complicated search.

Thankfully Ryan (Lithnet God) has written a few tools to make it easier for us to write xpath queries. We would use his cmdlets like New-XPathQuery , New-XPathQueryGroup and New-XPathExpression

Let’s start with simple query: Find everyone whose AccountName (string) starts with “P”

Output

/Person[(starts-with(AccountName, ‘P’))]

Easy yeah?

Lets do a boolean: Find every user for whom accountDisabled (Boolean) is present

Output

/Person[((accountDisabled = true) or (accountDisabled = false))]

 

Lets do a complex one: Find all users who Email Starts with “P” and are not disabled

Output

/Person[((starts-with(Email, ‘P’)) and (accountDisabled = False))]

 

You can see how you can build on this..

One final one: Find all accounts which have an email address starting with P and are not disabled or which have an AccountName and have Email containing “p@”

Output

/Person[(((starts-with(Email, ‘P’)) and (accountDisabled = False)) or ((starts-with(AccountName, ‘%’)) and (contains(Email, ‘p@’))))]

 

Above searches might not make sense logically in real world scenarios but what I am trying to show here is that how easy it is to build complex XPath search strings without knowing the XPath language and doing it pretty easily on Powershell.. (And don’t get me started how many times I have messed up the brackets 😛 )

Enormous potential and implantation capabilities if you come to think of it..

Migrating Lost Identities from AD to MIM

Setting up a new identity system from scratch is so easy.. but we know that kinda never happens… we are always migrating from an old system to new or changing an existing system due to business changes.

We went through the same process whereby we went from an OpenLDAP (ODSEE) being our primary source to FIM/MIM pushing out to AD. We were syncing identities manually from ODSEE to AD via Perl scripts in the old days..

So after the original migration done we have a lot of identities in AD not joined to MIM i.e. Disconnectors as we know them. This was because as per our policy we always added / modified from ODSEE -> AD but never a delete. Thus it left a lot of users and groups in AD which never came across in the initial migration.

So once the migration was complete, the big mammoth task came to migrate the unknowns from AD -> MIM and then join the identities in AD.

Using few wonderful tools suite called Lithnet written by @RyanLNewington

NOTE: Always remember, my script are as-is and have lots of rules and logics specific to my environment and business needs. Its there to give a basic understanding on what can be done. You will obviously have to modify it for your environment and needs.

REQUIREMENTS

  • Migrate AD Disconnectors to MIM Service
  • Join them to AD
  • Make sure no existing values are overwritten in AD like sAMAccountName, DN, DisplayName etc
  • Make sure membership (member) is not lost. memberOf is not cared for atm as that will take care of itself (Point 4 below).
  • Sync Engine SHOULD NOT be provisioning. Stop the syncs (not service itself)

LOGIC

I wrote two different scripts – one to migrate users and one to migrate groups. There are some things to note here

  1. We have our own business rules engine called ACMA to which we provision all our identities which then generates all the required attribute values according to our business rules. So you will see each object being written to both FIMService and ACMA DB (SQL). Do have a look at it.. Its powerful yet so simple… Must have…
  2.  We have a unique ID which is pushed to all system called monashObjectID which is basically a random guid which is generated in ACMA DB. We use this to have a constant ID between all system and thus later helpful to join ID etc.
  3. Due to the above point, my script assumes that all objects in AD , if they have monashObjectID means they are connected to FIM/MIM. Thus, on reverse, if they don’t have it means they have to be migrated.
  4. Simplification of OUs: Due to our previous messy environment we have over 100-200 OU’s in AD where all these objects exist. In the new age, we have simplified it and MIM only manages 8-10 OU’s. Therefore according to the “Type” of the object being migrated they are moved to the respective new OU in AD prior to migration (you need rights to do so in AD if you migration account doesn’t so already). Again you can comment out the whole function if its not required.
  5. Groups gets tricky. As they can be member of another group and if they group doesn’t already exist in MIM, it will loose membership once imported, I decided to get around it by scripting a check that if the group has members and if all of those members have monashObjectID then dump them in a CSV file. Thus this migration is run multiple times.
    1. Create 1st batch of CSV for those groups who don’t have members or which all members exist in MIM (by checking their monashObjectID as that comes from MIM)
    2. Migrate them
    3. Export to AD – this will write out monashObjectID for the newly migrated groups
    4. Repeat step a-c till all are done.

USER MIGRATION SCRIPT

Will start will user migrations as they can be members of a group. Again remember, loads of logic for our environment.

CSV Requirements

The CSV requires the following fields

  • Name: sAMAccountName of the user to be migrated
  • Type: We have accounttypes in our environment like person / service / custom etc. Need to mention that (or modify code to remove it)
  • OrgUnit: We need to mention an OrgUnit DisplayName for each user to which it belongs to (our environment specific again)
  • Owners: This is to mention the owner / supervisor of the user. They are sAMAccountName of already existing users in MIM. They can be multiple with ; separating them.
Script

Rundown
  • Script records everything via transcript command so you can set and forget, come back later and see what broke or potentially could break (loss of membership my main concern). Saves it to D:\ADUserMigration-{CSVFileName}.log
  • Asks for CSV file on run.
  • If the  “Type” is missing in CSV it will skip that user (MUST in our logic)
  • Moves the account to respective OU in AD as per the account type.
  • Checks if the user doesn’t have monashObjectID – thus good to migrate.
  • Creates the object in FIMService using LithnetRMA
    • Depending on the type either we set expiry date to never (9999-12-31) or two years from date of migration
    • Checks the owner if its a person or a group and then looks it up if it exits in MIM and adds it as owner.
    • If no owner then we set a default account we have as owner
    • Checks OrgUnit and if found it adds it to the object else sets a default OrgUnit.
  • Checks if the memberOf exists in MIM and adds the user to those groups in MIM.
  • Creates the object in ACMA
  • Repeats the steps above for all users in the CSV
  • Sync the identities in MIM via MIIS Powershell
    • Disables MRE Provisioning
    • DI the FIMService
    • DS / Commit just the ones which were migrated into MV (as its prod others could come in from Portal which needs MRE rules and thus excluding them)
    • DI ACMA MA
    • DS / Commit just the ones which were migrated into MV – this should join them due to our join rules looking for same fimserviceobjectID which was written to ACMA DB during create
    • DI AD MA – all moved objects will now show in correct OU in CS
    • DS / Commit just the ones which were migrated into MV – this will join due to our join rules looking for same sAMAccountName.
    • Enable provisioning
    • DS all of them again to make sure MRE rules apply to those objects for any provisioning if required.

Done..

Speed

Can’t remember to be honest but was quite quick (had nearly none to do for accounts)

GROUP MIGRATION SCRIPT

Remember, loads of logic for our environment.

CSV Requirements

The CSV requires the following fields

  • Name: sAMAccountName of the group to be migrated
  • Type: We have account types in our environment like UserGroup / ServiceManagedGroup / IdMAuthZGroup etc. Need to mention that (or modify code to remove it)
  • ServiceID: For our environment – mandatory.
  • Owners: This is to mention the owner / supervisor of the user. They are sAMAccountName of already existing users in MIM. They can be multiple with ; separating them.
  • OrgUnit: We need to mention an OrgUnit DisplayName for each user to which it belongs to (our environment specific again.

Script

Rundown
  • Script records everything via transcript command so you can set and forget, come back later and see what broke or potentially could break (loss of membership my main concern). Saves it to D:\ADGroupMigration-{CSVFileName}.log
  • Asks for CSV file on run.
  • If the  “serviceID” is missing in CSV for a ServiceManagedGroup it will skip that user (MUST in our logic)
  • Moves the group to respective OU in AD as per the account type.
  • Checks if the group doesn’t have monashObjectID – thus good to migrate.
  • Creates the object in FIMService using LithnetRMA
    • Depending on the type either we set expiry date to never (9999-12-31) or two years from date of migration
    • Sets all as Security and Global groups.
    • Checks the owner if its a person or a group and then looks it up if it exits in MIM and adds it as owner.
    • If no owner then we set a default account we have as owner
    • Checks OrgUnit and if found it adds it to the object else sets a default OrgUnit.
    • Checks each member of the group and if they exist then adds them – else will spit out a warning that they will loose membership in AD (good to have logs eh?)
  • Checks if the memberOf exists in MIM and adds the user to those groups in MIM.
  • Creates the object in ACMA
  • Repeats the steps above for all users in the CSV
  • Sync the identities in MIM via MIIS Powershell
    • Disables MRE Provisioning
    • DI the FIMService
    • DS / Commit just the ones which were migrated into MV (as its prod others could come in from Portal which needs MRE rules and thus excluding them)
    • DI ACMA MA
    • DS / Commit just the ones which were migrated into MV – this should join them due to our join rules looking for same fimserviceobjectID which was written to ACMA DB during create
    • DI AD MA – all moved objects will now show in correct OU in CS
    • DS / Commit just the ones which were migrated into MV – this will join due to our join rules looking for same sAMAccountName.
    • Enable provisioning
    • DS all of them again to make sure MRE rules apply to those objects for any provisioning if required.

Done..

Speed

This has varied for me. It really depends on number of members for the group.. about 3-5 groups / min .. I did about 1500 groups with 90% of them having 0-10 members but the last few having upto 1100 members in 6.5 hrs.

 

Hopefully this will help someone and see what can be done by  combination of various tools out there.

FIMService: Convert Static Group to Dynamic

Following from my previous post on how to create a group with both static members and dynamic filter, I came across a scenario where post migration we have some customers who initially had a group with explicit members but we thought they could be converted to a filter based group which matches the business case.

The script below helps you on doing the same. Again using LithnetRMA for the process.

Warning: Again, many custom attributes logic in FIMService for my environment but again you will get the whole idea. I have left the script as-is and not dumbed it down.

Please read my Groups with Static and Dynamic Members in FIMService post to understand the logic I have been applying for Autogen groups and how group and set tie up to each other in FIMService.

LOGIC

  • Asks for existing group name (DisplayName) in FIMService.
  • If found, outputs the number of explicit members found
  • Asks if you want to put a filter on the group and then asks for the XPATH filter (coming soon on how to create XPATH easily)
  • Checks if the filter is valid. If so (the fun part)
    • Outputs the count of users in the new filter
    • Compares the filter users to explicit members and gives a count of
      • New members to be added due to filter (i.e. not found in the explicit members list)
      • Count of common members found between existing explicit members and XPATH
      • Finally gives count of explicit members for the new set to be created (i.e. not found in the new filter).
  • Finally if the user wants to continue after above information
    • Creates a set with “Autogen-GroupName” and set the filter and explicit members to that
    • Modify’s existing group and deletes the current explicit members
    • Converts the existing group to a filter based and sets the objectID of the set created above.
  • Done

Hope it helps.. Please like, share or leave a comment below..

Groups with Static and Dynamic Members in FIMService

We had a business case whereby a filter couldn’t really create groups which catered for the purpose. We also needed to put in some static members (Explicit Members) in it.

Now by default a /Group in FIMService can either be Explicit or a Filter type group. But a Set allows you to have an explicit as well as filter on it.

WARNING: Many custom attributes are used for our purpose but you hopefully will get the gist of it

CREATE

So came up with a design for such groups (we identify them by Autogen and have an attribute called accountType)

  • Create a Set with Autogen-{GroupName} with an accountType = AutogenSet
  • Set the filter and explicit members needed on the set
  • Create a filter group with Autogen-{GroupName} with an accountType = AutogenGroup
  • Put the ResourceID of the new group in the set to an attribute called ‘connectedGroupObjectID’ and viceversa to ‘connectedSetObjectID’ – This it have relation between the group and set
  • Set the filter on this group as

CLEANUP

  • Create a auth workflow with delete resource and target =  [//Target/connectedSetObjectID]
  • Create a set which has ‘All Autogen Groups’
  • Create a MPR which ties the above i.e. when a delete resource happens for any in set ‘All Autogen Groups’ then run the Auth workflow which will delete the set as well

SCRIPT

Wrote a script so that these groups, sets and links can be created in one go using FIM/MIM Service Powershell Module.

NOTE

  • I assume user already knows the XPATH for his filter (Will help you on how to create it easily as well in a post coming soon).
  • Many custom attributes have been used in FIMService but you get the bigger picture and not necessarily needed for your environment.
  • I am really bad at powershell so don’t be surprised if you see some ‘what the hell did he do that for?’ moment 🙂 You will definitely find mistakes but hey.. works for me.. Still please point it out 🙂

LOGIC

This script was written by me quite specific for our environment and therefore contains a lot of custom attributes and logic. This is just to give an idea what we do / can do with the idea. I have decided not to dumb it down and present it as-is. If you get confused somewhere please do contact me or leave a comment

  • Asks for DisplayName (Mandatory)
  • Asks for a requestedAccountName (Mandatory in our env to generate sAMAccountName and AccountName)
  • Asks for a owner AccountName / uid (Mandatory in our environment to set as the owner of the group)
  • Asks if the Group will have a mail address (if so then we populate some additional attributes to provision to GoogleApps)
    • If Mail Enabled then asks for some additional attributes to be set like
      • Mail Prefix (to generate the mail address)
      • Posting Permission
      • To be hidden from Global Address List or not
      • Is it going to be used as a Security Group in AD (sets as Distribution else as MailEnabledSecurity)
    • If not then sets it as a Security Group
  • Asks for the XPATH Filter – then it goes and checks if it’s correct and does return some users back else exists.
  • Creates the Set and adds the above filter to it
  • Creates the Group
    • Sets the filter pointing to the set above
  • Links the two
  • Done

The only thing it doesn’t do is setting explicit members to the set which is done manually after creation.

 

To the sync side you will simply see one Group come out with Member attribute containing both ExplicitMembers and ComputedMembers and a combination.

Hope this helps someone in their “complex business” environment.