Again, not something that I usually get too involved in but…. it is always fun to write some PowerShell:
Contains GitHub Gists
Not really my cup of tea (networking), but I recently had to pull the routes from a Azure infra project, so… bleagh:
I have recently been working within a client where all Azure/ Office 365 users must perform MFA on logon.
Ages ago I posted about using credential manager to automate Office 365 scripts: https://blog.oholics.net/using-credential-manager-to-authenticate-office-365-scripts/. This method will clearly not suffice where MFA is enforced, as there is no mechanism to allow MFA challenge and response.
Recently I have been looking into using Azure Service Principle objects to bypass MFA and to allow scripts, that need to connect to Azure or other services, to do so without input. Thus, I can then schedule scripted tasks to generate reports on Azure AD objects or AzureRM items.
Firstly I need to create some certificates, these will be used to authenticate, see here: https://blog.oholics.net/creating-simple-ssl-certificates-for-server-authentication-using-openssl/ for details on certificate creation.
Next, once we have the PFX certificate file, we can create the Azure App Registration, using PowerShell:
Then (optionally), if the script that you want to automate will be reading AzureRM objects, run the following script. Note that if the role assignment is to be constrained to a specific resource group, add the -ResourceGroupName switch to New-AzureRMRoleAssignment
Additionally, the RoleDefinitionName can be altered to suit.
Now we have the Service Principle in place, we can connect! But in the case of the Azure AD connection, I need to first allow the application to Read AAD:
Go to the App Registrations blade in Azure AD, pick the application created earlier, then select settings. Select Required Permissions, add Azure AD and add the permissions shown in the following image:
Now lets connect using the certificate thumbprint:
By installing the certificate in the CurrentUser store, only that user can consume the certificate thumbprint for authentication using this method. Lovely.. 🙂
Why is this method secure?
- You can only access the application to sign in if you have installed the certificate on the machine that you want to run the script from.
- To install the certificate, you must know the password that was set on the private key during PFX creation.
- No AAD user object is created
- No plain text passwords need to be stored
To sign in using this method, you must know:
- The AAD Tenant GUID
- The Application GUID of the configured application
- The specific thumbprint of the certificate, used to make the connection
- The certificate and private key must be installed on the machine on which the connection attempt is being made.
I have been doing a fair bit of work with MIM PAM recently, finding a few issues. This has meant that I have re-installed the application (post-SharePoint Foundation), in my lab, a few times.
I was getting a little bored of clicking through the options, ticking boxes and refilling the URL’s etc. Then I spotted on the CD/ DVD, a batch file in the Service and Portal folder – called “Service and Portal_Reference_For_PAM_Install.bat“.
A quick look showed that this would automate MIM PAM installation. However, there was no documentation to go with it – notably to clarify which accounts were referred to by “ADMIN_USER = Administrator” and “SYNC_ADMIN = FIMSyncAdministrator”. A quick google revealed no relevant results…. So, take a snapshot and start trying accounts….. Based on the MSI command run at the end of the script Admin User relates to the SERVICE_ACCOUNT_NAME. So, in my case that relates to the MIMService account.
Thus, my complete working script is as below. Note – my PAM domain is a sub-domain of oholics.net called “priv“, my MIM PAM server is called “mimpam”
Note that the following lines will need to be amended – 7, 10, 17, 19, 20, 21, 22, 35, 54, 55, 62, 63, 70, 71.
Also, note the script assumes that you have a folder C:\Temp to write the log to – if you don’t you’ll get EXIT CODE: 1622
Nice bit of automation – run the file, then make coffee or whatever – certainly something fulfilling 🙂
A little update – the script in its current form is not perfect. Note that the MSI switches include: MAIL_SERVER=”%MACHINENAME%” and SQLSERVER_SERVER=”%MACHINENAME%” – Meaning that both attributes will be set to the local machine name. Set some more attributes and changes the MSI arg’s to suit.
Additionally, I have been testing the RESTful interface over the last few days and have seen some oddities – whether these are related to using this script to install is under investigation…..
Within my AD structure, group management is delegated within certain OU’s, I now need to replicate that functionality in the FIM portal.
The is no real way of identifying which groups should be managed by whom, except the OU within which the group currently resides.
So, to start off with I need to get the parent OU of the group into the portal:
Import the OU into the MV:
Setup an export flow for adOU into the portal.
Then, by using the Lithnet PowerShell Module, we can create all the sets and MPR’s required, below is a sample for creating one delegated “collection”. In production, my XML file is much bigger – delegating group management to around ten different groups.
Note, that you first need to create references to all users who might be given the rights to manage groups. This includes the FimServiceAdmin and FimServiceAccount – referenced by their ObjectID, the others are referenced by their AccountName. All members referenced in this section, are added to the __Set:GroupValidationBypassSet. This set is defined in the non-administrators set – not in this set – this bypasses the group validation workflow:
Create a set of groups to be managed – the filter being the OU that the groups belong to & MembershipLocked=False
Create a set of administrators for this delegation – adding the explicit members
Then create the two MPR’s to allow the members of the administrative set to manage those groups – the first MPR allows modification (Read, Add and Remove) of the ExplicitMember attribute, while the second allows creation and deletion.
Use Import-RMConfig -File <PathToXML> -Preview -Verbose to validate your xml and see what it would do. Drop the “-Preview” to make the change
While looking to improve on my method of getting exceptions or a long list of mail suffixes into an array, to be checked during code execution, I came across this: https://msdn.microsoft.com/en-us/library/windows/desktop/ms696048(v=vs.85).aspx
This seemed to me to be a really nice solution, just defining all exceptions and suffixes within one file, read it in on code execution, then check for existence or whatever in the code.
So, given the following xml file:
Add the System.Xml Import and declare the variables, so they are global:
Add the code to read the xml file into the Initialize Sub:
Then, when you wish to look for those values within those variables – just like in the last post:
In this post: https://blog.oholics.net/a-generic-array-from-file-function-to-cope-with-inevitable-exceptions/, I documented a method of generating an array of values from a text file.
While I was happy that this method worked, I was not entirely happy with the fact that I still had some hard coded values in the code. However, the way that the function operated meant that if I took my collection of mail suffixes (20+) and added them all to the text file, then the array would be built for each and every user that passed through the dll, not too efficient!
So, I was looking for something a little more elegant. I was happy for the array to simply be defined when the dll was loaded.
Here is my solution:
At the beginning of my AD MA, I declare my dates and logging levels etc, then generate those arrays using the function. These arrays are now static and are good for processing all users without being regenerated.
When I wish to look into the array to validate a valid email suffix for example, I go from this (as in the last post):
Much cleaner – plus all suffixes can now just reside in a text file.
Note that updates to the text file will only be realised if the dll is reloaded and the array is regenerated. I believe that this is after 5 minutes of inactivity and seems to hold true from testing.
A long term goal of mine, has been to get “account requestors” to take ownership of their Service Accounts.
Attempts have been made by my predecessors to record an owner of a service account, but it has simply been done as a string attribute of the AD object. Thus, when the person leaves and the account is deleted, the service account becomes orphaned, with an reference to a long forgotten ID.
So thinking of a way to carry this out….. I am already using the email address of the owner of an administrative account to make decisions about whether the administrative account should be enabled or disabled – based on the end date of the owner – discovered by looking up the email address in the MV.
I figured that I could do something similar for those Service Accounts. I’ll be creating service accounts via the portal, the owner of the account will be assigned to the manager attribute. So, how can I get the email address of the manager into the MV as a thing that I can lookup??? I can’t do an advanced flow rule on the FIMMA, and even if I could, Manager is a reference attribute, so I can’t do it anyway… I found an article about dereferencing another attribute, that get me going down this path….. The solution is simple. Create a new attribute and binding in the portal – “ManagerEmailAddress”, then setup a workflow as follows:
When the account falls into scope, the managers email address is set into that new attribute – in the sync engine create a direct flow to put that into the MV (I’m using “serialNumber” – for one reason or another, that I wont go into :)).
I have on the import from AD, some code to set an MV boolean flag – “functionalID” – if the DN of the person object contains the strings found in the Service Account OU’s, thenfunctionalID = True. This attribute is pushed into the portal and is used in set definitions.
So, I’m getting there. Now I need something to set another flag in the MV that will go to the portal. this one defines if the owner of the Service Account is approaching their end date (30 days prior):It is defined on the Import from AD and populates the MV attribute “functionalID-owner-expiring”
Of course after initial code definition, I found another of those inevitable exceptions, so added the generateArrayFromFile function, with a reference (in txt file) to the email address that should be ignored.
Create attribute and binding in the portal for FunctionalID-owner-expiring
Setup an Export in the FIMMA for the new attribute
Create a set: FunctionalID = True and FunctionalID-owner-expiring = True.
Create notification workflow and mail template: notification to [//Target/Manager], then the set transition MPR.
I think I have it, just need to do a little testing to see that it works as expected.
I’m still a long way from the stated goal, as I still need to find “owners” for all of those accounts that have been created in the past.
In the last few days, I have had a few more exceptions to cope with in my FIM Config.
- Another new mail suffix
- A user who is employed by one tenant, who has that tenants email address suffix; but who is on secondment to another tenant, who have a different mail suffix. The users attributes have been changed in the HR system, so that they gain access to the stuff in the other tenant, which is controlled by automatic groups, based on attribute data!
So, I’d been thinking for a while about having a method to add exceptions without having to add them to the code directly and thus forcing a rebuild followed by full syncs. I found a nice function to read a text file to an array, this is added to the top of the dll after the lines:
Public Class MAExtensionObject_YourMA
So, to put this use – take my previous port regarding generating validating email addresses: https://blog.oholics.net/defining-a-unique-email-address-and-validating-mail-suffix/, at line 97 I ask “Does the suffix match?” This chunk is now as follows:
So, the referenced file simply has the email address of the user that I don’t want to be alerted about. If the email address does not match the expected value, look in the array generated from the text file; if it in not in there either raise an error to get this fixed or investigated.
Regarding the valid mail suffixes – I posted about this already: https://blog.oholics.net/emailaddresspresent-flag-setting-and-checking-email-suffix-validity/.
I have a hardcoded list of those that are already in use in the dll, if the suffix is not found in that array, it does a lookup of the array generated from the “suffixes” text file, if it is not in there it raises an error:
When trying something new out with FIM Development, I often see how to do it in a console app beforehand. Then once I have the process/ method worked out, I translate it into FIM code. Usually this is a very clean process and is quicker than editing the FIM code directly, then doing sync’s on individual accounts.
When I was initially looking at exporting userAccountControl values to AD, I used Jorge’s code snippet: https://jorgequestforknowledge.wordpress.com/2010/07/29/managing-the-useraccountcontrol-attribute-in-ad-by-fim/ as the basis for my code. Initially, I had some difficulty understanding the differences between the “Or’s and And’s”, so used a console app to understand what integer values the different combinations made. The list of flags can be found here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa772300(v=vs.85).aspx
My userAccountControl Export code became a bit of a monster, due to the number of rules needed to match the existing configuration.
The console app is super simple – fiddle with the different flags and operators to see the different results:
Note, that you need to add the reference to “Active DS Type Library”, else you will get squiggles under “ADS_USER_FLAG”: