Lithnet FIM/MIM Synchronization Service PowerShell Module released

Ryan Newington (Developer of FIM/MIM Lithnet PS Module, new FIM/MIM Service Client and RestAPI) already anounced new PowerShell Cmdlets for the FIM/MIM Synchronization Service on the last MIM Team User Group Meeting.

You can now download that module on github.

See documentation on the modules and also the disclaimer.

Download the FIM/MIM Sync PowerShell Module.

Great job again Ryan.

Here is a list of modules included:

Read more of this post

Authorize MIM Portal user image upload with Microsoft Cognitive Services

I saw these great videos from //build keynote some weeks ago about the Microsoft Cognitive Services and I was really impressed. I know these APIs like face, emotion, speech are designed for other purposes but I was thinking to myself on who to benefit from them for identity management.

So I remembered some time ago when talking about MIM Portal as a user self-service portal for personal data some customers find it is sometimes not a good idea if users can upload their own photos. The arguments where that photos cannot be validated in that way that it really belongs to that person. So people could upload for example funny pictures and avatars or even more bad images.

Sure, you can handle this by organizational policies, but I was thinking of a technical solution. At this point when thinking about Microsoft Cognitive Services the Face API came to my mind.

If you check the Face API it has methods for face detection in images and also face identification or verifying. You can also create person groups and persons with multiple faces saved in Azure if you want.

But for my little demo I only need the face detect and verify methods.

So here is how this demo works:

If people upload new images to MIM Portal, I trigger an authorization workflow and get the current and new photo with an MIMWAL update resource activity and pass that data to a PowerShell script which then calls the Face API.

The PowerShell Script uploads both images to Azure to do a face detection within the image and then returns a faceId for each of the pictures. Images are saved 24 hours Azure.
Read more of this post

MIM2016: Using Azure MFA in an Authorization Workflow with PowerShell

While thinking about Azure MFA and it’s usage in MIM for password reset or as authorization step when requesting a PAM role, I thought to myself, why not use this as an workflow activity in an authorization workflow. For example when requesting a group membership. Sadly you can not configure the OOB MFA activities that comes with MIM.

So why not doing it on my own, using the Azure MFA SDK. And I find out it’s quite simple so far.
This demo approves a member join to a group by Azure MFA with a phone call, you have to anser the call with a # to get into the group. The MobilePhone attribute of your MIM Portal users have to be set to a valid number for this demo to work.
Read more of this post

Assign Azure/O365 licenses based on AD group membership

Hello,

just a short post today.

I thgought it might be a good idea to share more scripts in future, so here is the first one to assign Azure/O365 licenses based on AD group membership.
EMS/AADP and RMS licenses can also be assigned directly in Azure using group memberships but you still have to handle O365 licenses by your own with scripts.

So at some customers I have the reqirement to also manage O365 licenses after synchronizing objects with AADConnect, so I decided to manage all licenses with script.

This script still need some improvement in security (PW stored in file) but you can modify that like you want.
Also I do not cover License Option of O365 Licenses, instead the complete O365 features will be assigned.

Read more of this post

A minimalistic FIM AAD sync connector solution for Windows Intune

After some DirSync implementations one of my FIM customers has the need for mobile device management with Windows Intune. So it seems a perfect time to me for my first implementation of the AAD Connector for FIM 2010 R2.

The customer had the following special requirements:

  • No Password Sync, instead using SSO with ADFS
  • Minimalistic set of attributes on users in the cloud (Corporate and legal issues)
  • Manual management of which user goes into the cloud or not (by helpdesk)
  • Usage of proxy connection for all servers incl. FIM (no direct internet connect)

I searched the internet a bit for configuration of the WAAD connector, but the technical reference ends at the step of adding attribute flows and other posts are mostly for complex scenarios (hybrid, multi-forest and so on).

So once again I had to figure it out by myself and I decided to put my solution on here for this minimalistic implementation. I will skip the installation and configuration of ADFS and WAP, the Azure AD configuration and also the firewall/proxy configuration. There is a lot of documentation out there for this. Bit I will give the one or the other hint on some facts.
To setup your Azure/Intune for SSO with ADFS follow the guide in your Azure/Intune portal.

Read more of this post

Using AuthZ Workflows on FIM built-in service account changes [Workaround]

As everybody knows the two FIM built-in accounts “Forefront Identity Manager Service Account” and “Built-in synchronization account” will bypass all AuthZ workflows.

So by default you are not able to do approvals for example on changes by these accounts.
In addition you cannot have AuthZ workflows on set transition, only Action workflows are allowed here.

But a customer wants to final delete accounts 180 days after deactivation.
This action should be approved by a helpdesk administrator because there are some manual and non-technical tasks to do before this should happen.

Hmmm, so with the above restrictions, what to do?

I used the FIM PowerShell Activity a lot in that customer solution, and I remember that changes done by this activity runs in the context of a normal user account (from FIMs perspective) which is the service account of the FIM web service (svcFIMService in my case).

In order to allow updates to the FIM service by this account via the Export-FIMConfig and Import-FIMConfig cmdlets I created this account in portal and grant permissions to the necessary objects.
If it does not exists, just create this account with the following attributes set:

  • DisplayName
  • AccountName (sAMAccountName from FIM webservice account in AD)
  • Domain
  • ObjectSID (from AD)

(You should manually create this account, as I got into trouble when I try to synchronize this account to FIM portal)

How to use this:

I created a workflow with the PowerShell activity which sets an attribute I created on user account, let’s say DoFinalDelete, to a value of true.

I created a MPR which fires these workflow when users transition into my set “Users with disableDate older than 180 days”.
(Btw. this disableDate is also set by a powershell workflow activity, as you can imagine)

Now I’m able to create an MPR with an AuthZ workflow to approve this change of the account svcFIMService and after that can trigger all other MPRs and workflows I want.
So in my scenario I import the DoFinalDelete attribute to MV and trigger deprovisioning on the objects in the provisioning code of my MV extension using the DeprovisionAll() method, which then triggers all the defined actions on my MA’s regarding to their deprovisioning configurations.

So once again this great piece of code “FIM PowerShell Activity” from Craig Martin and Brian Desmond is like a Swiss army knife for me. (thx guys)
You can do nearly all with PowerShell and only have to maintain one custom activity in FIM Portal, which made upgrades and migrations much easier.

Maintenance of Indexes and Fulltext-Catalog of FIM SQL databases

This is a follow-up to 2 posts I wrote in the past. To avoid index fragmentation and the issues I had with sets in the past I implemented 2 SQL Jobs to keep them clean.

Speed up FIM 2010 R2 SQL performance by rebuild/reorganize indexes

FIM 2010 R2: SQL timeout on using large sets in other sets

For around a month now the following to SQL jobs work perfectly in my customer’s environment even in production, so I think it seems to be safe to implement this, but you should test them on your own.

By default both SQL jobs run weekly on every sunday.

The first SQL job starts a PowerShell script to maintain the indexes on all tables of the FIMService database. The script works only on indexes with fragmentation higher than 20% and will only rebuild 100 indexes per schedule (You can adjust this in the script).

The PowerShell script is not my work, see author information in the scripts URL.

Make sure you replace the DOMAIN\USER and SERVERNAME placeholder in the script to values that fits to your environment.

USE [msdb]
GO

/****** Object:  Job [Index Rebuild on all FIMService tables]    Script Date: 11/17/2013 14:08:12 ******/
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
/****** Object:  JobCategory [[Uncategorized (Local)]]]    Script Date: 11/17/2013 14:08:12 ******/
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

END

DECLARE @jobId BINARY(16)
EXEC @ReturnCode =  msdb.dbo.sp_add_job @job_name=N'Index Rebuild on all FIMService tables',
		@enabled=1,
		@notify_level_eventlog=0,
		@notify_level_email=0,
		@notify_level_netsend=0,
		@notify_level_page=0,
		@delete_level=0,
		@description=N'No description available.',
		@category_name=N'[Uncategorized (Local)]',
		@owner_login_name=N'DOMAIN\USER', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/****** Object:  Step [Index rebuild step]    Script Date: 11/17/2013 14:08:12 ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Index rebuild step',
		@step_id=1,
		@cmdexec_success_code=0,
		@on_success_action=1,
		@on_success_step_id=0,
		@on_fail_action=2,
		@on_fail_step_id=0,
		@retry_attempts=0,
		@retry_interval=0,
		@os_run_priority=0, @subsystem=N'PowerShell',
		@command=N'PUSHD SQLSERVER:\SQL\SERVERNAME\DEFAULT

# PowerShell script iterated over tables in database, gathers set of indexes
# then for every index, gathers all partitions and performs RebuildIndex on that partition
# To execute this script:
#      Launch SQL PowerShell ( Start -> Run -> sqlps.exe)
#      in Powershell window CD  SQL\machine_name\instance_name; Example: CD SQL\demo-machine\DEFAULT
#      Copy the following script and paste it in SQL powershell window  to run this script
# http://sethusrinivasan.com/2012/02/14/index-rebuild-on-large-database-sql-agent-powershell-job/

# following variables can be updated
# database Name
$dbName = "FIMService"
# number of indexes to rebuild, script terminates after Rebuilding specified number of indexes
$indexesToProcess = 100
# fragmentation threshold - indexes with fragmentation less than this value will be skipped
$fragmentationThreshold = 20

$processedIndex = 0
$tables = dir Databases\$dbName\Tables
"Listing all tables from Database:" + $dbName

foreach($table in $tables)
{
   "    Listing Indexes for Table:" + $table
   foreach($index in $table.Indexes)
   {
        "    Listing Physical Partitions for Indexes:" + $index
        foreach($partition in $index.PhysicalPartitions)
        {
            $fragInfo = $index.EnumFragmentation([Microsoft.SqlServer.Management.Smo.FragmentationOption]::Sampled,
                                    $partition.PartitionNumber)
            $fragmentation = $fragInfo.Rows.Item(0)["AverageFragmentation"]

            "        Checking fragmentation on " +  $index.Name + " is greater than :" + $fragmentationThreshold
            "        Current Fragmentation:" + $fragmentation
            "        Paritition:" + $partition.PartitionNumber
            if($fragmentation -gt $fragmentationThreshold)
            {
                "        Rebuilding Index: " + $index.Name + " partition:" + $partition.PartitionNumber
                $processedIndex = $processedIndex + 1
                if($index.IsPartitioned -eq $True)
                {
                    $index.Rebuild($partition.PartitionNumber)
                }
                else
                {
                    $index.Rebuild()
                }
            }

            if ( $processedIndex -ge $indexesToProcess)
            {
                break
            }
        }

        if ( $processedIndex -ge $indexesToProcess)
        {
            break
        }
    }

    if ( $processedIndex -ge $indexesToProcess)
    {
        break
    }
}

POPD',
		@database_name=N'master',
		@flags=48
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'Index rebuild weekly',
		@enabled=1,
		@freq_type=8,
		@freq_interval=1,
		@freq_subday_type=1,
		@freq_subday_interval=0,
		@freq_relative_interval=0,
		@freq_recurrence_factor=1,
		@active_start_date=20120214,
		@active_end_date=99991231,
		@active_start_time=130000,
		@active_end_time=235959,
		@schedule_uid=N'69a997b3-6475-4c18-bd87-9f4cf27e687a'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
    IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:

GO

The second SQL job uses the SQL internal function to do a rebuild on the FIMService full text catalog. You can user this SQL script or the build-in wizard to create this job.

If using this script make sure to replace the DOMAIN\USER placeholder to values that fits to your environment.

USE [msdb]
GO

/****** Object:  Job [Start Optimize Catalog Population on FIMService.ftCatalog]    Script Date: 11/17/2013 14:13:07 ******/
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
/****** Object:  JobCategory [Full-Text]    Script Date: 11/17/2013 14:13:07 ******/
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'Full-Text' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Full-Text'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

END

DECLARE @jobId BINARY(16)
EXEC @ReturnCode =  msdb.dbo.sp_add_job @job_name=N'Start Optimize Catalog Population on FIMService.ftCatalog',
		@enabled=1,
		@notify_level_eventlog=2,
		@notify_level_email=0,
		@notify_level_netsend=0,
		@notify_level_page=0,
		@delete_level=0,
		@description=N'Scheduled full-text optimize catalog population for full-text catalog ftCatalog in database FIMService. This job was created by the Full-Text Catalog Scheduling dialog or Full-Text Indexing Wizard.',
		@category_name=N'Full-Text',
		@owner_login_name=N'DOMAIN\USER', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/****** Object:  Step [Full-Text Indexing]    Script Date: 11/17/2013 14:13:07 ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Full-Text Indexing',
		@step_id=1,
		@cmdexec_success_code=0,
		@on_success_action=1,
		@on_success_step_id=-1,
		@on_fail_action=2,
		@on_fail_step_id=-1,
		@retry_attempts=0,
		@retry_interval=0,
		@os_run_priority=0, @subsystem=N'TSQL',
		@command=N'USE [FIMService]
ALTER FULLTEXT CATALOG [ftCatalog] REORGANIZE
',
		@database_name=N'master',
		@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'Weekly FIM.ftCatalog rebuild',
		@enabled=1,
		@freq_type=8,
		@freq_interval=1,
		@freq_subday_type=1,
		@freq_subday_interval=0,
		@freq_relative_interval=0,
		@freq_recurrence_factor=1,
		@active_start_date=20131024,
		@active_end_date=99991231,
		@active_start_time=120000,
		@active_end_time=235959,
		@schedule_uid=N'6ba433a3-79eb-4552-ba0b-5f1cc9d5dc1b'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
    IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:

GO
%d bloggers like this: