{"id":1133,"date":"2010-12-15T09:47:50","date_gmt":"2010-12-15T09:47:50","guid":{"rendered":"https:\/\/www.wapshere.com\/missmiis\/?p=1133"},"modified":"2010-12-16T08:14:39","modified_gmt":"2010-12-16T08:14:39","slug":"three-different-ways-to-create-a-bpos-management-agent","status":"publish","type":"post","link":"https:\/\/www.wapshere.com\/missmiis\/three-different-ways-to-create-a-bpos-management-agent","title":{"rendered":"Three Different Ways to Create a BPOS Management Agent"},"content":{"rendered":"<table border=\"0\">\n<tbody>\n<tr>\n<td width=\"500\">This year I have been working on a large BPOS project, with 17000 mailboxes being migrated from a variety of source mail systems. As is currently obligatory for such an installation, we use DirSync to synchronise users, groups and contacts from a source AD to BPOS. So while I don&#8217;t need my own BPOS MA for provisioning and flow rules, I have implemented one as a feedback mechanism. This post shows the series of approaches I&#8217;ve taken.<\/td>\n<td><a href=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2010\/12\/bpos-loopback.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-1139\" title=\"bpos loopback\" src=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2010\/12\/bpos-loopback-300x251.jpg\" alt=\"\" width=\"300\" height=\"251\" srcset=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2010\/12\/bpos-loopback-300x251.jpg 300w, https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2010\/12\/bpos-loopback.jpg 423w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><!--more--><\/p>\n<h3>1. Using the DirSync DLL<\/h3>\n<p>It is actually possible to create an XMA on your FIM server and then give it the DLL used on the DirSync server for its BPOS MA. However, after verifying that this appeared to be possible, I didn&#8217;t persevere with it. It only gives me the attributes that DirSync is interested in &#8211; the ones that exist in my source AD and which I already know about. What I really wanted were the details you can get through powershell such as status, subscriptions and last logged in.<\/p>\n<h3>2. A Powershell XMA<\/h3>\n<p>I&#8217;ve already blogged about how to write a\u00c2\u00a0<a href=\"https:\/\/www.wapshere.com\/missmiis\/remote-powershell-script-xma\">Remote Powershell XMA<\/a> so I&#8217;m just including the powershell script here.<\/p>\n<p>The XMA connects to the DirSync server (it doesn&#8217;t have to be the DirSync server, but in my case that&#8217;s the only one with the appropriate firewall permissions) and runs this script which uses the Get-MSOnlineUser cmdlet to collect information about all the users. The data is saved in two AVP files (as you have to run the commands for enabled and disabled users seperately) and the final step (not shown here) is to copy the two AVP files into a single import.avp file in your MA&#8217;s MaData folder, all ready to import.<\/p>\n<h4>Get-BPOSUsers.ps1<\/h4>\n<pre>PARAM($BPOSUser, $BPOSPassword)\r\n\r\n$SNAPIN = \"Microsoft.Exchange.Transporter\"\r\n$FOLDER = \"C:\\scripts\"\r\n\r\n$SVATTRIBUTES = @(\"Identity\",\"FirstName\",\"LastName\",\"Password\",\"UserLocation\",\"SubscriptionIds\",\"MailboxSize\",\"DisplayName\",\"FaxNumber\",\"MobilePhone\",\"OfficePhone\",\"StreetAddress\",\"City\",\"StateOrProvince\",\"ZipOrPostalCode\",\"CountryOrRegion\",\"JobTitle\",\"Department\",\"OfficeNumber\",\"PasswordExpirationDate\",\"UsedMailboxSize\",\"CreatedDate\",\"IsActive\",\"LastSignedInDate\",\"SourceProps\",\"TargetProps\",\"Props\",\"ContentType\",\"StatusText\",\"SourceTechnology\",\"SourceServerVersion\",\"SourceServer\",\"SourceLocation\",\"SourceUID\",\"TargetTechnology\",\"TargetServerVersion\",\"TargetServer\",\"TargetLocation\",\"TargetUID\",\"ResolvedName\",\"MigrationStatus\",\"LastMigrationTime\",\"Error\",\"TargetMatches\",\"TargetMatchType\",\"HardMatch\",\"HardMatchName\",\"FirstSoftMatch\",\"FirstSoftMatchName\",\"AssociatedMatches\",\"Name\",\"UniqueName\",\"Description\",\"StorageByteSize\",\"RuntimeByteSize\",\"ItemCount\",\"DetailLevel\",\"CreatedTime\",\"ModifiedTime\",\"AccessedTime\",\"Attachments\",\"Acls\",\"doNotInheritACL\",\"ResolvableUri\")\r\n$MVATTRIBUTES = @(\"ProxyAddresses\")\r\n\r\n$ENFILE = $FOLDER + \"\\enabled.avp\"\r\n$DISFILE = $FOLDER + \"\\disabled.avp\"\r\n\r\nfunction WriteAttributes\r\n{\r\n  PARAM($object, $file)\r\n  END\r\n    {\r\n        $text = \"\"\r\n        foreach ($attrib in $SVATTRIBUTES) {\r\n            $value = $object.$attrib -replace \"`n\", \" \"\r\n            $text = $text + $attrib + \":\" + $value + [Environment]::NewLine\r\n        }\r\n        foreach ($attrib in $MVATTRIBUTES) {\r\n            foreach ($item in $object.$attrib) {\r\n                $text =  $text + $attrib + \":\" + $item + [Environment]::NewLine\r\n            }\r\n        }\r\n        $text =  $text + [Environment]::NewLine\r\n        Add-Content $file $text\r\n    }\r\n}\r\n\r\nIF ((GET-PSSNAPIN $SNAPIN) \u00e2\u20ac\u201ceq $NULL)\r\n{\r\n    Add-PSSnapin $SNAPIN\r\n}\r\n\r\n$fileEnabled = New-Item -Type file -Path $ENFILE -Force\r\n$fileDisabled = New-Item -Type file -Path $DISFILE -Force\r\n\r\n$securepwd = ConvertTo-SecureString -String $BPOSPassword -AsPlainText -Force\r\n$bposcred = New-Object System.Management.Automation.PSCredential $BPOSUser, $securepwd\r\n\r\n$colEnabledUsers = Get-MSOnlineUser -Credential $bposcred -enabled -ResultSize 50000\r\nforeach ($userObject in $colEnabledUsers) {\r\n   WriteAttributes -object $userObject -file $fileEnabled\r\n}\r\n\r\n$colDisabledUsers = Get-MSOnlineUser -Credential $bposcred -disabled -ResultSize 50000\r\nforeach ($userObject in $colDisabledUsers) {\r\n    WriteAttributes -object $userObject -file $fileDisabled\r\n}<\/pre>\n<h3>3. Staging via SQL<\/h3>\n<p>I ran the\u00c2\u00a0powershell XMA for a good six months before I had to change. One problem was that it was\u00c2\u00a0slow -\u00c2\u00a0sometimes taking 15 minutes to extract the data to the AVP file before even beginning the import. But the thing that finally killed it was introducing password sync.<\/p>\n<p>I&#8217;ll be blogging\u00c2\u00a0next\u00c2\u00a0on how I got password sync to BPOS working &#8211; but I&#8217;ll just comment here about one problem I had. It seems there is a bug when you try and run the MA in a seperate process and then introduce a password extension &#8211; the extension is not recognised (Error: &#8220;Class not registered&#8221;). However I found my XMA didn&#8217;t run all that well in process. Rather than hunting down the memory leak I decided to stage the data via SQL. This gives me two great advantages: I can use the OOB SQL MA, and I can introduce a <a href=\"https:\/\/www.wapshere.com\/missmiis\/sql-query-generate-delta-table\">delta table<\/a>!<\/p>\n<p>My powershell script (below) is now run once an hour from the Task Scheduler on the DirSync server. I then have an SSIS package which imports the text files into SQL tables (including <a href=\"https:\/\/www.wapshere.com\/missmiis\/configuration-of-the-sql-ma\">a multivalue and a delta table<\/a>) which I call uing dtexec.exe just before running the Import run profile.<\/p>\n<p>For the purposes of the multivalue table I now export a third file for multivalue data. Note I&#8217;ve done some extra trickery with SubscriptionIds here, after realising that while people could have more than one, the attribute comes as a comma separated list, rather than a true multi.<\/p>\n<h4>Get-BPOSUsers2.ps1<\/h4>\n<pre>PARAM($BPOSUser, $BPOSPassword)\r\n\r\n$SNAPIN = \"Microsoft.Exchange.Transporter\"\r\n$FOLDER = \"C:\\scripts\"\r\n\r\n$SVATTRIBUTES = @(\"Identity\",\"FirstName\",\"LastName\",\"UserLocation\",\"MailboxSize\",\"DisplayName\",\"PasswordExpirationDate\",\"UsedMailboxSize\",\"CreatedDate\",\"IsActive\",\"LastSignedInDate\",\"FaxNumber\",\"MobilePhone\",\"OfficePhone\",\"StreetAddress\",\"City\",\"StateOrProvince\",\"ZipOrPostalCode\",\"CountryOrRegion\",\"JobTitle\",\"Department\",\"OfficeNumber\",\"Description\",\"MigrationStatus\")\r\n$MVATTRIBUTES = @(\"ProxyAddresses\",\"SubscriptionIds\")\r\n\r\n$ENFILE = $FOLDER + \"\\en_svdata.csv\"\r\n$DISFILE = $FOLDER + \"\\dis_svdata.csv\"\r\n$MVFILE = $FOLDER + \"\\mvdata.csv\"\r\n$ENFILE_Prev = $FOLDER + \"\\en_svdata.prev.csv\"\r\n$DISFILE_Prev = $FOLDER + \"\\dis_svdata.prev.csv\"\r\n$MVFILE_Prev = $FOLDER + \"\\mvdata.prev.csv\"\r\n$ENFILE_Tmp = $FOLDER + \"\\en_svdata.tmp\"\r\n$DISFILE_Tmp = $FOLDER + \"\\dis_svdata.tmp\"\r\n$MVFILE_Tmp = $FOLDER + \"\\mvdata.tmp\"\r\n\r\nfunction WriteMVAttributes\r\n{\r\n  PARAM($object)\r\n  END\r\n    {\r\n        foreach ($attrib in $MVATTRIBUTES) {\r\n            foreach ($item in $object.$attrib) {\r\n                  $strItem = [string]$item\r\n                  if ($strItem.Contains(\",\")) {\r\n                    foreach ($substr in $strItem.Split(\",\")) {\r\n                      $text =  $object.Identity + \";\" + $attrib + \";\" + $substr\r\n                      Add-Content $MVFILE_Tmp $text\r\n                    }\r\n                  }\r\n                  else {\r\n                    if ($strItem -ne \"\") {\r\n                      $text =  $object.Identity + \";\" + $attrib + \";\" + $item\r\n                      Add-Content $MVFILE_Tmp $text\r\n                    }\r\n                  }\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\nif (!$BPOSUser -or !$BPOSPassword) {\r\n  Write-EventLog -LogName \"Application\" -Source \"Custom Scripts\" -EventID \"1\" -EntryType Error -Message \"Script SSISGet-bposUsers.ps1 aborted: No username or password supplied.\"\r\n  exit\r\n}\r\n\r\nIF ((GET-PSSNAPIN $SNAPIN) \u00e2\u20ac\u201ceq $NULL)\r\n{\r\n    Add-PSSnapin $SNAPIN\r\n}\r\n\r\nif (Test-Path $ENFILE) {Copy-Item -Path $ENFILE -Destination $ENFILE_Prev -Force}\r\nif (Test-Path $DISFILE) {Copy-Item -Path $DISFILE -Destination $DISFILE_Prev -Force}\r\nif (Test-Path $MVFILE) {Copy-Item -Path $MVFILE -Destination $MVFILE_Prev -Force}\r\n\r\nAdd-Content $MVFILE_Tmp \"Identity;attrName;attrValue\"\r\n\r\n$securepwd = ConvertTo-SecureString -String $BPOSPassword -AsPlainText -Force\r\n$bposcred = New-Object System.Management.Automation.PSCredential $BPOSUser, $securepwd\r\n\r\n$colEnabledUsers = Get-MSOnlineUser -Credential $bposcred -enabled -ResultSize 50000\r\n$colEnabledUsers | select -Property $SVATTRIBUTES | export-csv $ENFILE_Tmp -NoTypeInformation -Encoding Default -Delimiter \";\"\r\nforeach ($userObject in $colEnabledUsers) {\r\n   WriteMVAttributes -object $userObject\r\n}\r\n\r\n$colEnabledUsers = Get-MSOnlineUser -Credential $bposcred -disabled -ResultSize 50000\r\n$colEnabledUsers | select -Property $SVATTRIBUTES | export-csv $DISFILE_Tmp -NoTypeInformation -Encoding Default -Delimiter \";\"\r\nforeach ($userObject in $colEnabledUsers) {\r\n   WriteMVAttributes -object $userObject\r\n}\r\n\r\nMove-Item -Path $ENFILE_Tmp -Destination $ENFILE -force\r\nMove-Item -Path $DISFILE_Tmp -Destination $DISFILE -force\r\nMove-Item -Path $MVFILE_Tmp -Destination $MVFILE -force<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>This year I have been working on a large BPOS project, with 17000 mailboxes being migrated from a variety of source mail systems. As is currently obligatory for such an installation, we use DirSync to synchronise users, groups and contacts from a source AD to BPOS. So while I don&#8217;t need my own BPOS MA&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[47,42,23],"tags":[],"class_list":["post-1133","post","type-post","status-publish","format-standard","hentry","category-bpos","category-fim-2010","category-powershell"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pkp1o-ih","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/1133","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/comments?post=1133"}],"version-history":[{"count":13,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/1133\/revisions"}],"predecessor-version":[{"id":1176,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/1133\/revisions\/1176"}],"wp:attachment":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/media?parent=1133"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/categories?post=1133"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/tags?post=1133"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}