Thursday, September 24, 2009

Powershell function: Convert-DiacriticCharacters


We needed to normalize diacritics characters to standard English characters. Diacritic characters are extended or accented characters to the modern latin basic alphabet i.e. A-Z. Scandinavian diacritics such as å,ä and ö should in normalized form become a, a and o. Spanish diacritics should such as ó, ñ and ç should be normalized to o, n and c. The unicode normalization forms can be found here and the normalization charts here.
 
Since we mostly use powershell these days, I've written a nice Convert-DiacriticCharacters powershell function.
 
function Convert-DiacriticCharacters {
    param(
        [string]$inputString
    )
    [string]$formD = $inputString.Normalize(
            [System.text.NormalizationForm]::FormD
    )
    $stringBuilder = new-object System.Text.StringBuilder
    for ($i = 0; $i -lt $formD.Length; $i++){
        $unicodeCategory = [System.Globalization.CharUnicodeInfo]::GetUnicodeCategory($formD[$i])
        $nonSPacingMark = [System.Globalization.UnicodeCategory]::NonSpacingMark
        if($unicodeCategory -ne $nonSPacingMark){
            $stringBuilder.Append($formD[$i]) | out-null
        }
    }
    $stringBuilder.ToString().
Normalize([System.text.NormalizationForm]::FormC)
}
 
The resulting function will convert diacritics in the follwoing way:
 
    PS C:\> Convert-DiacriticCharacters "Ångström"
    Angstrom
    PS C:\> Convert-DiacriticCharacters "Ó señor"
    O senor

In our Identity Management projects we encounter issues like these as soon as we deal with global companies. Many systems can't handle Unicode characters or diacritic characters from different non-unicode code pages, in our case we were writing some code to provision users in RACF and RACF couldn't handle the characters.

Automated User Provisioning with Windows PowerShell


As you can see in the history of my previous articles (hmm …my single previous article) I’m not a frequent blogger. I just thought that I had to make a blog response to a fairly nice but slightly missing the point article in Technet Magazine. The article is called Windows Powershell Automated User Provisioning, which comes in four parts. The article describes how to do create a User (AD and Exchange 2007), Create Home Folder, ACL home folder and Add user to groups, Set other attributes.

The scripts are nice enough but I’ve gone through a similar process in a global Active Directory environment and the process is not as simple as the Technet article sees things. There are two things which lack consideration, multi site as the first point and the other is a lack of consideration for mature environments. I’m not sure if the latter is explained well as a single point but I’ll expand on that one in a second. I’m not really criticising here, since I enjoyed the Technet article, but more extending the discussion on provisioning.


To the first point, multi site:

The scripts in the article don’t take into consideration the normal 15 minute sync replication delay in multi site environments. You can’t blankly create AD users without specifying a DC. You need to control where you create the account if you wish to have “realtime” provisioning. You need to have some logic to first resolve where the users are going to be created. For example use the logic, whatever it happens to be, to determine which home folder server to use. Then you need to resolve which site the home folder server is in or directly ask the home folder server for its secure channel using NLTEST.exe. The secure channel will be an ideal DC to provision the user to.

When provisioning the exchange 2007 mailbox you will step into a similar issue to the issue above, all though this one is easily solvable. You need to establish which site the mailbox resides in before you create the account. If exchange and the account is in the same domain but different sites then you will face replications issues i.e. The user account might not have been synchronized to the DC which exchange is talking to. To solve this issue you can use the new-mailbox CMDLET’s switches ‑domaincontroller and ‑linkeddomaincontroller. The latter switch is for specifying the DC for the account domain if exchange is running in a resource forest.

Moving into the ACLing of home folders, whatever client or server running the cacls.exe tool will be doing username to SID resolution over its secure channel. If you are running the script on a workstation in one site and the target home folder server sits in another site then there might be further replication issues where the SID resolution fails to find the user account. Cacls.exe can also mess up the order of the ACEs in the ACL so therefore it shouldn’t be used. Using Get-ACL and the System.Security.AccessControl namespace is a far better idea.


Second point ,mature environments.

What I’m trying to say whit this one is that the Technet article doesn’t take into consideration, for better or worse, things like : We have NetApp Filer servers, other people might have Samba based NAS solutions for home folder storage; corporate standards –a standard might have been set across the corporate for each home folder to be a share. I know it’s not necessary to have this anymore but once implemented in an environment it can be hard to back out of it. Large mature environments often have these quirks.

In my first point, the multi site point, I fall flat on the second point of critique actually. NetApp Filers don’t support the RPC call behind the NLTEST query for secure channel. So I was violating my own point of critique in that paragraph. This complicates the issue of getting hold of a suitable DC for account provisioning. NetApp Filers doesn’t either support the NLTEST query to get the site name (NLTEST is using theDsGetSiteName() in NETAPI32.dll). This leaves us with querying the subnets and calculating the site from the IP address of the NetApp Filer.

If the corporate standard is to user individual shares for each home folder, then you have to do two additional tasks to fully provision an AD user. You need to create a share and then permission the share. A share can be created using WMI or by calling NetShareAdd() inNETAPI32.dll. WMI works fine in Windows only environment, NetApp Filers can auto create shares but in other NAS environments you could try to resort back to the NetShareAdd() function. We decided on usingNetShareAdd() function which is a C function, we call it by on the fly compiling a small C# class which in turn uses PInvoke to call the C function. Share permissioning can be done with System.Security.AccessControl.NativeObjectSecurity.

When you permission the share and folder, be sure that you have read the ObjectSID from the newly provisioned AD account. Again, this is to ensure that no username to SID resolution takes place on the client or server running the code.The  SecureIdentifier object can be created from the ObjectSID and the passed to the AccessRule constructor to avoid Username to SID lookups.


Our wrap up.

After our small ordeals of getting this working flawlessly in our environment we created an embedded Powershell host and wrapped it up in a web service which talks SPML (Service Provisioning Markup Language). Now users can be provisioned from any SPML compliant requester agent.
Other people have probably seen similar issues as we have done and it would be great to see what issues you’ve seen and how you’ve solved it to be able to do realtime provision in a fail safe manor in a global AD environment. So please add your 2 cent to the puzzle.